diff options
Diffstat (limited to 'src/qml/qml/v4')
127 files changed, 43079 insertions, 8770 deletions
diff --git a/src/qml/qml/v4/llvm_installation.pri b/src/qml/qml/v4/llvm_installation.pri new file mode 100644 index 0000000000..99e955fd2b --- /dev/null +++ b/src/qml/qml/v4/llvm_installation.pri @@ -0,0 +1,23 @@ +LLVM_CONFIG=llvm-config +# Pick up the qmake variable or environment variable for LLVM_INSTALL_DIR. If either was set, change the LLVM_CONFIG to use that. +isEmpty(LLVM_INSTALL_DIR):LLVM_INSTALL_DIR=$$(LLVM_INSTALL_DIR) +!isEmpty(LLVM_INSTALL_DIR):LLVM_CONFIG=$$LLVM_INSTALL_DIR/bin/llvm-config +exists ($${LLVM_CONFIG}) { + CONFIG += llvm-libs + message("Found LLVM in $$LLVM_INSTALL_DIR") +} + +llvm-libs { + win32 { + LLVM_INCLUDEPATH = $$LLVM_INSTALL_DIR/include +# TODO: check if the next line is needed somehow for the llvm_runtime target. + LLVM_LIBS += -ladvapi32 -lshell32 + } + + unix { + LLVM_INCLUDEPATH = $$system($$LLVM_CONFIG --includedir) + LLVM_LIBDIR = $$system($$LLVM_CONFIG --libdir) + } + + LLVM_DEFINES += __STDC_LIMIT_MACROS __STDC_CONSTANT_MACROS +} diff --git a/src/qml/qml/v4/llvm_runtime.cpp b/src/qml/qml/v4/llvm_runtime.cpp new file mode 100644 index 0000000000..0498bab44f --- /dev/null +++ b/src/qml/qml/v4/llvm_runtime.cpp @@ -0,0 +1,513 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4runtime_p.h" +#include "qv4context_p.h" +#include "qv4engine_p.h" +#include <stdio.h> +#include <setjmp.h> + +using namespace QV4; + +extern "C" { + +Value __qmljs_llvm_return(ExecutionContext */*ctx*/, Value *result) +{ + return *result; +} + +Value *__qmljs_llvm_get_argument(ExecutionContext *ctx, int index) +{ + assert(ctx->type == ExecutionContext::Type_CallContext); + return &static_cast<CallContext *>(ctx)->arguments[index]; +} + +void __qmljs_llvm_init_undefined(Value *result) +{ + *result = Value::undefinedValue(); +} + +void __qmljs_llvm_init_null(Value *result) +{ + *result = Value::nullValue(); +} + +void __qmljs_llvm_init_boolean(Value *result, bool value) +{ + *result = Value::fromBoolean(value); +} + +void __qmljs_llvm_init_number(Value *result, double value) +{ + *result = Value::fromDouble(value); +} + +void __qmljs_llvm_init_string(ExecutionContext *ctx, Value *result, const char *str) +{ + *result = Value::fromString(ctx->engine->newString(QString::fromUtf8(str))); +} + +void __qmljs_llvm_init_closure(ExecutionContext *ctx, Value *result, + String *name, bool hasDirectEval, + bool usesArgumentsObject, bool isStrict, + bool hasNestedFunctions, + String **formals, unsigned formalCount, + String **locals, unsigned localCount) +{ + Function *clos = __qmljs_register_function(ctx, name, hasDirectEval, + usesArgumentsObject, isStrict, + hasNestedFunctions, + formals, formalCount, + locals, localCount); + __qmljs_init_closure(ctx, result, clos); +} + +bool __qmljs_llvm_to_boolean(ExecutionContext *ctx, const Value *value) +{ + return __qmljs_to_boolean(*value); +} + +void __qmljs_llvm_bit_and(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_bit_and(ctx, result, *left, *right); +} + +void __qmljs_llvm_bit_or(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_bit_or(ctx, result, *left, *right); +} + +void __qmljs_llvm_bit_xor(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_bit_xor(ctx, result, *left, *right); +} + +void __qmljs_llvm_add(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_add(ctx, result, *left, *right); +} + +void __qmljs_llvm_sub(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_sub(ctx, result, *left, *right); +} + +void __qmljs_llvm_mul(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_mul(ctx, result, *left, *right); +} + +void __qmljs_llvm_div(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_div(ctx, result, *left, *right); +} + +void __qmljs_llvm_mod(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_mod(ctx, result, *left, *right); +} + +void __qmljs_llvm_shl(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_shl(ctx, result, *left, *right); +} + +void __qmljs_llvm_shr(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_shr(ctx, result, *left, *right); +} + +void __qmljs_llvm_ushr(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_ushr(ctx, result, *left, *right); +} + +void __qmljs_llvm_gt(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_gt(ctx, result, *left, *right); +} + +void __qmljs_llvm_lt(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_lt(ctx, result, *left, *right); +} + +void __qmljs_llvm_ge(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_ge(ctx, result, *left, *right); +} + +void __qmljs_llvm_le(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_le(ctx, result, *left, *right); +} + +void __qmljs_llvm_eq(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_eq(ctx, result, *left, *right); +} + +void __qmljs_llvm_ne(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_ne(ctx, result, *left, *right); +} + +void __qmljs_llvm_se(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_se(ctx, result, *left, *right); +} + +void __qmljs_llvm_sne(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_sne(ctx, result, *left, *right); +} + +void __qmljs_llvm_instanceof(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_instanceof(ctx, result, *left, *right); +} + +void __qmljs_llvm_in(ExecutionContext *ctx, Value *result, Value *left, Value *right) +{ + __qmljs_in(ctx, result, *left, *right); +} + +void __qmljs_llvm_uplus(ExecutionContext *ctx, Value *result, const Value *value) +{ + __qmljs_uplus(result, *value); +} + +void __qmljs_llvm_uminus(ExecutionContext *ctx, Value *result, const Value *value) +{ + __qmljs_uminus(result, *value); +} + +void __qmljs_llvm_compl(ExecutionContext *ctx, Value *result, const Value *value) +{ + __qmljs_compl(result, *value); +} + +void __qmljs_llvm_not(ExecutionContext *ctx, Value *result, const Value *value) +{ + __qmljs_not(result, *value); +} + +void __qmljs_llvm_inplace_bit_and_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_bit_and_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_bit_or_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_bit_or_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_bit_xor_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_bit_xor_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_add_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_add_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_sub_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_sub_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_mul_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_mul_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_div_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_div_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_mod_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_mod_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_shl_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_shl_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_shr_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_shr_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_ushr_name(ExecutionContext *ctx, String *dest, Value *src) +{ + __qmljs_inplace_ushr_name(ctx, dest, *src); +} + +void __qmljs_llvm_inplace_bit_and_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_bit_and_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_bit_or_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_bit_or_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_bit_xor_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_bit_xor_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_add_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_add_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_sub_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_sub_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_mul_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_mul_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_div_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_div_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_mod_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_mod_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_shl_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_shl_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_shr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_shr_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_ushr_element(ExecutionContext *ctx, Value *base, Value *index, Value *value) +{ + __qmljs_inplace_ushr_element(ctx, *base, *index, *value); +} + +void __qmljs_llvm_inplace_bit_and_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_bit_and_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_bit_or_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_bit_or_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_bit_xor_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_bit_xor_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_add_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_add_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_sub_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_sub_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_mul_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_mul_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_div_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_div_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_mod_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_mod_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_shl_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_shl_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_shr_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_shr_member(ctx, *base, member, *value); +} + +void __qmljs_llvm_inplace_ushr_member(ExecutionContext *ctx, Value *value, Value *base, String *member) +{ + __qmljs_inplace_ushr_member(ctx, *base, member, *value); +} + +String *__qmljs_llvm_identifier_from_utf8(ExecutionContext *ctx, const char *str) +{ + return ctx->engine->newIdentifier(QString::fromUtf8(str)); +} + +void __qmljs_llvm_call_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) +{ + __qmljs_call_activation_property(context, result, name, args, argc); +} + +void __qmljs_llvm_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value *func, Value *args, int argc) +{ + __qmljs_call_value(context, result, thisObject, *func, args, argc); +} + +void __qmljs_llvm_construct_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) +{ + __qmljs_construct_activation_property(context, result, name, args, argc); +} + +void __qmljs_llvm_construct_value(ExecutionContext *context, Value *result, const Value *func, Value *args, int argc) +{ + __qmljs_construct_value(context, result, *func, args, argc); +} + +void __qmljs_llvm_get_activation_property(ExecutionContext *ctx, Value *result, String *name) +{ + __qmljs_get_activation_property(ctx, result, name); +} + +void __qmljs_llvm_set_activation_property(ExecutionContext *ctx, String *name, Value *value) +{ + __qmljs_set_activation_property(ctx, name, *value); +} + +void __qmljs_llvm_get_property(ExecutionContext *ctx, Value *result, Value *object, String *name) +{ + __qmljs_get_property(ctx, result, *object, name); +} + +void __qmljs_llvm_call_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc) +{ + __qmljs_call_property(context, result, *base, name, args, argc); +} + +void __qmljs_llvm_construct_property(ExecutionContext *context, Value *result, const Value *base, String *name, Value *args, int argc) +{ + __qmljs_construct_property(context, result, *base, name, args, argc); +} + +void __qmljs_llvm_get_element(ExecutionContext *ctx, Value *result, Value *object, Value *index) +{ + __qmljs_get_element(ctx, result, *object, *index); +} + +void __qmljs_llvm_set_element(ExecutionContext *ctx, Value *object, Value *index, Value *value) +{ + __qmljs_set_element(ctx, *object, *index, *value); +} + +void __qmljs_llvm_set_property(ExecutionContext *ctx, Value *object, String *name, Value *value) +{ + __qmljs_set_property(ctx, *object, name, *value); +} + +void __qmljs_llvm_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) +{ + __qmljs_builtin_declare_var(ctx, deletable, name); +} + +void __qmljs_llvm_typeof(ExecutionContext *ctx, Value *result, const Value *value) +{ + __qmljs_builtin_typeof(ctx, result, *value); +} + +void __qmljs_llvm_throw(ExecutionContext *context, Value *value) +{ + __qmljs_throw(context, *value); +} + +void __qmljs_llvm_delete_exception_handler(ExecutionContext *context) +{ + // ### FIXME. +} + +void __qmljs_llvm_foreach_iterator_object(ExecutionContext *context, Value *result, Value *in) +{ + __qmljs_foreach_iterator_object(context, result, *in); +} + +void __qmljs_llvm_foreach_next_property_name(Value *result, Value *it) +{ + __qmljs_foreach_next_property_name(result, *it); +} + +void __qmljs_llvm_get_this_object(ExecutionContext *ctx, Value *result) +{ + *result = ctx->thisObject; +} + +void __qmljs_llvm_delete_subscript(ExecutionContext *ctx, Value *result, Value *base, Value *index) +{ + __qmljs_delete_subscript(ctx, result, *base, *index); +} + +void __qmljs_llvm_delete_member(ExecutionContext *ctx, Value *result, Value *base, String *name) +{ + __qmljs_delete_member(ctx, result, *base, name); +} + +void __qmljs_llvm_delete_name(ExecutionContext *ctx, Value *result, String *name) +{ + __qmljs_delete_name(ctx, result, name); +} + +} // extern "C" diff --git a/src/qml/qml/v4/moth/moth.pri b/src/qml/qml/v4/moth/moth.pri new file mode 100644 index 0000000000..73bd893286 --- /dev/null +++ b/src/qml/qml/v4/moth/moth.pri @@ -0,0 +1,13 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qv4isel_moth_p.h \ + $$PWD/qv4instr_moth_p.h \ + $$PWD/qv4vme_moth_p.h + +SOURCES += \ + $$PWD/qv4isel_moth.cpp \ + $$PWD/qv4instr_moth.cpp \ + $$PWD/qv4vme_moth.cpp + +#DEFINES += DO_TRACE_INSTR diff --git a/src/qml/qml/v4/moth/qv4instr_moth.cpp b/src/qml/qml/v4/moth/qv4instr_moth.cpp new file mode 100644 index 0000000000..ec68ede72d --- /dev/null +++ b/src/qml/qml/v4/moth/qv4instr_moth.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4instr_moth_p.h" + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +int Instr::size(Type type) +{ +#define MOTH_RETURN_INSTR_SIZE(I, FMT) case I: return InstrMeta<(int)I>::Size; + switch (type) { + FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE) + default: return 0; + } +#undef MOTH_RETURN_INSTR_SIZE +} + diff --git a/src/qml/qml/v4/moth/qv4instr_moth_p.h b/src/qml/qml/v4/moth/qv4instr_moth_p.h new file mode 100644 index 0000000000..7397a1811d --- /dev/null +++ b/src/qml/qml/v4/moth/qv4instr_moth_p.h @@ -0,0 +1,612 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4INSTR_MOTH_P_H +#define QV4INSTR_MOTH_P_H + +#include <QtCore/qglobal.h> +#include <private/qv4object_p.h> + +QT_BEGIN_NAMESPACE + +#define FOR_EACH_MOTH_INSTR(F) \ + F(Ret, ret) \ + F(LoadValue, loadValue) \ + F(LoadClosure, loadClosure) \ + F(MoveTemp, moveTemp) \ + F(LoadName, loadName) \ + F(StoreName, storeName) \ + F(LoadElement, loadElement) \ + F(StoreElement, storeElement) \ + F(LoadProperty, loadProperty) \ + F(StoreProperty, storeProperty) \ + F(Push, push) \ + F(EnterTry, enterTry) \ + F(CallValue, callValue) \ + F(CallProperty, callProperty) \ + F(CallElement, callElement) \ + F(CallActivationProperty, callActivationProperty) \ + F(CallBuiltinThrow, callBuiltinThrow) \ + F(CallBuiltinFinishTry, callBuiltinFinishTry) \ + F(CallBuiltinPushScope, callBuiltinPushScope) \ + F(CallBuiltinPopScope, callBuiltinPopScope) \ + F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \ + F(CallBuiltinForeachNextPropertyName, callBuiltinForeachNextPropertyName) \ + F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \ + F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \ + F(CallBuiltinDeleteName, callBuiltinDeleteName) \ + F(CallBuiltinTypeofMember, callBuiltinTypeofMember) \ + F(CallBuiltinTypeofSubscript, callBuiltinTypeofSubscript) \ + F(CallBuiltinTypeofName, callBuiltinTypeofName) \ + F(CallBuiltinTypeofValue, callBuiltinTypeofValue) \ + F(CallBuiltinPostIncMember, callBuiltinPostIncMember) \ + F(CallBuiltinPostIncSubscript, callBuiltinPostIncSubscript) \ + F(CallBuiltinPostIncName, callBuiltinPostIncName) \ + F(CallBuiltinPostIncValue, callBuiltinPostIncValue) \ + F(CallBuiltinPostDecMember, callBuiltinPostDecMember) \ + F(CallBuiltinPostDecSubscript, callBuiltinPostDecSubscript) \ + F(CallBuiltinPostDecName, callBuiltinPostDecName) \ + F(CallBuiltinPostDecValue, callBuiltinPostDecValue) \ + F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \ + F(CallBuiltinDefineGetterSetter, callBuiltinDefineGetterSetter) \ + F(CallBuiltinDefineProperty, callBuiltinDefineProperty) \ + F(CallBuiltinDefineArray, callBuiltinDefineArray) \ + F(CallBuiltinDefineObjectLiteral, callBuiltinDefineObjectLiteral) \ + F(CreateValue, createValue) \ + F(CreateProperty, createProperty) \ + F(CreateActivationProperty, createActivationProperty) \ + F(Jump, jump) \ + F(CJump, cjump) \ + F(Unop, unop) \ + F(Binop, binop) \ + F(AddNumberParams, addNumberParams) \ + F(MulNumberParams, mulNumberParams) \ + F(SubNumberParams, subNumberParams) \ + F(LoadThis, loadThis) \ + F(InplaceElementOp, inplaceElementOp) \ + F(InplaceMemberOp, inplaceMemberOp) \ + F(InplaceNameOp, inplaceNameOp) + +#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) +# define MOTH_THREADED_INTERPRETER +#endif + +#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QQmlJS::Moth::Instr) - 1) + +#ifdef MOTH_THREADED_INTERPRETER +# define MOTH_INSTR_HEADER void *code; \ + unsigned int breakPoint : 1; +#else +# define MOTH_INSTR_HEADER quint8 instructionType; \ + unsigned int breakPoint : 1; +#endif + +#define MOTH_INSTR_ENUM(I, FMT) I, +#define MOTH_INSTR_SIZE(I, FMT) ((sizeof(QQmlJS::Moth::Instr::instr_##FMT) + MOTH_INSTR_ALIGN_MASK) & ~MOTH_INSTR_ALIGN_MASK) + + +namespace QQmlJS { +namespace Moth { + +union Instr +{ + struct Param { + enum { + ValueType = 0, + ArgumentType = 1, + LocalType = 2, + TempType = 3, + ScopedLocalType = 4 + }; + QV4::Value value; + unsigned type : 3; + unsigned scope : 29; + unsigned index; + + bool isValue() const { return type == ValueType; } + bool isArgument() const { return type == ArgumentType; } + bool isLocal() const { return type == LocalType; } + bool isTemp() const { return type == TempType; } + bool isScopedLocal() const { return type == ScopedLocalType; } + + static Param createValue(const QV4::Value &v) + { + Param p; + p.type = ValueType; + p.scope = 0; + p.value = v; + return p; + } + + static Param createArgument(unsigned idx, uint scope) + { + Param p; + p.type = ArgumentType; + p.scope = scope; + p.index = idx; + return p; + } + + static Param createLocal(unsigned idx) + { + Param p; + p.type = LocalType; + p.scope = 0; + p.index = idx; + return p; + } + + static Param createTemp(unsigned idx) + { + Param p; + p.type = TempType; + p.scope = 0; + p.index = idx; + return p; + } + + static Param createScopedLocal(unsigned idx, uint scope) + { + Param p; + p.type = ScopedLocalType; + p.scope = scope; + p.index = idx; + return p; + } + + inline bool operator==(const Param &other) const + { return type == other.type && scope == other.scope && index == other.index; } + + inline bool operator!=(const Param &other) const + { return !(*this == other); } + }; + + enum Type { + FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM) + }; + + struct instr_common { + MOTH_INSTR_HEADER + }; + struct instr_ret { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_loadValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_moveTemp { + MOTH_INSTR_HEADER + Param source; + Param result; + }; + struct instr_loadClosure { + MOTH_INSTR_HEADER + QV4::Function *value; + Param result; + }; + struct instr_loadName { + MOTH_INSTR_HEADER + QV4::String *name; + Param result; + }; + struct instr_storeName { + MOTH_INSTR_HEADER + QV4::String *name; + Param source; + }; + struct instr_loadProperty { + MOTH_INSTR_HEADER + QV4::String *name; + Param base; + Param result; + }; + struct instr_storeProperty { + MOTH_INSTR_HEADER + QV4::String *name; + Param base; + Param source; + }; + struct instr_loadElement { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_storeElement { + MOTH_INSTR_HEADER + Param base; + Param index; + Param source; + }; + struct instr_push { + MOTH_INSTR_HEADER + quint32 value; + }; + struct instr_enterTry { + MOTH_INSTR_HEADER + ptrdiff_t tryOffset; + ptrdiff_t catchOffset; + QV4::String *exceptionVarName; + Param exceptionVar; + }; + struct instr_callValue { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + Param dest; + Param result; + }; + struct instr_callProperty { + MOTH_INSTR_HEADER + QV4::String *name; + quint32 argc; + quint32 args; + Param base; + Param result; + }; + struct instr_callElement { + MOTH_INSTR_HEADER + Param base; + Param index; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_callActivationProperty { + MOTH_INSTR_HEADER + QV4::String *name; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_callBuiltinThrow { + MOTH_INSTR_HEADER + Param arg; + }; + struct instr_callBuiltinFinishTry { + MOTH_INSTR_HEADER + }; + struct instr_callBuiltinPushScope { + MOTH_INSTR_HEADER + Param arg; + }; + struct instr_callBuiltinPopScope { + MOTH_INSTR_HEADER + }; + struct instr_callBuiltinForeachIteratorObject { + MOTH_INSTR_HEADER + Param arg; + Param result; + }; + struct instr_callBuiltinForeachNextPropertyName { + MOTH_INSTR_HEADER + Param arg; + Param result; + }; + struct instr_callBuiltinDeleteMember { + MOTH_INSTR_HEADER + QV4::String *member; + Param base; + Param result; + }; + struct instr_callBuiltinDeleteSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinDeleteName { + MOTH_INSTR_HEADER + QV4::String *name; + Param result; + }; + struct instr_callBuiltinTypeofMember { + MOTH_INSTR_HEADER + QV4::String *member; + Param base; + Param result; + }; + struct instr_callBuiltinTypeofSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinTypeofName { + MOTH_INSTR_HEADER + QV4::String *name; + Param result; + }; + struct instr_callBuiltinTypeofValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinPostIncMember { + MOTH_INSTR_HEADER + Param base; + QV4::String *member; + Param result; + }; + struct instr_callBuiltinPostIncSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinPostIncName { + MOTH_INSTR_HEADER + QV4::String *name; + Param result; + }; + struct instr_callBuiltinPostIncValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinPostDecMember { + MOTH_INSTR_HEADER + Param base; + QV4::String *member; + Param result; + }; + struct instr_callBuiltinPostDecSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinPostDecName { + MOTH_INSTR_HEADER + QV4::String *name; + Param result; + }; + struct instr_callBuiltinPostDecValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinDeclareVar { + MOTH_INSTR_HEADER + QV4::String *varName; + bool isDeletable; + }; + struct instr_callBuiltinDefineGetterSetter { + MOTH_INSTR_HEADER + QV4::String *name; + Param object; + Param getter; + Param setter; + }; + struct instr_callBuiltinDefineProperty { + MOTH_INSTR_HEADER + QV4::String *name; + Param object; + Param value; + }; + struct instr_callBuiltinDefineArray { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + Param result; + }; + struct instr_callBuiltinDefineObjectLiteral { + MOTH_INSTR_HEADER + QV4::InternalClass *internalClass; + quint32 args; + Param result; + }; + struct instr_createValue { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + Param func; + Param result; + }; + struct instr_createProperty { + MOTH_INSTR_HEADER + QV4::String *name; + quint32 argc; + quint32 args; + Param base; + Param result; + }; + struct instr_createActivationProperty { + MOTH_INSTR_HEADER + QV4::String *name; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_jump { + MOTH_INSTR_HEADER + ptrdiff_t offset; + }; + struct instr_cjump { + MOTH_INSTR_HEADER + ptrdiff_t offset; + Param condition; + }; + struct instr_unop { + MOTH_INSTR_HEADER + QV4::UnaryOpName alu; + Param source; + Param result; + }; + struct instr_binop { + MOTH_INSTR_HEADER + QV4::BinOp alu; + Param lhs; + Param rhs; + Param result; + }; + struct instr_addNumberParams { + MOTH_INSTR_HEADER + Param lhs; + Param rhs; + Param result; + }; + struct instr_mulNumberParams { + MOTH_INSTR_HEADER + Param lhs; + Param rhs; + Param result; + }; + struct instr_subNumberParams { + MOTH_INSTR_HEADER + Param lhs; + Param rhs; + Param result; + }; + struct instr_loadThis { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_inplaceElementOp { + MOTH_INSTR_HEADER + QV4::InplaceBinOpElement alu; + Param base; + Param index; + Param source; + }; + struct instr_inplaceMemberOp { + MOTH_INSTR_HEADER + QV4::InplaceBinOpMember alu; + QV4::String *member; + Param base; + Param source; + }; + struct instr_inplaceNameOp { + MOTH_INSTR_HEADER + QV4::InplaceBinOpName alu; + QV4::String *name; + Param source; + }; + + instr_common common; + instr_ret ret; + instr_loadValue loadValue; + instr_moveTemp moveTemp; + instr_loadClosure loadClosure; + instr_loadName loadName; + instr_storeName storeName; + instr_loadElement loadElement; + instr_storeElement storeElement; + instr_loadProperty loadProperty; + instr_storeProperty storeProperty; + instr_push push; + instr_enterTry enterTry; + instr_callValue callValue; + instr_callProperty callProperty; + instr_callElement callElement; + instr_callActivationProperty callActivationProperty; + instr_callBuiltinThrow callBuiltinThrow; + instr_callBuiltinFinishTry callBuiltinFinishTry; + instr_callBuiltinPushScope callBuiltinPushScope; + instr_callBuiltinPopScope callBuiltinPopScope; + instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject; + instr_callBuiltinForeachNextPropertyName callBuiltinForeachNextPropertyName; + instr_callBuiltinDeleteMember callBuiltinDeleteMember; + instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript; + instr_callBuiltinDeleteName callBuiltinDeleteName; + instr_callBuiltinTypeofMember callBuiltinTypeofMember; + instr_callBuiltinTypeofSubscript callBuiltinTypeofSubscript; + instr_callBuiltinTypeofName callBuiltinTypeofName; + instr_callBuiltinTypeofValue callBuiltinTypeofValue; + instr_callBuiltinPostIncMember callBuiltinPostIncMember; + instr_callBuiltinPostIncSubscript callBuiltinPostIncSubscript; + instr_callBuiltinPostIncName callBuiltinPostIncName; + instr_callBuiltinPostIncValue callBuiltinPostIncValue; + instr_callBuiltinPostDecMember callBuiltinPostDecMember; + instr_callBuiltinPostDecSubscript callBuiltinPostDecSubscript; + instr_callBuiltinPostDecName callBuiltinPostDecName; + instr_callBuiltinPostDecValue callBuiltinPostDecValue; + instr_callBuiltinDeclareVar callBuiltinDeclareVar; + instr_callBuiltinDefineGetterSetter callBuiltinDefineGetterSetter; + instr_callBuiltinDefineProperty callBuiltinDefineProperty; + instr_callBuiltinDefineArray callBuiltinDefineArray; + instr_callBuiltinDefineObjectLiteral callBuiltinDefineObjectLiteral; + instr_createValue createValue; + instr_createProperty createProperty; + instr_createActivationProperty createActivationProperty; + instr_jump jump; + instr_cjump cjump; + instr_unop unop; + instr_binop binop; + instr_addNumberParams addNumberParams; + instr_mulNumberParams mulNumberParams; + instr_subNumberParams subNumberParams; + instr_loadThis loadThis; + instr_inplaceElementOp inplaceElementOp; + instr_inplaceMemberOp inplaceMemberOp; + instr_inplaceNameOp inplaceNameOp; + + static int size(Type type); +}; + +template<int N> +struct InstrMeta { +}; + +#define MOTH_INSTR_META_TEMPLATE(I, FMT) \ + template<> struct InstrMeta<(int)Instr::I> { \ + enum { Size = MOTH_INSTR_SIZE(I, FMT) }; \ + typedef Instr::instr_##FMT DataType; \ + static const DataType &data(const Instr &instr) { return instr.FMT; } \ + static void setData(Instr &instr, const DataType &v) { instr.FMT = v; } \ + }; +FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE); +#undef MOTH_INSTR_META_TEMPLATE + +template<int InstrType> +class InstrData : public InstrMeta<InstrType>::DataType +{ +}; + +} // namespace Moth +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4INSTR_MOTH_P_H diff --git a/src/qml/qml/v4/moth/qv4isel_moth.cpp b/src/qml/qml/v4/moth/qv4isel_moth.cpp new file mode 100644 index 0000000000..04d759ed8d --- /dev/null +++ b/src/qml/qml/v4/moth/qv4isel_moth.cpp @@ -0,0 +1,1075 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4isel_util_p.h" +#include "qv4isel_moth_p.h" +#include "qv4vme_moth_p.h" +#include "qv4ssa_p.h" +#include <private/qv4functionobject_p.h> +#include <private/qv4regexpobject_p.h> +#include <private/qv4debugging_p.h> +#include <private/qv4function_p.h> + +#undef USE_TYPE_INFO + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +namespace { + +inline QV4::BinOp aluOpFunction(V4IR::AluOp op) +{ + switch (op) { + case V4IR::OpInvalid: + return 0; + case V4IR::OpIfTrue: + return 0; + case V4IR::OpNot: + return 0; + case V4IR::OpUMinus: + return 0; + case V4IR::OpUPlus: + return 0; + case V4IR::OpCompl: + return 0; + case V4IR::OpBitAnd: + return QV4::__qmljs_bit_and; + case V4IR::OpBitOr: + return QV4::__qmljs_bit_or; + case V4IR::OpBitXor: + return QV4::__qmljs_bit_xor; + case V4IR::OpAdd: + return QV4::__qmljs_add; + case V4IR::OpSub: + return QV4::__qmljs_sub; + case V4IR::OpMul: + return QV4::__qmljs_mul; + case V4IR::OpDiv: + return QV4::__qmljs_div; + case V4IR::OpMod: + return QV4::__qmljs_mod; + case V4IR::OpLShift: + return QV4::__qmljs_shl; + case V4IR::OpRShift: + return QV4::__qmljs_shr; + case V4IR::OpURShift: + return QV4::__qmljs_ushr; + case V4IR::OpGt: + return QV4::__qmljs_gt; + case V4IR::OpLt: + return QV4::__qmljs_lt; + case V4IR::OpGe: + return QV4::__qmljs_ge; + case V4IR::OpLe: + return QV4::__qmljs_le; + case V4IR::OpEqual: + return QV4::__qmljs_eq; + case V4IR::OpNotEqual: + return QV4::__qmljs_ne; + case V4IR::OpStrictEqual: + return QV4::__qmljs_se; + case V4IR::OpStrictNotEqual: + return QV4::__qmljs_sne; + case V4IR::OpInstanceof: + return QV4::__qmljs_instanceof; + case V4IR::OpIn: + return QV4::__qmljs_in; + case V4IR::OpAnd: + return 0; + case V4IR::OpOr: + return 0; + default: + assert(!"Unknown AluOp"); + return 0; + } +}; +} // anonymous namespace + +// TODO: extend to optimize out temp-to-temp moves, where the lifetime of one temp ends at that statement. +// To handle that, add a hint when such a move will occur, and add a stmt for the hint. +// Then when asked for a register, check if the active statement is the terminating statement, and if so, apply the hint. +// This generalises the hint usage for Phi removal too, when the phi is passed in there as the current statement. +class QQmlJS::Moth::StackSlotAllocator +{ + QHash<V4IR::Temp, int> _slotForTemp; + QHash<V4IR::Temp, int> _hints; + QVector<int> _activeSlots; + + QHash<V4IR::Temp, V4IR::LifeTimeInterval> _intervals; + +public: + StackSlotAllocator(const QList<V4IR::LifeTimeInterval> &ranges, int maxTempCount) + : _activeSlots(maxTempCount) + { + _intervals.reserve(ranges.size()); + foreach (const V4IR::LifeTimeInterval &r, ranges) + _intervals[r.temp()] = r; + } + + void addHint(const V4IR::Temp &hintedSlotOfTemp, const V4IR::Temp &newTemp) + { + if (hintedSlotOfTemp.kind != V4IR::Temp::VirtualRegister + || newTemp.kind != V4IR::Temp::VirtualRegister) + return; + + if (_slotForTemp.contains(newTemp) || _hints.contains(newTemp)) + return; + + int hintedSlot = _slotForTemp.value(hintedSlotOfTemp, -1); + Q_ASSERT(hintedSlot >= 0); + _hints[newTemp] = hintedSlot; + } + + int stackSlotFor(V4IR::Temp *t, V4IR::Stmt *currentStmt) { + int idx = _slotForTemp.value(*t, -1); + if (idx == -1) + idx = allocateSlot(t, currentStmt); + Q_ASSERT(idx >= 0); + return idx; + } + +private: + int allocateSlot(V4IR::Temp *t, V4IR::Stmt *currentStmt) { + const V4IR::LifeTimeInterval &interval = _intervals[*t]; + int idx = _hints.value(*t, -1); + if (idx != -1 && _activeSlots[idx] <= currentStmt->id) { + _slotForTemp[*t] = idx; + _activeSlots[idx] = interval.end(); + return idx; + } + + for (int i = 0, ei = _activeSlots.size(); i != ei; ++i) { + if (_activeSlots[i] < currentStmt->id) { + _slotForTemp[*t] = i; + _activeSlots[i] = interval.end(); + return i; + } + } + + return -1; + } +}; + +InstructionSelection::InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module) + : EvalInstructionSelection(engine, module) + , _function(0) + , _vmFunction(0) + , _block(0) + , _codeStart(0) + , _codeNext(0) + , _codeEnd(0) + , _stackSlotAllocator(0) + , _currentStatement(0) +{ +} + +InstructionSelection::~InstructionSelection() +{ +} + +void InstructionSelection::run(QV4::Function *vmFunction, V4IR::Function *function) +{ + V4IR::BasicBlock *block = 0, *nextBlock = 0; + + QHash<V4IR::BasicBlock *, QVector<ptrdiff_t> > patches; + QHash<V4IR::BasicBlock *, ptrdiff_t> addrs; + + int codeSize = 4096; + uchar *codeStart = new uchar[codeSize]; + memset(codeStart, 0, codeSize); + uchar *codeNext = codeStart; + uchar *codeEnd = codeStart + codeSize; + + qSwap(_function, function); + qSwap(_vmFunction, vmFunction); + qSwap(block, _block); + qSwap(nextBlock, _nextBlock); + qSwap(patches, _patches); + qSwap(addrs, _addrs); + qSwap(codeStart, _codeStart); + qSwap(codeNext, _codeNext); + qSwap(codeEnd, _codeEnd); + + V4IR::Optimizer opt(_function); + opt.run(); + StackSlotAllocator *stackSlotAllocator = 0; + if (opt.isInSSA()) + stackSlotAllocator = new StackSlotAllocator(opt.lifeRanges(), _function->tempCount); + qSwap(_stackSlotAllocator, stackSlotAllocator); + V4IR::Stmt *cs = 0; + qSwap(_currentStatement, cs); + + int locals = frameSize(); + assert(locals >= 0); + + Instruction::Push push; + push.value = quint32(locals); + addInstruction(push); + + for (int i = 0, ei = _function->basicBlocks.size(); i != ei; ++i) { + _block = _function->basicBlocks[i]; + _nextBlock = (i < ei - 1) ? _function->basicBlocks[i + 1] : 0; + _addrs.insert(_block, _codeNext - _codeStart); + + foreach (V4IR::Stmt *s, _block->statements) { + if (s->location.isValid()) { + QV4::LineNumberMapping mapping; + mapping.codeOffset = _codeNext - _codeStart; + mapping.lineNumber = s->location.startLine; + _vmFunction->lineNumberMappings.append(mapping); + } + + if (opt.isInSSA() && s->asTerminator()) { + foreach (const V4IR::Optimizer::SSADeconstructionMove &move, + opt.ssaDeconstructionMoves(_block)) { + Q_ASSERT(move.source->asTemp()); // FIXME: support Const exprs in Phi nodes. + if (move.needsConversion()) + convertType(move.source->asTemp(), move.target); + else + copyValue(move.source->asTemp(), move.target); + } + } + + _currentStatement = s; + s->accept(this); + } + } + + // TODO: patch stack size (the push instruction) + patchJumpAddresses(); + + _vmFunction->code = VME::exec; + _vmFunction->codeData = squeezeCode(); + + if (QV4::Debugging::Debugger *debugger = engine()->debugger) + debugger->setPendingBreakpoints(_vmFunction); + + qSwap(_currentStatement, cs); + qSwap(_stackSlotAllocator, stackSlotAllocator); + delete stackSlotAllocator; + qSwap(_function, function); + qSwap(_vmFunction, vmFunction); + qSwap(block, _block); + qSwap(nextBlock, _nextBlock); + qSwap(patches, _patches); + qSwap(addrs, _addrs); + qSwap(codeStart, _codeStart); + qSwap(codeNext, _codeNext); + qSwap(codeEnd, _codeEnd); + + delete[] codeStart; +} + +void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + Instruction::CallValue call; + prepareCallArgs(args, call.argc, call.args); + call.dest = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) +{ + // call the property on the loaded base + Instruction::CallProperty call; + call.base = getParam(base); + call.name = identifier(name); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) +{ + // call the property on the loaded base + Instruction::CallElement call; + call.base = getParam(base); + call.index = getParam(index); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::convertType(V4IR::Temp *source, V4IR::Temp *target) +{ + if (_stackSlotAllocator) + _stackSlotAllocator->addHint(*source, *target); + + // FIXME: do something more useful with this info + copyValue(source, target); +} + +void InstructionSelection::constructActivationProperty(V4IR::Name *func, + V4IR::ExprList *args, + V4IR::Temp *result) +{ + Instruction::CreateActivationProperty create; + create.name = identifier(*func->id); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) +{ + Instruction::CreateProperty create; + create.base = getParam(base); + create.name = identifier(name); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + Instruction::CreateValue create; + create.func = getParam(value); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::loadThisObject(V4IR::Temp *temp) +{ + Instruction::LoadThis load; + load.result = getResultParam(temp); + addInstruction(load); +} + +void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) +{ + assert(sourceConst); + + Instruction::LoadValue load; + load.value = getParam(sourceConst); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::loadString(const QString &str, V4IR::Temp *targetTemp) +{ + Instruction::LoadValue load; + load.value = Instr::Param::createValue(QV4::Value::fromString(identifier(str))); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) +{ + QV4::Value v = QV4::Value::fromObject(engine()->newRegExpObject( + *sourceRegexp->value, + sourceRegexp->flags)); + _vmFunction->generatedValues.append(v); + + Instruction::LoadValue load; + load.value = Instr::Param::createValue(v); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp) +{ + Instruction::LoadName load; + load.name = identifier(*name->id); + load.result = getResultParam(temp); + addInstruction(load); +} + +void InstructionSelection::setActivationProperty(V4IR::Temp *source, const QString &targetName) +{ + Instruction::StoreName store; + store.source = getParam(source); + store.name = identifier(targetName); + addInstruction(store); +} + +void InstructionSelection::initClosure(V4IR::Closure *closure, V4IR::Temp *target) +{ + QV4::Function *vmFunc = vmFunction(closure->value); + assert(vmFunc); + Instruction::LoadClosure load; + load.value = vmFunc; + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target) +{ + Instruction::LoadProperty load; + load.base = getParam(base); + load.name = identifier(name); + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + Instruction::StoreProperty store; + store.base = getParam(targetBase); + store.name = identifier(targetName); + store.source = getParam(source); + addInstruction(store); +} + +void InstructionSelection::getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target) +{ + Instruction::LoadElement load; + load.base = getParam(base); + load.index = getParam(index); + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex) +{ + Instruction::StoreElement store; + store.base = getParam(targetBase); + store.index = getParam(targetIndex); + store.source = getParam(source); + addInstruction(store); +} + +void InstructionSelection::copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + if (_stackSlotAllocator) + _stackSlotAllocator->addHint(*sourceTemp, *targetTemp); + + Instruction::MoveTemp move; + move.source = getParam(sourceTemp); + move.result = getResultParam(targetTemp); + if (move.source != move.result) + addInstruction(move); +} + +void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + QV4::UnaryOpName op = 0; + switch (oper) { + case V4IR::OpIfTrue: assert(!"unreachable"); break; + case V4IR::OpNot: op = QV4::__qmljs_not; break; + case V4IR::OpUMinus: op = QV4::__qmljs_uminus; break; + case V4IR::OpUPlus: op = QV4::__qmljs_uplus; break; + case V4IR::OpCompl: op = QV4::__qmljs_compl; break; + case V4IR::OpIncrement: op = QV4::__qmljs_increment; break; + case V4IR::OpDecrement: op = QV4::__qmljs_decrement; break; + default: assert(!"unreachable"); break; + } // switch + + if (op) { + Instruction::Unop unop; + unop.alu = op; + unop.source = getParam(sourceTemp); + unop.result = getResultParam(targetTemp); + addInstruction(unop); + } else { + qWarning(" UNOP1"); + } +} + +void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target) +{ +#ifdef USE_TYPE_INFO + if (leftSource->type & V4IR::NumberType && rightSource->type & V4IR::NumberType) { + // TODO: add Temp+Const variation on the topic. + switch (oper) { + case V4IR::OpAdd: { + Instruction::AddNumberParams instr; + instr.lhs = getParam(leftSource); + instr.rhs = getParam(rightSource); + instr.result = getResultParam(target); + addInstruction(instr); + } return; + case V4IR::OpMul: { + Instruction::MulNumberParams instr; + instr.lhs = getParam(leftSource); + instr.rhs = getParam(rightSource); + instr.result = getResultParam(target); + addInstruction(instr); + } return; + case V4IR::OpSub: { + Instruction::SubNumberParams instr; + instr.lhs = getParam(leftSource); + instr.rhs = getParam(rightSource); + instr.result = getResultParam(target); + addInstruction(instr); + } return; + default: + break; + } + } +#else // !USE_TYPE_INFO + Q_ASSERT(leftSource->asTemp() && rightSource->asTemp()); +#endif // USE_TYPE_INFO + + Instruction::Binop binop; + binop.alu = aluOpFunction(oper); + binop.lhs = getParam(leftSource); + binop.rhs = getParam(rightSource); + binop.result = getResultParam(target); + addInstruction(binop); +} + +void InstructionSelection::inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName) +{ + QV4::InplaceBinOpName op = 0; + switch (oper) { + case V4IR::OpBitAnd: op = QV4::__qmljs_inplace_bit_and_name; break; + case V4IR::OpBitOr: op = QV4::__qmljs_inplace_bit_or_name; break; + case V4IR::OpBitXor: op = QV4::__qmljs_inplace_bit_xor_name; break; + case V4IR::OpAdd: op = QV4::__qmljs_inplace_add_name; break; + case V4IR::OpSub: op = QV4::__qmljs_inplace_sub_name; break; + case V4IR::OpMul: op = QV4::__qmljs_inplace_mul_name; break; + case V4IR::OpDiv: op = QV4::__qmljs_inplace_div_name; break; + case V4IR::OpMod: op = QV4::__qmljs_inplace_mod_name; break; + case V4IR::OpLShift: op = QV4::__qmljs_inplace_shl_name; break; + case V4IR::OpRShift: op = QV4::__qmljs_inplace_shr_name; break; + case V4IR::OpURShift: op = QV4::__qmljs_inplace_ushr_name; break; + default: break; + } + + if (op) { + Instruction::InplaceNameOp ieo; + ieo.alu = op; + ieo.name = identifier(targetName); + ieo.source = getParam(rightSource); + addInstruction(ieo); + } +} + +void InstructionSelection::inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp) +{ + QV4::InplaceBinOpElement op = 0; + switch (oper) { + case V4IR::OpBitAnd: op = QV4::__qmljs_inplace_bit_and_element; break; + case V4IR::OpBitOr: op = QV4::__qmljs_inplace_bit_or_element; break; + case V4IR::OpBitXor: op = QV4::__qmljs_inplace_bit_xor_element; break; + case V4IR::OpAdd: op = QV4::__qmljs_inplace_add_element; break; + case V4IR::OpSub: op = QV4::__qmljs_inplace_sub_element; break; + case V4IR::OpMul: op = QV4::__qmljs_inplace_mul_element; break; + case V4IR::OpDiv: op = QV4::__qmljs_inplace_div_element; break; + case V4IR::OpMod: op = QV4::__qmljs_inplace_mod_element; break; + case V4IR::OpLShift: op = QV4::__qmljs_inplace_shl_element; break; + case V4IR::OpRShift: op = QV4::__qmljs_inplace_shr_element; break; + case V4IR::OpURShift: op = QV4::__qmljs_inplace_ushr_element; break; + default: break; + } + + Instruction::InplaceElementOp ieo; + ieo.alu = op; + ieo.base = getParam(targetBaseTemp); + ieo.index = getParam(targetIndexTemp); + ieo.source = getParam(source); + addInstruction(ieo); +} + +void InstructionSelection::inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + QV4::InplaceBinOpMember op = 0; + switch (oper) { + case V4IR::OpBitAnd: op = QV4::__qmljs_inplace_bit_and_member; break; + case V4IR::OpBitOr: op = QV4::__qmljs_inplace_bit_or_member; break; + case V4IR::OpBitXor: op = QV4::__qmljs_inplace_bit_xor_member; break; + case V4IR::OpAdd: op = QV4::__qmljs_inplace_add_member; break; + case V4IR::OpSub: op = QV4::__qmljs_inplace_sub_member; break; + case V4IR::OpMul: op = QV4::__qmljs_inplace_mul_member; break; + case V4IR::OpDiv: op = QV4::__qmljs_inplace_div_member; break; + case V4IR::OpMod: op = QV4::__qmljs_inplace_mod_member; break; + case V4IR::OpLShift: op = QV4::__qmljs_inplace_shl_member; break; + case V4IR::OpRShift: op = QV4::__qmljs_inplace_shr_member; break; + case V4IR::OpURShift: op = QV4::__qmljs_inplace_ushr_member; break; + default: break; + } + + Instruction::InplaceMemberOp imo; + imo.alu = op; + imo.base = getParam(targetBase); + imo.member = identifier(targetName); + imo.source = getParam(source); + addInstruction(imo); +} + +void InstructionSelection::prepareCallArgs(V4IR::ExprList *e, quint32 &argc, quint32 &args) +{ + bool singleArgIsTemp = false; + if (e && e->next == 0 && e->expr->asTemp()) { + singleArgIsTemp = e->expr->asTemp()->kind == V4IR::Temp::VirtualRegister; + } + + if (singleArgIsTemp) { + // We pass single arguments as references to the stack, but only if it's not a local or an argument. + argc = 1; + args = getParam(e->expr).index; + } else if (e) { + // We need to move all the temps into the function arg array + int argLocation = outgoingArgumentTempStart(); + assert(argLocation >= 0); + argc = 0; + args = argLocation; + while (e) { + Instruction::MoveTemp move; + move.source = getParam(e->expr); + move.result = Instr::Param::createTemp(argLocation); + addInstruction(move); + ++argLocation; + ++argc; + e = e->next; + } + } else { + argc = 0; + args = 0; + } +} + +void InstructionSelection::visitJump(V4IR::Jump *s) +{ + if (s->target == _nextBlock) + return; + + Instruction::Jump jump; + jump.offset = 0; + ptrdiff_t loc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + + _patches[s->target].append(loc); +} + +void InstructionSelection::visitCJump(V4IR::CJump *s) +{ + Instr::Param condition; + if (V4IR::Temp *t = s->cond->asTemp()) { + condition = getResultParam(t); + } else if (V4IR::Binop *b = s->cond->asBinop()) { + condition = getResultParam(0); + Instruction::Binop binop; + binop.alu = aluOpFunction(b->op); + binop.lhs = getParam(b->left); + binop.rhs = getParam(b->right); + binop.result = condition; + addInstruction(binop); + } else { + Q_UNIMPLEMENTED(); + } + + Instruction::CJump jump; + jump.offset = 0; + jump.condition = condition; + ptrdiff_t trueLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + _patches[s->iftrue].append(trueLoc); + + if (s->iffalse != _nextBlock) { + Instruction::Jump jump; + jump.offset = 0; + ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + _patches[s->iffalse].append(falseLoc); + } +} + +void InstructionSelection::visitRet(V4IR::Ret *s) +{ + Instruction::Ret ret; + ret.result = getParam(s->expr); + addInstruction(ret); +} + +void InstructionSelection::visitTry(V4IR::Try *t) +{ + Instruction::EnterTry enterTry; + enterTry.tryOffset = 0; + enterTry.catchOffset = 0; + enterTry.exceptionVarName = identifier(t->exceptionVarName); + enterTry.exceptionVar = getParam(t->exceptionVar); + ptrdiff_t enterTryLoc = addInstruction(enterTry); + + ptrdiff_t tryLoc = enterTryLoc + (((const char *)&enterTry.tryOffset) - ((const char *)&enterTry)); + _patches[t->tryBlock].append(tryLoc); + + ptrdiff_t catchLoc = enterTryLoc + (((const char *)&enterTry.catchOffset) - ((const char *)&enterTry)); + _patches[t->catchBlock].append(catchLoc); +} + +void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) +{ + Instruction::CallActivationProperty call; + call.name = identifier(*func->id); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinTypeofMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + Instruction::CallBuiltinTypeofSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofName(const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinTypeofName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result) +{ + Instruction::CallBuiltinTypeofValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinDeleteMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + Instruction::CallBuiltinDeleteSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteName(const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinDeleteName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result) +{ + Instruction::LoadValue load; + load.value = Instr::Param::createValue(QV4::Value::fromBoolean(false)); + load.result = getResultParam(result); + addInstruction(load); +} + +void InstructionSelection::callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostDecMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostDecSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostDecName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostDecValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostIncMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostIncSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostIncName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + Instruction::CallBuiltinPostIncValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinThrow(V4IR::Temp *arg) +{ + Instruction::CallBuiltinThrow call; + call.arg = getParam(arg); + addInstruction(call); +} + +void InstructionSelection::callBuiltinFinishTry() +{ + Instruction::CallBuiltinFinishTry call; + addInstruction(call); +} + +void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) +{ + Instruction::CallBuiltinForeachIteratorObject call; + call.arg = getParam(arg); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) +{ + Instruction::CallBuiltinForeachNextPropertyName call; + call.arg = getParam(arg); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPushWithScope(V4IR::Temp *arg) +{ + Instruction::CallBuiltinPushScope call; + call.arg = getParam(arg); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPopScope() +{ + Instruction::CallBuiltinPopScope call; + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +{ + Instruction::CallBuiltinDeclareVar call; + call.isDeletable = deletable; + call.varName = identifier(name); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) +{ + Instruction::CallBuiltinDefineGetterSetter call; + call.object = getParam(object); + call.name = identifier(name); + call.getter = getParam(getter); + call.setter = getParam(setter); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) +{ + Instruction::CallBuiltinDefineProperty call; + call.object = getParam(object); + call.name = identifier(name); + call.value = getParam(value); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) +{ + Instruction::CallBuiltinDefineArray call; + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args) +{ + int argLocation = outgoingArgumentTempStart(); + + QV4::InternalClass *klass = engine()->emptyClass; + V4IR::ExprList *it = args; + while (it) { + V4IR::Name *name = it->expr->asName(); + it = it->next; + + bool isData = it->expr->asConst()->value; + it = it->next; + klass = klass->addMember(identifier(*name->id), isData ? QV4::Attr_Data : QV4::Attr_Accessor); + + Instruction::MoveTemp move; + move.source = getParam(it->expr); + move.result = Instr::Param::createTemp(argLocation); + addInstruction(move); + ++argLocation; + + if (!isData) { + it = it->next; + + Instruction::MoveTemp move; + move.source = getParam(it->expr); + move.result = Instr::Param::createTemp(argLocation); + addInstruction(move); + ++argLocation; + } + + it = it->next; + } + + Instruction::CallBuiltinDefineObjectLiteral call; + call.internalClass = klass; + call.args = outgoingArgumentTempStart(); + call.result = getResultParam(result); + addInstruction(call); +} + +ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) +{ +#ifdef MOTH_THREADED_INTERPRETER + instr.common.code = VME::instructionJumpTable()[static_cast<int>(type)]; +#else + instr.common.instructionType = type; +#endif + instr.common.breakPoint = 0; + + int instructionSize = Instr::size(type); + if (_codeEnd - _codeNext < instructionSize) { + int currSize = _codeEnd - _codeStart; + uchar *newCode = new uchar[currSize * 2]; + ::memset(newCode + currSize, 0, currSize); + ::memcpy(newCode, _codeStart, currSize); + _codeNext = _codeNext - _codeStart + newCode; + delete[] _codeStart; + _codeStart = newCode; + _codeEnd = _codeStart + currSize * 2; + } + + ::memcpy(_codeNext, reinterpret_cast<const char *>(&instr), instructionSize); + ptrdiff_t ptrOffset = _codeNext - _codeStart; + _codeNext += instructionSize; + + return ptrOffset; +} + +void InstructionSelection::patchJumpAddresses() +{ + typedef QHash<V4IR::BasicBlock *, QVector<ptrdiff_t> >::ConstIterator PatchIt; + for (PatchIt i = _patches.begin(), ei = _patches.end(); i != ei; ++i) { + Q_ASSERT(_addrs.contains(i.key())); + ptrdiff_t target = _addrs.value(i.key()); + + const QVector<ptrdiff_t> &patchList = i.value(); + for (int ii = 0, eii = patchList.count(); ii < eii; ++ii) { + ptrdiff_t patch = patchList.at(ii); + + *((ptrdiff_t *)(_codeStart + patch)) = target - patch; + } + } + + _patches.clear(); + _addrs.clear(); +} + +uchar *InstructionSelection::squeezeCode() const +{ + int codeSize = _codeNext - _codeStart; + uchar *squeezed = new uchar[codeSize]; + ::memcpy(squeezed, _codeStart, codeSize); + return squeezed; +} + +QV4::String *InstructionSelection::identifier(const QString &s) +{ + QV4::String *str = engine()->newIdentifier(s); + _vmFunction->identifiers.append(str); + return str; +} + +Instr::Param InstructionSelection::getParam(V4IR::Expr *e) { + typedef Instr::Param Param; + assert(e); + + if (V4IR::Const *c = e->asConst()) { + return Param::createValue(convertToValue(c)); + } else if (V4IR::Temp *t = e->asTemp()) { + switch (t->kind) { + case V4IR::Temp::Formal: + case V4IR::Temp::ScopedFormal: return Param::createArgument(t->index, t->scope); + case V4IR::Temp::Local: return Param::createLocal(t->index); + case V4IR::Temp::ScopedLocal: return Param::createScopedLocal(t->index, t->scope); + case V4IR::Temp::VirtualRegister: + return Param::createTemp(_stackSlotAllocator ? + _stackSlotAllocator->stackSlotFor(t, _currentStatement) : t->index); + default: + Q_UNIMPLEMENTED(); + return Param(); + } + } else { + Q_UNIMPLEMENTED(); + return Param(); + } +} diff --git a/src/qml/qml/v4/moth/qv4isel_moth_p.h b/src/qml/qml/v4/moth/qv4isel_moth_p.h new file mode 100644 index 0000000000..9c17245f67 --- /dev/null +++ b/src/qml/qml/v4/moth/qv4isel_moth_p.h @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ISEL_MOTH_P_H +#define QV4ISEL_MOTH_P_H + +#include <private/qv4global_p.h> +#include <private/qv4isel_p.h> +#include <private/qv4isel_util_p.h> +#include <private/qv4jsir_p.h> +#include <private/qv4object_p.h> +#include "qv4instr_moth_p.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace Moth { + +class StackSlotAllocator; + +class Q_QML_EXPORT InstructionSelection: + public V4IR::IRDecoder, + public EvalInstructionSelection +{ +public: + InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module); + ~InstructionSelection(); + + virtual void run(QV4::Function *vmFunction, V4IR::Function *function); + +protected: + virtual void visitJump(V4IR::Jump *); + virtual void visitCJump(V4IR::CJump *); + virtual void visitRet(V4IR::Ret *); + virtual void visitTry(V4IR::Try *); + + virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteValue(V4IR::Temp *result); + virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinThrow(V4IR::Temp *arg); + virtual void callBuiltinFinishTry(); + virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinPushWithScope(V4IR::Temp *arg); + virtual void callBuiltinPopScope(); + virtual void callBuiltinDeclareVar(bool deletable, const QString &name); + virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter); + virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value); + virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args); + virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args); + virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result); + virtual void convertType(V4IR::Temp *source, V4IR::Temp *target); + virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + virtual void loadThisObject(V4IR::Temp *temp); + virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp); + virtual void loadString(const QString &str, V4IR::Temp *targetTemp); + virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp); + virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp); + virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName); + virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target); + virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target); + virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target); + virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex); + virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target); + virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName); + virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + +private: + struct Instruction { +#define MOTH_INSTR_DATA_TYPEDEF(I, FMT) typedef InstrData<Instr::I> I; + FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF) +#undef MOTH_INSTR_DATA_TYPEDEF + private: + Instruction(); + }; + + Instr::Param getParam(V4IR::Expr *e); + + Instr::Param getResultParam(V4IR::Temp *result) + { + if (result) + return getParam(result); + else + return Instr::Param::createTemp(scratchTempIndex()); + } + + void simpleMove(V4IR::Move *); + void prepareCallArgs(V4IR::ExprList *, quint32 &, quint32 &); + + int outgoingArgumentTempStart() const { return _function->tempCount; } + int scratchTempIndex() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; } + int frameSize() const { return scratchTempIndex() + 1; } + + template <int Instr> + inline ptrdiff_t addInstruction(const InstrData<Instr> &data); + ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr); + void patchJumpAddresses(); + uchar *squeezeCode() const; + + QV4::String *identifier(const QString &s); + + V4IR::Function *_function; + QV4::Function *_vmFunction; + V4IR::BasicBlock *_block; + V4IR::BasicBlock *_nextBlock; + + QHash<V4IR::BasicBlock *, QVector<ptrdiff_t> > _patches; + QHash<V4IR::BasicBlock *, ptrdiff_t> _addrs; + + uchar *_codeStart; + uchar *_codeNext; + uchar *_codeEnd; + + StackSlotAllocator *_stackSlotAllocator; + V4IR::Stmt *_currentStatement; +}; + +class Q_QML_EXPORT ISelFactory: public EvalISelFactory +{ +public: + virtual ~ISelFactory() {} + virtual EvalInstructionSelection *create(QV4::ExecutionEngine *engine, V4IR::Module *module) + { return new InstructionSelection(engine, module); } + virtual bool jitCompileRegexps() const + { return false; } +}; + +template<int InstrT> +ptrdiff_t InstructionSelection::addInstruction(const InstrData<InstrT> &data) +{ + Instr genericInstr; + InstrMeta<InstrT>::setData(genericInstr, data); + return addInstructionHelper(static_cast<Instr::Type>(InstrT), genericInstr); +} + +} // namespace Moth +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ISEL_MOTH_P_H diff --git a/src/qml/qml/v4/moth/qv4vme_moth.cpp b/src/qml/qml/v4/moth/qv4vme_moth.cpp new file mode 100644 index 0000000000..678d0edc4f --- /dev/null +++ b/src/qml/qml/v4/moth/qv4vme_moth.cpp @@ -0,0 +1,592 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4vme_moth_p.h" +#include "qv4instr_moth_p.h" +#include <private/qv4value_p.h> +#include <private/qv4debugging_p.h> +#include <private/qv4exception_p.h> +#include <private/qv4math_p.h> + +#include <iostream> + +#include "qv4alloca_p.h" + +#ifdef DO_TRACE_INSTR +# define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I); +# define TRACE(n, str, ...) { char buf[4096]; snprintf(buf, 4096, str, __VA_ARGS__); fprintf(stderr, " %s : %s\n", #n, buf); } +#else +# define TRACE_INSTR(I) +# define TRACE(n, str, ...) +#endif // DO_TRACE_INSTR + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +#define MOTH_BEGIN_INSTR_COMMON(I) { \ + const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \ + code += InstrMeta<(int)Instr::I>::Size; \ + if (context->engine->debugger && (instr.breakPoint || context->engine->debugger->pauseAtNextOpportunity())) \ + context->engine->debugger->maybeBreakAtInstruction(code, instr.breakPoint); \ + Q_UNUSED(instr); \ + TRACE_INSTR(I) + +#ifdef MOTH_THREADED_INTERPRETER + +# define MOTH_BEGIN_INSTR(I) op_##I: \ + MOTH_BEGIN_INSTR_COMMON(I) + +# define MOTH_NEXT_INSTR(I) { \ + genericInstr = reinterpret_cast<const Instr *>(code); \ + goto *genericInstr->common.code; \ + } + +# define MOTH_END_INSTR(I) } \ + genericInstr = reinterpret_cast<const Instr *>(code); \ + goto *genericInstr->common.code; \ + +#else + +# define MOTH_BEGIN_INSTR(I) \ + case Instr::I: \ + MOTH_BEGIN_INSTR_COMMON(I) + +# define MOTH_NEXT_INSTR(I) { \ + break; \ + } + +# define MOTH_END_INSTR(I) } \ + break; + +#endif + +#ifdef WITH_STATS +namespace { +struct VMStats { + quint64 paramIsValue; + quint64 paramIsArg; + quint64 paramIsLocal; + quint64 paramIsTemp; + quint64 paramIsScopedLocal; + + VMStats() + : paramIsValue(0) + , paramIsArg(0) + , paramIsLocal(0) + , paramIsTemp(0) + , paramIsScopedLocal(0) + {} + + ~VMStats() + { show(); } + + void show() { + fprintf(stderr, "VM stats:\n"); + fprintf(stderr, " value: %lu\n", paramIsValue); + fprintf(stderr, " arg: %lu\n", paramIsArg); + fprintf(stderr, " local: %lu\n", paramIsLocal); + fprintf(stderr, " temp: %lu\n", paramIsTemp); + fprintf(stderr, " scoped local: %lu\n", paramIsScopedLocal); + } +}; +static VMStats vmStats; +#define VMSTATS(what) ++vmStats.what +} +#else // !WITH_STATS +#define VMSTATS(what) {} +#endif // WITH_STATS + +static inline QV4::Value *getValueRef(QV4::ExecutionContext *context, + QV4::Value* stack, + const Instr::Param ¶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<QV4::Value *>(¶m.value); + } else if (param.isArgument()) { + VMSTATS(paramIsArg); + QV4::ExecutionContext *c = context; + uint scope = param.scope; + while (scope--) + c = c->outer; + QV4::CallContext *cc = static_cast<QV4::CallContext *>(c); + const unsigned arg = param.index; + Q_ASSERT(arg >= 0); + Q_ASSERT((unsigned) arg < cc->argumentCount); + Q_ASSERT(cc->arguments); + return cc->arguments + arg; + } else if (param.isLocal()) { + VMSTATS(paramIsLocal); + const unsigned index = param.index; + QV4::CallContext *c = static_cast<QV4::CallContext *>(context); + Q_ASSERT(index >= 0); + Q_ASSERT(index < context->variableCount()); + Q_ASSERT(c->locals); + return c->locals + index; + } else if (param.isTemp()) { + VMSTATS(paramIsTemp); + Q_ASSERT(param.index < stackSize); + return stack + param.index; + } else if (param.isScopedLocal()) { + VMSTATS(paramIsScopedLocal); + QV4::ExecutionContext *c = context; + uint scope = param.scope; + while (scope--) + c = c->outer; + const unsigned index = param.index; + QV4::CallContext *cc = static_cast<QV4::CallContext *>(c); + Q_ASSERT(index >= 0); + Q_ASSERT(index < cc->variableCount()); + Q_ASSERT(cc->locals); + return cc->locals + index; + } else { + Q_UNIMPLEMENTED(); + return 0; + } +} + +#if defined(QT_NO_DEBUG) +# define VALUE(param) (*VALUEPTR(param)) + +// The non-temp case might need some tweaking for QML: there it would probably be a value instead of a local. +# define VALUEPTR(param) \ + (param.isTemp() ? stack + param.index \ + : (param.isLocal() ? static_cast<QV4::CallContext *>(context)->locals + param.index \ + : getValueRef(context, stack, param))) +#else +# define VALUE(param) *getValueRef(context, stack, param, stackSize) +# define VALUEPTR(param) getValueRef(context, stack, param, stackSize) +#endif + +QV4::Value VME::run(QV4::ExecutionContext *context, const uchar *&code, + QV4::Value *stack, unsigned stackSize +#ifdef MOTH_THREADED_INTERPRETER + , void ***storeJumpTable +#endif + ) +{ +#ifdef DO_TRACE_INSTR + qDebug("Starting VME with context=%p and code=%p", context, code); +#endif // DO_TRACE_INSTR + +#ifdef MOTH_THREADED_INTERPRETER + if (storeJumpTable) { +#define MOTH_INSTR_ADDR(I, FMT) &&op_##I, + static void *jumpTable[] = { + FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR) + }; +#undef MOTH_INSTR_ADDR + *storeJumpTable = jumpTable; + return QV4::Value::undefinedValue(); + } +#endif + + context->interpreterInstructionPointer = &code; + +#ifdef MOTH_THREADED_INTERPRETER + const Instr *genericInstr = reinterpret_cast<const Instr *>(code); + goto *genericInstr->common.code; +#else + for (;;) { + const Instr *genericInstr = reinterpret_cast<const Instr *>(code); + switch (genericInstr->common.instructionType) { +#endif + + MOTH_BEGIN_INSTR(MoveTemp) + VALUE(instr.result) = VALUE(instr.source); + MOTH_END_INSTR(MoveTemp) + + MOTH_BEGIN_INSTR(LoadValue) +// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); + VALUE(instr.result) = VALUE(instr.value); + MOTH_END_INSTR(LoadValue) + + MOTH_BEGIN_INSTR(LoadClosure) + __qmljs_init_closure(context, VALUEPTR(instr.result), instr.value); + MOTH_END_INSTR(LoadClosure) + + MOTH_BEGIN_INSTR(LoadName) + TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); + __qmljs_get_activation_property(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(LoadName) + + MOTH_BEGIN_INSTR(StoreName) + TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); + __qmljs_set_activation_property(context, instr.name, VALUE(instr.source)); + MOTH_END_INSTR(StoreName) + + MOTH_BEGIN_INSTR(LoadElement) + __qmljs_get_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(LoadElement) + + MOTH_BEGIN_INSTR(StoreElement) + __qmljs_set_element(context, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); + MOTH_END_INSTR(StoreElement) + + MOTH_BEGIN_INSTR(LoadProperty) + __qmljs_get_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name); + MOTH_END_INSTR(LoadProperty) + + MOTH_BEGIN_INSTR(StoreProperty) + __qmljs_set_property(context, VALUE(instr.base), instr.name, VALUE(instr.source)); + MOTH_END_INSTR(StoreProperty) + + MOTH_BEGIN_INSTR(Push) + TRACE(inline, "stack size: %u", instr.value); + stackSize = instr.value; + stack = static_cast<QV4::Value *>(alloca(stackSize * sizeof(QV4::Value))); + memset(stack, 0, stackSize * sizeof(QV4::Value)); + MOTH_END_INSTR(Push) + + MOTH_BEGIN_INSTR(CallValue) +#ifdef DO_TRACE_INSTR + if (Debugging::Debugger *debugger = context->engine->debugger) { + if (QV4::FunctionObject *o = (VALUE(instr.dest)).asFunctionObject()) { + if (Debugging::FunctionDebugInfo *info = debugger->debugInfo(o)) { + QString n = debugger->name(o); + std::cerr << "*** Call to \"" << (n.isNull() ? "<no name>" : qPrintable(n)) << "\" defined @" << info->startLine << ":" << info->startColumn << std::endl; + } + } + } +#endif // DO_TRACE_INSTR + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_call_value(context, VALUEPTR(instr.result), /*thisObject*/0, VALUE(instr.dest), args, instr.argc); + MOTH_END_INSTR(CallValue) + + MOTH_BEGIN_INSTR(CallProperty) + TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(instr.name->toQString()), instr.args, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_call_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name, args, instr.argc); + MOTH_END_INSTR(CallProperty) + + MOTH_BEGIN_INSTR(CallElement) + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_call_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index), args, instr.argc); + MOTH_END_INSTR(CallElement) + + MOTH_BEGIN_INSTR(CallActivationProperty) + Q_ASSERT(instr.args + instr.argc <= stackSize); + TRACE(args, "starting at %d, length %d", instr.args, instr.argc); + QV4::Value *args = stack + instr.args; + __qmljs_call_activation_property(context, VALUEPTR(instr.result), instr.name, args, instr.argc); + MOTH_END_INSTR(CallActivationProperty) + + MOTH_BEGIN_INSTR(CallBuiltinThrow) + __qmljs_throw(context, VALUE(instr.arg)); + MOTH_END_INSTR(CallBuiltinThrow) + + MOTH_BEGIN_INSTR(EnterTry) + VALUE(instr.exceptionVar) = QV4::Value::undefinedValue(); + try { + const uchar *tryCode = ((uchar *)&instr.tryOffset) + instr.tryOffset; + run(context, tryCode, stack, stackSize); + code = tryCode; + context->interpreterInstructionPointer = &code; + } catch (QV4::Exception &ex) { + ex.accept(context); + VALUE(instr.exceptionVar) = ex.value(); + try { + QV4::ExecutionContext *catchContext = __qmljs_builtin_push_catch_scope(instr.exceptionVarName, ex.value(), context); + const uchar *catchCode = ((uchar *)&instr.catchOffset) + instr.catchOffset; + run(catchContext, catchCode, stack, stackSize); + code = catchCode; + context->interpreterInstructionPointer = &code; + context = __qmljs_builtin_pop_scope(catchContext); + } catch (QV4::Exception &ex) { + ex.accept(context); + VALUE(instr.exceptionVar) = ex.value(); + const uchar *catchCode = ((uchar *)&instr.catchOffset) + instr.catchOffset; + run(context, catchCode, stack, stackSize); + code = catchCode; + context->interpreterInstructionPointer = &code; + } + } + MOTH_END_INSTR(EnterTry) + + MOTH_BEGIN_INSTR(CallBuiltinFinishTry) + return QV4::Value(); + MOTH_END_INSTR(CallBuiltinFinishTry) + + MOTH_BEGIN_INSTR(CallBuiltinPushScope) + context = __qmljs_builtin_push_with_scope(VALUE(instr.arg), context); + MOTH_END_INSTR(CallBuiltinPushScope) + + MOTH_BEGIN_INSTR(CallBuiltinPopScope) + context = __qmljs_builtin_pop_scope(context); + MOTH_END_INSTR(CallBuiltinPopScope) + + MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) + __qmljs_foreach_iterator_object(context, VALUEPTR(instr.result), VALUE(instr.arg)); + MOTH_END_INSTR(CallBuiltinForeachIteratorObject) + + MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName) + __qmljs_foreach_next_property_name(VALUEPTR(instr.result), VALUE(instr.arg)); + MOTH_END_INSTR(CallBuiltinForeachNextPropertyName) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) + __qmljs_delete_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinDeleteMember) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript) + __qmljs_delete_subscript(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(CallBuiltinDeleteSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteName) + __qmljs_delete_name(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(CallBuiltinDeleteName) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofMember) + __qmljs_builtin_typeof_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript) + __qmljs_builtin_typeof_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofName) + __qmljs_builtin_typeof_name(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofValue) + __qmljs_builtin_typeof(context, VALUEPTR(instr.result), VALUE(instr.value)); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncMember) + __qmljs_builtin_post_increment_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncSubscript) + __qmljs_builtin_post_increment_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUEPTR(instr.index)); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncName) + __qmljs_builtin_post_increment_name(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncValue) + __qmljs_builtin_post_increment(VALUEPTR(instr.result), VALUEPTR(instr.value)); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecMember) + __qmljs_builtin_post_decrement_member(context, VALUEPTR(instr.result), VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecSubscript) + __qmljs_builtin_post_decrement_element(context, VALUEPTR(instr.result), VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecName) + __qmljs_builtin_post_decrement_name(context, VALUEPTR(instr.result), instr.name); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecValue) + __qmljs_builtin_post_decrement(VALUEPTR(instr.result), VALUEPTR(instr.value)); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) + __qmljs_builtin_declare_var(context, instr.isDeletable, instr.varName); + MOTH_END_INSTR(CallBuiltinDeclareVar) + + MOTH_BEGIN_INSTR(CallBuiltinDefineGetterSetter) + __qmljs_builtin_define_getter_setter(context, VALUE(instr.object), instr.name, VALUEPTR(instr.getter), VALUEPTR(instr.setter)); + MOTH_END_INSTR(CallBuiltinDefineGetterSetter) + + MOTH_BEGIN_INSTR(CallBuiltinDefineProperty) + __qmljs_builtin_define_property(context, VALUE(instr.object), instr.name, VALUEPTR(instr.value)); + MOTH_END_INSTR(CallBuiltinDefineProperty) + + MOTH_BEGIN_INSTR(CallBuiltinDefineArray) + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_builtin_define_array(context, VALUEPTR(instr.result), args, instr.argc); + MOTH_END_INSTR(CallBuiltinDefineArray) + + MOTH_BEGIN_INSTR(CallBuiltinDefineObjectLiteral) + QV4::Value *args = stack + instr.args; + __qmljs_builtin_define_object_literal(context, VALUEPTR(instr.result), args, instr.internalClass); + MOTH_END_INSTR(CallBuiltinDefineObjectLiteral) + + MOTH_BEGIN_INSTR(CreateValue) + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_construct_value(context, VALUEPTR(instr.result), VALUE(instr.func), args, instr.argc); + MOTH_END_INSTR(CreateValue) + + MOTH_BEGIN_INSTR(CreateProperty) + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_construct_property(context, VALUEPTR(instr.result), VALUE(instr.base), instr.name, args, instr.argc); + MOTH_END_INSTR(CreateProperty) + + MOTH_BEGIN_INSTR(CreateActivationProperty) + TRACE(inline, "property name = %s, args = %d, argc = %d", instr.name->toQString().toUtf8().constData(), instr.args, instr.argc); + Q_ASSERT(instr.args + instr.argc <= stackSize); + QV4::Value *args = stack + instr.args; + __qmljs_construct_activation_property(context, VALUEPTR(instr.result), instr.name, args, instr.argc); + MOTH_END_INSTR(CreateActivationProperty) + + MOTH_BEGIN_INSTR(Jump) + code = ((uchar *)&instr.offset) + instr.offset; + MOTH_END_INSTR(Jump) + + MOTH_BEGIN_INSTR(CJump) + uint cond = __qmljs_to_boolean(VALUE(instr.condition)); + TRACE(condition, "%s", cond ? "TRUE" : "FALSE"); + if (cond) + code = ((uchar *)&instr.offset) + instr.offset; + MOTH_END_INSTR(CJump) + + MOTH_BEGIN_INSTR(Unop) + instr.alu(VALUEPTR(instr.result), VALUE(instr.source)); + MOTH_END_INSTR(Unop) + + MOTH_BEGIN_INSTR(Binop) + instr.alu(context, VALUEPTR(instr.result), VALUE(instr.lhs), VALUE(instr.rhs)); + MOTH_END_INSTR(Binop) + + MOTH_BEGIN_INSTR(AddNumberParams) + QV4::Value lhs = VALUE(instr.lhs); + QV4::Value rhs = VALUE(instr.rhs); + if (lhs.isInteger() && rhs.isInteger()) + VALUE(instr.result) = QV4::add_int32(lhs.integerValue(), rhs.integerValue()); + else + VALUEPTR(instr.result)->setDouble(lhs.asDouble() + rhs.asDouble()); + MOTH_END_INSTR(AddNumberParams) + + MOTH_BEGIN_INSTR(MulNumberParams) + QV4::Value lhs = VALUE(instr.lhs); + QV4::Value rhs = VALUE(instr.rhs); + if (lhs.isInteger() && rhs.isInteger()) + VALUE(instr.result) = QV4::mul_int32(lhs.integerValue(), rhs.integerValue()); + else + VALUEPTR(instr.result)->setDouble(lhs.asDouble() * rhs.asDouble()); + MOTH_END_INSTR(MulNumberParams) + + MOTH_BEGIN_INSTR(SubNumberParams) + QV4::Value lhs = VALUE(instr.lhs); + QV4::Value rhs = VALUE(instr.rhs); + if (lhs.isInteger() && rhs.isInteger()) + VALUE(instr.result) = QV4::sub_int32(lhs.integerValue(), rhs.integerValue()); + else + VALUEPTR(instr.result)->setDouble(lhs.asDouble() - rhs.asDouble()); + MOTH_END_INSTR(SubNumberParams) + + MOTH_BEGIN_INSTR(Ret) + QV4::Value &result = VALUE(instr.result); +// TRACE(Ret, "returning value %s", result.toString(context)->toQString().toUtf8().constData()); + return result; + MOTH_END_INSTR(Ret) + + MOTH_BEGIN_INSTR(LoadThis) + VALUE(instr.result) = context->thisObject; + MOTH_END_INSTR(LoadThis) + + MOTH_BEGIN_INSTR(InplaceElementOp) + instr.alu(context, + VALUE(instr.base), + VALUE(instr.index), + VALUE(instr.source)); + MOTH_END_INSTR(InplaceElementOp) + + MOTH_BEGIN_INSTR(InplaceMemberOp) + instr.alu(context, + VALUE(instr.base), + instr.member, + VALUE(instr.source)); + MOTH_END_INSTR(InplaceMemberOp) + + MOTH_BEGIN_INSTR(InplaceNameOp) + TRACE(name, "%s", instr.name->toQString().toUtf8().constData()); + instr.alu(context, instr.name, VALUE(instr.source)); + MOTH_END_INSTR(InplaceNameOp) + +#ifdef MOTH_THREADED_INTERPRETER + // nothing to do +#else + default: + qFatal("QQmlJS::Moth::VME: Internal error - unknown instruction %d", genericInstr->common.instructionType); + break; + } + } +#endif + +} + +#ifdef MOTH_THREADED_INTERPRETER +void **VME::instructionJumpTable() +{ + static void **jumpTable = 0; + if (!jumpTable) { + const uchar *code = 0; + VME().run(0, code, 0, 0, &jumpTable); + } + return jumpTable; +} +#endif + +QV4::Value VME::exec(QV4::ExecutionContext *ctxt, const uchar *code) +{ + VME vme; + return vme.run(ctxt, code); +} diff --git a/src/qml/qml/v4/moth/qv4vme_moth_p.h b/src/qml/qml/v4/moth/qv4vme_moth_p.h new file mode 100644 index 0000000000..59692500ba --- /dev/null +++ b/src/qml/qml/v4/moth/qv4vme_moth_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4VME_MOTH_P_H +#define QV4VME_MOTH_P_H + +#include <private/qv4runtime_p.h> +#include "qv4instr_moth_p.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace Moth { + +class VME +{ +public: + static QV4::Value exec(QV4::ExecutionContext *, const uchar *); + +#ifdef MOTH_THREADED_INTERPRETER + static void **instructionJumpTable(); +#endif + +private: + QV4::Value run(QV4::ExecutionContext *, const uchar *&code, + QV4::Value *stack = 0, unsigned stackSize = 0 +#ifdef MOTH_THREADED_INTERPRETER + , void ***storeJumpTable = 0 +#endif + ); +}; + +} // namespace Moth +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4VME_MOTH_P_H diff --git a/src/qml/qml/v4/qv4_llvm_p.h b/src/qml/qml/v4/qv4_llvm_p.h new file mode 100644 index 0000000000..50bd7d3831 --- /dev/null +++ b/src/qml/qml/v4/qv4_llvm_p.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4_LLVM_P_H +#define QV4_LLVM_P_H + +#include "qv4global_p.h" +#include "qv4jsir_p.h" + +#include <QtCore/QString> + +namespace QQmlJS { + +// Note: keep this enum in sync with the command-line option! +enum LLVMOutputType { + LLVMOutputJit = -1, + LLVMOutputIR = 0, // .ll + LLVMOutputBitcode = 1, // .bc + LLVMOutputAssembler = 2, // .s + LLVMOutputObject = 3 // .o +}; + +Q_QML_EXPORT int compileWithLLVM(V4IR::Module *module, const QString &fileName, LLVMOutputType outputType, int (*)(void *)); + +} // QQmlJS + +#endif // QV4_LLVM_P_H diff --git a/src/qml/qml/v4/qv4alloca_p.h b/src/qml/qml/v4/qv4alloca_p.h new file mode 100644 index 0000000000..e4580da3d8 --- /dev/null +++ b/src/qml/qml/v4/qv4alloca_p.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4_ALLOCA_H +#define QV4_ALLOCA_H + +#include <qglobal.h> + +#if defined(Q_OS_WIN) +#include <malloc.h> +#define alloca _alloca +#else +#include <alloca.h> +#endif + +#endif diff --git a/src/qml/qml/v4/qv4argumentsobject.cpp b/src/qml/qml/v4/qv4argumentsobject.cpp new file mode 100644 index 0000000000..6247ef1504 --- /dev/null +++ b/src/qml/qml/v4/qv4argumentsobject.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qv4argumentsobject_p.h> + +using namespace QV4; + +static Value throwTypeError(SimpleCallContext *ctx) +{ + ctx->throwTypeError(); + return Value::undefinedValue(); +} + +DEFINE_MANAGED_VTABLE(ArgumentsObject); + +ArgumentsObject::ArgumentsObject(CallContext *context, int formalParameterCount, int actualParameterCount) + : Object(context->engine), context(context) +{ + vtbl = &static_vtbl; + type = Type_ArgumentsObject; + + defineDefaultProperty(context->engine->id_length, Value::fromInt32(actualParameterCount)); + if (context->strictMode) { + for (uint i = 0; i < context->argumentCount; ++i) + Object::put(context, QString::number(i), context->arguments[i]); + FunctionObject *thrower = context->engine->newBuiltinFunction(context, 0, throwTypeError); + Property pd = Property::fromAccessor(thrower, thrower); + __defineOwnProperty__(context, QStringLiteral("callee"), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + __defineOwnProperty__(context, QStringLiteral("caller"), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + } else { + uint numAccessors = qMin(formalParameterCount, actualParameterCount); + context->engine->requireArgumentsAccessors(numAccessors); + for (uint i = 0; i < (uint)numAccessors; ++i) { + mappedArguments.append(context->argument(i)); + __defineOwnProperty__(context, i, context->engine->argumentsAccessors.at(i), Attr_Accessor); + } + for (uint i = numAccessors; i < qMin((uint)actualParameterCount, context->argumentCount); ++i) { + Property pd = Property::fromValue(context->argument(i)); + __defineOwnProperty__(context, i, pd, Attr_Data); + } + defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); + isNonStrictArgumentsObject = true; + } +} + +void ArgumentsObject::destroy(Managed *that) +{ + static_cast<ArgumentsObject *>(that)->~ArgumentsObject(); +} + +bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs) +{ + uint pidx = propertyIndexFromArrayIndex(index); + Property *pd = arrayData + pidx; + Property map; + PropertyAttributes mapAttrs; + bool isMapped = false; + if (pd && index < (uint)mappedArguments.size()) + isMapped = arrayAttributes && arrayAttributes[pidx].isAccessor() && pd->getter() == context->engine->argumentsAccessors.at(index).getter(); + + if (isMapped) { + map = *pd; + mapAttrs = arrayAttributes[pidx]; + arrayAttributes[pidx] = Attr_Data; + pd->value = mappedArguments.at(index); + } + + isNonStrictArgumentsObject = false; + bool strict = ctx->strictMode; + ctx->strictMode = false; + bool result = Object::__defineOwnProperty__(ctx, index, desc, attrs); + ctx->strictMode = strict; + isNonStrictArgumentsObject = true; + + if (isMapped && attrs.isData()) { + if (!attrs.isGeneric()) { + Value arg = desc.value; + map.setter()->call(Value::fromObject(this), &arg, 1); + } + if (attrs.isWritable()) { + *pd = map; + arrayAttributes[pidx] = mapAttrs; + } + } + + if (ctx->strictMode && !result) + ctx->throwTypeError(); + return result; +} + +DEFINE_MANAGED_VTABLE(ArgumentsGetterFunction); + +Value ArgumentsGetterFunction::call(Managed *getter, const Value &thisObject, Value *, int) +{ + ArgumentsGetterFunction *g = static_cast<ArgumentsGetterFunction *>(getter); + Object *that = thisObject.asObject(); + if (!that) + getter->engine()->current->throwTypeError(); + ArgumentsObject *o = that->asArgumentsObject(); + if (!o) + getter->engine()->current->throwTypeError(); + + assert(g->index < o->context->argumentCount); + return o->context->argument(g->index); +} + +DEFINE_MANAGED_VTABLE(ArgumentsSetterFunction); + +Value ArgumentsSetterFunction::call(Managed *setter, const Value &thisObject, Value *args, int argc) +{ + ArgumentsSetterFunction *s = static_cast<ArgumentsSetterFunction *>(setter); + Object *that = thisObject.asObject(); + if (!that) + setter->engine()->current->throwTypeError(); + ArgumentsObject *o = that->asArgumentsObject(); + if (!o) + setter->engine()->current->throwTypeError(); + + assert(s->index < o->context->argumentCount); + o->context->arguments[s->index] = argc ? args[0] : Value::undefinedValue(); + return Value::undefinedValue(); +} + +void ArgumentsObject::markObjects(Managed *that) +{ + ArgumentsObject *o = static_cast<ArgumentsObject *>(that); + o->context->mark(); + for (int i = 0; i < o->mappedArguments.size(); ++i) { + Managed *m = o->mappedArguments.at(i).asManaged(); + if (m) + m->mark(); + } + Object::markObjects(that); +} diff --git a/src/qml/qml/v4/qv4argumentsobject_p.h b/src/qml/qml/v4/qv4argumentsobject_p.h new file mode 100644 index 0000000000..3d760b8937 --- /dev/null +++ b/src/qml/qml/v4/qv4argumentsobject_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ARGUMENTSOBJECTS_H +#define QV4ARGUMENTSOBJECTS_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ArgumentsGetterFunction: FunctionObject +{ + uint index; + + ArgumentsGetterFunction(ExecutionContext *scope, uint index) + : FunctionObject(scope), index(index) { vtbl = &static_vtbl; } + + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct ArgumentsSetterFunction: FunctionObject +{ + uint index; + + ArgumentsSetterFunction(ExecutionContext *scope, uint index) + : FunctionObject(scope), index(index) { vtbl = &static_vtbl; } + + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + + +struct ArgumentsObject: Object { + CallContext *context; + QVector<Value> mappedArguments; + ArgumentsObject(CallContext *context, int formalParameterCount, int actualParameterCount); + ~ArgumentsObject() {} + + bool defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs); + + static void markObjects(Managed *that); +protected: + static const ManagedVTable static_vtbl; + static void destroy(Managed *); +}; + +} + +QT_END_NAMESPACE + +#endif + diff --git a/src/qml/qml/v4/qv4arrayobject.cpp b/src/qml/qml/v4/qv4arrayobject.cpp new file mode 100644 index 0000000000..b1f25177dd --- /dev/null +++ b/src/qml/qml/v4/qv4arrayobject.cpp @@ -0,0 +1,865 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4arrayobject_p.h" +#include "qv4sparsearray_p.h" + +using namespace QV4; + +DEFINE_MANAGED_VTABLE(ArrayCtor); + +ArrayCtor::ArrayCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Array"))) +{ + vtbl = &static_vtbl; +} + +Value ArrayCtor::construct(Managed *m, Value *argv, int argc) +{ + ExecutionEngine *v4 = m->engine(); + ArrayObject *a = v4->newArrayObject(); + uint len; + if (argc == 1 && argv[0].isNumber()) { + bool ok; + len = argv[0].asArrayLength(&ok); + + if (!ok) + v4->current->throwRangeError(argv[0]); + + if (len < 0x1000) + a->arrayReserve(len); + } else { + len = argc; + a->arrayReserve(len); + for (unsigned int i = 0; i < len; ++i) + a->arrayData[i].value = argv[i]; + a->arrayDataLen = len; + } + a->setArrayLengthUnchecked(len); + + return Value::fromObject(a); +} + +Value ArrayCtor::call(Managed *that, const Value &, Value *argv, int argc) +{ + return construct(that, argv, argc); +} + +ArrayPrototype::ArrayPrototype(ExecutionContext *context) + : ArrayObject(context->engine) +{ +} + +void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isArray"), method_isArray, 1); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("concat"), method_concat, 1); + defineDefaultProperty(ctx, QStringLiteral("join"), method_join, 1); + defineDefaultProperty(ctx, QStringLiteral("pop"), method_pop, 0); + defineDefaultProperty(ctx, QStringLiteral("push"), method_push, 1); + defineDefaultProperty(ctx, QStringLiteral("reverse"), method_reverse, 0); + defineDefaultProperty(ctx, QStringLiteral("shift"), method_shift, 0); + defineDefaultProperty(ctx, QStringLiteral("slice"), method_slice, 2); + defineDefaultProperty(ctx, QStringLiteral("sort"), method_sort, 1); + defineDefaultProperty(ctx, QStringLiteral("splice"), method_splice, 2); + defineDefaultProperty(ctx, QStringLiteral("unshift"), method_unshift, 1); + defineDefaultProperty(ctx, QStringLiteral("indexOf"), method_indexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); + defineDefaultProperty(ctx, QStringLiteral("every"), method_every, 1); + defineDefaultProperty(ctx, QStringLiteral("some"), method_some, 1); + defineDefaultProperty(ctx, QStringLiteral("forEach"), method_forEach, 1); + defineDefaultProperty(ctx, QStringLiteral("map"), method_map, 1); + defineDefaultProperty(ctx, QStringLiteral("filter"), method_filter, 1); + defineDefaultProperty(ctx, QStringLiteral("reduce"), method_reduce, 1); + defineDefaultProperty(ctx, QStringLiteral("reduceRight"), method_reduceRight, 1); +} + +uint ArrayPrototype::getLength(ExecutionContext *ctx, Object *o) +{ + if (o->isArrayObject()) + return o->arrayLength(); + return o->get(ctx->engine->id_length).toUInt32(); +} + +Value ArrayPrototype::method_isArray(SimpleCallContext *ctx) +{ + Value arg = ctx->argument(0); + bool isArray = arg.asArrayObject(); + return Value::fromBoolean(isArray); +} + +Value ArrayPrototype::method_toString(SimpleCallContext *ctx) +{ + return method_join(ctx); +} + +Value ArrayPrototype::method_toLocaleString(SimpleCallContext *ctx) +{ + return method_toString(ctx); +} + +Value ArrayPrototype::method_concat(SimpleCallContext *ctx) +{ + ArrayObject *result = ctx->engine->newArrayObject(); + + if (ArrayObject *instance = ctx->thisObject.asArrayObject()) { + result->copyArrayData(instance); + } else { + QString v = ctx->thisObject.toString(ctx)->toQString(); + result->arraySet(0, Value::fromString(ctx, v)); + } + + for (uint i = 0; i < ctx->argumentCount; ++i) { + Value arg = ctx->argument(i); + + if (ArrayObject *elt = arg.asArrayObject()) + result->arrayConcat(elt); + + else + result->arraySet(getLength(ctx, result), arg); + } + + return Value::fromObject(result); +} + +Value ArrayPrototype::method_join(SimpleCallContext *ctx) +{ + Value arg = ctx->argument(0); + + QString r4; + if (arg.isUndefined()) + r4 = QStringLiteral(","); + else + r4 = arg.toString(ctx)->toQString(); + + Value self = ctx->thisObject; + const Value length = self.property(ctx, ctx->engine->id_length); + const quint32 r2 = Value::toUInt32(length.isUndefined() ? 0 : length.toNumber()); + + static QSet<Object *> visitedArrayElements; + + if (! r2 || visitedArrayElements.contains(self.objectValue())) + return Value::fromString(ctx, QString()); + + // avoid infinite recursion + visitedArrayElements.insert(self.objectValue()); + + QString R; + + // ### FIXME + if (ArrayObject *a = self.asArrayObject()) { + for (uint i = 0; i < a->arrayLength(); ++i) { + if (i) + R += r4; + + Value e = a->getIndexed(i); + if (! (e.isUndefined() || e.isNull())) + R += e.toString(ctx)->toQString(); + } + } else { + // + // crazy! + // + Value r6 = self.property(ctx, ctx->engine->newString(QStringLiteral("0"))); + if (!(r6.isUndefined() || r6.isNull())) + R = r6.toString(ctx)->toQString(); + + for (quint32 k = 1; k < r2; ++k) { + R += r4; + + String *name = Value::fromDouble(k).toString(ctx); + Value r12 = self.property(ctx, name); + + if (! (r12.isUndefined() || r12.isNull())) + R += r12.toString(ctx)->toQString(); + } + } + + visitedArrayElements.remove(self.objectValue()); + return Value::fromString(ctx, R); +} + +Value ArrayPrototype::method_pop(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + + if (!len) { + if (!instance->isArrayObject()) + instance->put(ctx->engine->id_length, Value::fromInt32(0)); + return Value::undefinedValue(); + } + + Value result = instance->getIndexed(len - 1); + + instance->deleteIndexedProperty(len - 1); + if (instance->isArrayObject()) + instance->setArrayLengthUnchecked(len - 1); + else + instance->put(ctx->engine->id_length, Value::fromDouble(len - 1)); + return result; +} + +Value ArrayPrototype::method_push(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + + if (len + ctx->argumentCount < len) { + // ughh... + double l = len; + for (double i = 0; i < ctx->argumentCount; ++i) { + Value idx = Value::fromDouble(l + i); + instance->put(idx.toString(ctx), ctx->argument(i)); + } + double newLen = l + ctx->argumentCount; + if (!instance->isArrayObject()) + instance->put(ctx->engine->id_length, Value::fromDouble(newLen)); + else + ctx->throwRangeError(Value::fromString(ctx, QStringLiteral("Array.prototype.push: Overflow"))); + return Value::fromDouble(newLen); + } + + bool protoHasArray = false; + Object *p = instance; + while ((p = p->prototype)) + if (p->arrayDataLen) + protoHasArray = true; + + if (!protoHasArray && instance->arrayDataLen <= len) { + for (uint i = 0; i < ctx->argumentCount; ++i) { + Value v = ctx->argument(i); + + if (!instance->sparseArray) { + if (len >= instance->arrayAlloc) + instance->arrayReserve(len + 1); + instance->arrayData[len].value = v; + if (instance->arrayAttributes) + instance->arrayAttributes[len] = Attr_Data; + instance->arrayDataLen = len + 1; + } else { + uint i = instance->allocArrayValue(v); + instance->sparseArray->push_back(i, len); + } + ++len; + } + } else { + for (uint i = 0; i < ctx->argumentCount; ++i) + instance->putIndexed(len + i, ctx->argument(i)); + len += ctx->argumentCount; + } + if (instance->isArrayObject()) + instance->setArrayLengthUnchecked(len); + else + instance->put(ctx->engine->id_length, Value::fromDouble(len)); + + if (len < INT_MAX) + return Value::fromInt32(len); + return Value::fromDouble((double)len); + +} + +Value ArrayPrototype::method_reverse(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint length = getLength(ctx, instance); + + int lo = 0, hi = length - 1; + + for (; lo < hi; ++lo, --hi) { + bool loExists, hiExists; + Value lval = instance->getIndexed(lo, &loExists); + Value hval = instance->getIndexed(hi, &hiExists); + if (hiExists) + instance->putIndexed(lo, hval); + else + instance->deleteIndexedProperty(lo); + if (loExists) + instance->putIndexed(hi, lval); + else + instance->deleteIndexedProperty(hi); + } + return Value::fromObject(instance); +} + +Value ArrayPrototype::method_shift(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + + if (!len) { + if (!instance->isArrayObject()) + instance->put(ctx->engine->id_length, Value::fromInt32(0)); + return Value::undefinedValue(); + } + + Property *front = 0; + uint pidx = instance->propertyIndexFromArrayIndex(0); + if (pidx < UINT_MAX && (!instance->arrayAttributes || !instance->arrayAttributes[0].isGeneric())) + front = instance->arrayData + pidx; + + Value result = front ? instance->getValue(front, instance->arrayAttributes ? instance->arrayAttributes[pidx] : Attr_Data) : Value::undefinedValue(); + + bool protoHasArray = false; + Object *p = instance; + while ((p = p->prototype)) + if (p->arrayDataLen) + protoHasArray = true; + + if (!protoHasArray && instance->arrayDataLen <= len) { + if (!instance->sparseArray) { + if (instance->arrayDataLen) { + ++instance->arrayOffset; + ++instance->arrayData; + --instance->arrayDataLen; + --instance->arrayAlloc; + if (instance->arrayAttributes) + ++instance->arrayAttributes; + } + } else { + uint idx = instance->sparseArray->pop_front(); + instance->freeArrayValue(idx); + } + } else { + // do it the slow way + for (uint k = 1; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(k, &exists); + if (exists) + instance->putIndexed(k - 1, v); + else + instance->deleteIndexedProperty(k - 1); + } + instance->deleteIndexedProperty(len - 1); + } + + if (instance->isArrayObject()) + instance->setArrayLengthUnchecked(len - 1); + else + instance->put(ctx->engine->id_length, Value::fromDouble(len - 1)); + return result; +} + +Value ArrayPrototype::method_slice(SimpleCallContext *ctx) +{ + Object *o = ctx->thisObject.toObject(ctx); + + ArrayObject *result = ctx->engine->newArrayObject(); + uint len = o->get(ctx->engine->id_length).toUInt32(); + double s = ctx->argument(0).toInteger(); + uint start; + if (s < 0) + start = (uint)qMax(len + s, 0.); + else if (s > len) + start = len; + else + start = (uint) s; + uint end = len; + if (!ctx->argument(1).isUndefined()) { + double e = ctx->argument(1).toInteger(); + if (e < 0) + end = (uint)qMax(len + e, 0.); + else if (e > len) + end = len; + else + end = (uint) e; + } + + uint n = 0; + for (uint i = start; i < end; ++i) { + bool exists; + Value v = o->getIndexed(i, &exists); + if (exists) { + result->arraySet(n, v); + } + ++n; + } + return Value::fromObject(result); +} + +Value ArrayPrototype::method_sort(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + Value comparefn = ctx->argument(0); + instance->arraySort(ctx, instance, comparefn, len); + return ctx->thisObject; +} + +Value ArrayPrototype::method_splice(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + + ArrayObject *newArray = ctx->engine->newArrayObject(); + + double rs = ctx->argument(0).toInteger(); + uint start; + if (rs < 0) + start = (uint) qMax(0., len + rs); + else + start = (uint) qMin(rs, (double)len); + + uint deleteCount = (uint)qMin(qMax(ctx->argument(1).toInteger(), 0.), (double)(len - start)); + + newArray->arrayReserve(deleteCount); + Property *pd = newArray->arrayData; + for (uint i = 0; i < deleteCount; ++i) { + pd->value = instance->getIndexed(start + i); + ++pd; + } + newArray->arrayDataLen = deleteCount; + newArray->setArrayLengthUnchecked(deleteCount); + + uint itemCount = ctx->argumentCount < 2 ? 0 : ctx->argumentCount - 2; + + if (itemCount < deleteCount) { + for (uint k = start; k < len - deleteCount; ++k) { + bool exists; + Value v = instance->getIndexed(k + deleteCount, &exists); + if (exists) + instance->putIndexed(k + itemCount, v); + else + instance->deleteIndexedProperty(k + itemCount); + } + for (uint k = len; k > len - deleteCount + itemCount; --k) + instance->deleteIndexedProperty(k - 1); + } else if (itemCount > deleteCount) { + uint k = len - deleteCount; + while (k > start) { + bool exists; + Value v = instance->getIndexed(k + deleteCount - 1, &exists); + if (exists) + instance->putIndexed(k + itemCount - 1, v); + else + instance->deleteIndexedProperty(k + itemCount - 1); + --k; + } + } + + for (uint i = 0; i < itemCount; ++i) + instance->putIndexed(start + i, ctx->argument(i + 2)); + + ctx->strictMode = true; + instance->put(ctx->engine->id_length, Value::fromDouble(len - deleteCount + itemCount)); + + return Value::fromObject(newArray); +} + +Value ArrayPrototype::method_unshift(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + + bool protoHasArray = false; + Object *p = instance; + while ((p = p->prototype)) + if (p->arrayDataLen) + protoHasArray = true; + + if (!protoHasArray && instance->arrayDataLen <= len) { + for (int i = ctx->argumentCount - 1; i >= 0; --i) { + Value v = ctx->argument(i); + + if (!instance->sparseArray) { + if (!instance->arrayOffset) + instance->getArrayHeadRoom(); + + --instance->arrayOffset; + --instance->arrayData; + ++instance->arrayDataLen; + if (instance->arrayAttributes) { + --instance->arrayAttributes; + *instance->arrayAttributes = Attr_Data; + } + instance->arrayData->value = v; + } else { + uint idx = instance->allocArrayValue(v); + instance->sparseArray->push_front(idx); + } + } + } else { + for (uint k = len; k > 0; --k) { + bool exists; + Value v = instance->getIndexed(k - 1, &exists); + if (exists) + instance->putIndexed(k + ctx->argumentCount - 1, v); + else + instance->deleteIndexedProperty(k + ctx->argumentCount - 1); + } + for (uint i = 0; i < ctx->argumentCount; ++i) + instance->putIndexed(i, ctx->argument(i)); + } + + uint newLen = len + ctx->argumentCount; + if (instance->isArrayObject()) + instance->setArrayLengthUnchecked(newLen); + else + instance->put(ctx->engine->id_length, Value::fromDouble(newLen)); + + if (newLen < INT_MAX) + return Value::fromInt32(newLen); + return Value::fromDouble((double)newLen); +} + +Value ArrayPrototype::method_indexOf(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + if (!len) + return Value::fromInt32(-1); + + Value searchValue; + uint fromIndex = 0; + + if (ctx->argumentCount >= 1) + searchValue = ctx->argument(0); + else + searchValue = Value::undefinedValue(); + + if (ctx->argumentCount >= 2) { + double f = ctx->argument(1).toInteger(); + if (f >= len) + return Value::fromInt32(-1); + if (f < 0) + f = qMax(len + f, 0.); + fromIndex = (uint) f; + } + + if (instance->isStringObject()) { + for (uint k = fromIndex; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(k, &exists); + if (exists && __qmljs_strict_equal(v, searchValue)) + return Value::fromDouble(k); + } + return Value::fromInt32(-1); + } + + return instance->arrayIndexOf(searchValue, fromIndex, len, ctx, instance); +} + +Value ArrayPrototype::method_lastIndexOf(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + uint len = getLength(ctx, instance); + if (!len) + return Value::fromInt32(-1); + + Value searchValue; + uint fromIndex = len; + + if (ctx->argumentCount >= 1) + searchValue = ctx->argument(0); + else + searchValue = Value::undefinedValue(); + + if (ctx->argumentCount >= 2) { + double f = ctx->argument(1).toInteger(); + if (f > 0) + f = qMin(f, (double)(len - 1)); + else if (f < 0) { + f = len + f; + if (f < 0) + return Value::fromInt32(-1); + } + fromIndex = (uint) f + 1; + } + + for (uint k = fromIndex; k > 0;) { + --k; + bool exists; + Value v = instance->getIndexed(k, &exists); + if (exists && __qmljs_strict_equal(v, searchValue)) + return Value::fromDouble(k); + } + return Value::fromInt32(-1); +} + +Value ArrayPrototype::method_every(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + Value thisArg = ctx->argument(1); + + bool ok = true; + for (uint k = 0; ok && k < len; ++k) { + bool exists; + Value v = instance->getIndexed(k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = callback->call(thisArg, args, 3); + ok = r.toBoolean(); + } + return Value::fromBoolean(ok); +} + +Value ArrayPrototype::method_some(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + Value thisArg = ctx->argument(1); + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value r = callback->call(thisArg, args, 3); + if (r.toBoolean()) + return Value::fromBoolean(true); + } + return Value::fromBoolean(false); +} + +Value ArrayPrototype::method_forEach(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + Value thisArg = ctx->argument(1); + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + callback->call(thisArg, args, 3); + } + return Value::undefinedValue(); +} + +Value ArrayPrototype::method_map(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + Value thisArg = ctx->argument(1); + + ArrayObject *a = ctx->engine->newArrayObject(); + a->arrayReserve(len); + a->setArrayLengthUnchecked(len); + + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value mapped = callback->call(thisArg, args, 3); + a->arraySet(k, mapped); + } + return Value::fromObject(a); +} + +Value ArrayPrototype::method_filter(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + Value thisArg = ctx->argument(1); + + ArrayObject *a = ctx->engine->newArrayObject(); + a->arrayReserve(len); + + uint to = 0; + for (uint k = 0; k < len; ++k) { + bool exists; + Value v = instance->getIndexed(k, &exists); + if (!exists) + continue; + + Value args[3]; + args[0] = v; + args[1] = Value::fromDouble(k); + args[2] = ctx->thisObject; + Value selected = callback->call(thisArg, args, 3); + if (selected.toBoolean()) { + a->arraySet(to, v); + ++to; + } + } + return Value::fromObject(a); +} + +Value ArrayPrototype::method_reduce(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + uint k = 0; + Value acc; + if (ctx->argumentCount > 1) { + acc = ctx->argument(1); + } else { + bool kPresent = false; + while (k < len && !kPresent) { + Value v = instance->getIndexed(k, &kPresent); + if (kPresent) + acc = v; + ++k; + } + if (!kPresent) + ctx->throwTypeError(); + } + + while (k < len) { + bool kPresent; + Value v = instance->getIndexed(k, &kPresent); + if (kPresent) { + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k); + args[3] = ctx->thisObject; + acc = callback->call(Value::undefinedValue(), args, 4); + } + ++k; + } + return acc; +} + +Value ArrayPrototype::method_reduceRight(SimpleCallContext *ctx) +{ + Object *instance = ctx->thisObject.toObject(ctx); + + uint len = getLength(ctx, instance); + + FunctionObject *callback = ctx->argument(0).asFunctionObject(); + if (!callback) + ctx->throwTypeError(); + + if (len == 0) { + if (ctx->argumentCount == 1) + ctx->throwTypeError(); + return ctx->argument(1); + } + + uint k = len; + Value acc; + if (ctx->argumentCount > 1) { + acc = ctx->argument(1); + } else { + bool kPresent = false; + while (k > 0 && !kPresent) { + Value v = instance->getIndexed(k - 1, &kPresent); + if (kPresent) + acc = v; + --k; + } + if (!kPresent) + ctx->throwTypeError(); + } + + while (k > 0) { + bool kPresent; + Value v = instance->getIndexed(k - 1, &kPresent); + if (kPresent) { + Value args[4]; + args[0] = acc; + args[1] = v; + args[2] = Value::fromDouble(k - 1); + args[3] = ctx->thisObject; + acc = callback->call(Value::undefinedValue(), args, 4); + } + --k; + } + return acc; +} + diff --git a/src/qml/qml/v4/qv4arrayobject_p.h b/src/qml/qml/v4/qv4arrayobject_p.h new file mode 100644 index 0000000000..13c3882f4f --- /dev/null +++ b/src/qml/qml/v4/qv4arrayobject_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ARRAYOBJECT_H +#define QV4ARRAYOBJECT_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ArrayCtor: FunctionObject +{ + ArrayCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct ArrayPrototype: ArrayObject +{ + ArrayPrototype(ExecutionContext *context); + + void init(ExecutionContext *ctx, const Value &ctor); + + static uint getLength(ExecutionContext *ctx, Object *o); + + static Value method_isArray(SimpleCallContext *ctx); + static Value method_toString(SimpleCallContext *ctx); + static Value method_toLocaleString(SimpleCallContext *ctx); + static Value method_concat(SimpleCallContext *ctx); + static Value method_join(SimpleCallContext *ctx); + static Value method_pop(SimpleCallContext *ctx); + static Value method_push(SimpleCallContext *ctx); + static Value method_reverse(SimpleCallContext *ctx); + static Value method_shift(SimpleCallContext *ctx); + static Value method_slice(SimpleCallContext *ctx); + static Value method_sort(SimpleCallContext *ctx); + static Value method_splice(SimpleCallContext *ctx); + static Value method_unshift(SimpleCallContext *ctx); + static Value method_indexOf(SimpleCallContext *ctx); + static Value method_lastIndexOf(SimpleCallContext *ctx); + static Value method_every(SimpleCallContext *ctx); + static Value method_some(SimpleCallContext *ctx); + static Value method_forEach(SimpleCallContext *ctx); + static Value method_map(SimpleCallContext *ctx); + static Value method_filter(SimpleCallContext *ctx); + static Value method_reduce(SimpleCallContext *ctx); + static Value method_reduceRight(SimpleCallContext *ctx); +}; + + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4/qv4bindings.cpp b/src/qml/qml/v4/qv4bindings.cpp deleted file mode 100644 index 668f7b4d05..0000000000 --- a/src/qml/qml/v4/qv4bindings.cpp +++ /dev/null @@ -1,2478 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// #define REGISTER_CLEANUP_DEBUG - -#include "qv4bindings_p.h" -#include "qv4program_p.h" -#include "qv4compiler_p.h" -#include "qv4compiler_p_p.h" - -#include <private/qqmlglobal_p.h> - -#include <private/qv8_p.h> -#include <private/qjsconverter_p.h> -#include <private/qjsconverter_impl_p.h> -#include <private/qjsvalue_impl_p.h> -#include <private/qjsvalueiterator_impl_p.h> -#include <private/qv8engine_impl_p.h> - -#include <private/qqmlaccessors_p.h> -#include <private/qqmlprofilerservice_p.h> -#include <private/qqmlmetatype_p.h> -#include <private/qqmltrace_p.h> -#include <private/qqmlstringconverters_p.h> -#include <private/qqmlproperty_p.h> -#include <private/qqmlvmemetaobject_p.h> - -#include <QtQml/qqmlinfo.h> -#include <QtCore/qnumeric.h> -#include <QtCore/qmath.h> -#include <math.h> // ::fmod - -#ifdef Q_CC_MSVC -// MSVC2010 warns about 'unreferenced formal parameter', even if it's used in p->~T() -# pragma warning( disable : 4100 ) -#endif - -QT_BEGIN_NAMESPACE - -using namespace QQmlJS; - -QQmlAbstractBinding::VTable QV4Bindings_Binding_vtable = { - QV4Bindings::Binding::destroy, - QQmlAbstractBinding::default_expression, - QV4Bindings::Binding::propertyIndex, - QV4Bindings::Binding::object, - QV4Bindings::Binding::setEnabled, - QV4Bindings::Binding::update, - QV4Bindings::Binding::retargetBinding -}; - -namespace { - -// The highest bit is the sign bit, in any endianness -static const quint64 doubleSignMask = (quint64(0x1) << 63); - -inline bool signBitSet(const double &v) -{ - union { double d; quint64 u; } u; - u.d = v; - return (u.u & doubleSignMask); -} - -inline double setSignBit(const double &v, bool b = true) -{ - union { double d; quint64 u; } u; - - u.d = v; - if (b) { - u.u |= doubleSignMask; - } else { - u.u &= ~doubleSignMask; - } - return u.d; -} - -inline double clearSignBit(const double &v, bool b = true) -{ - return setSignBit(v, !b); -} - -struct Register { - typedef QQmlRegisterType Type; - - enum SpecialNumericValue { - NegativeZero = 1, - PositiveInfinity = 2, - NegativeInfinity = 3, - NotANumber = 4 - }; - - inline void setUndefined() { dataType = UndefinedType; } - inline bool isUndefined() const { return dataType == UndefinedType; } - - inline void setNull() { dataType = NullType; } - - inline void setNaN() { setnumber(qSNaN()); } - inline void setNaNType() { dataType = SpecialNumericType; intValue = NotANumber; } // non-numeric representation of NaN - inline bool isNaN() const { return (((dataType == SpecialNumericType) && (intValue == NotANumber)) || - ((dataType == NumberType) && qIsNaN(numberValue))); } - - inline void setInf(bool negative) { setnumber(setSignBit(qInf(), negative)); } - inline void setInfType(bool negative) { dataType = SpecialNumericType; intValue = (negative ? NegativeInfinity : PositiveInfinity); } // non-numeric representation of Inf - inline bool isInf() const { return (((dataType == SpecialNumericType) && ((intValue == NegativeInfinity) || (intValue == PositiveInfinity))) || - ((dataType == NumberType) && qIsInf(numberValue))); } - - inline void setNegativeZero() { setnumber(setSignBit(0)); } - inline void setNegativeZeroType() { dataType = SpecialNumericType; intValue = NegativeZero; } // non-numeric representation of -0 - inline bool isNegativeZero() const { return (((dataType == SpecialNumericType) && (intValue == NegativeZero)) || - ((dataType == NumberType) && (numberValue == 0) && signBitSet(numberValue))); } - - inline void setQObject(QObject *o) { qobjectValue = o; dataType = QObjectStarType; } - inline QObject *getQObject() const { return qobjectValue; } - - inline void setnumber(double v) { numberValue = v; dataType = NumberType; } - inline double getnumber() const { return numberValue; } - inline double &getnumberref() { return numberValue; } - - inline void setfloat(float v) { floatValue = v; dataType = FloatType; } - inline float getfloat() const { return floatValue; } - inline float &getfloatref() { return floatValue; } - - inline void setint(int v) { intValue = v; dataType = IntType; } - inline int getint() const { return intValue; } - inline int &getintref() { return intValue; } - - inline void setbool(bool v) { boolValue = v; dataType = BoolType; } - inline bool getbool() const { return boolValue; } - inline bool &getboolref() { return boolValue; } - - inline QVariant *getvariantptr() { return reinterpret_cast<QVariant *>(typeDataPtr()); } - inline QString *getstringptr() { return reinterpret_cast<QString *>(typeDataPtr()); } - inline QUrl *geturlptr() { return reinterpret_cast<QUrl *>(typeDataPtr()); } - inline v8::Handle<v8::Value> *gethandleptr() { return reinterpret_cast<v8::Handle<v8::Value> *>(typeDataPtr()); } - inline QJSValue *getjsvalueptr() { return reinterpret_cast<QJSValue *>(typeDataPtr()); } - - inline const QVariant *getvariantptr() const { return reinterpret_cast<const QVariant *>(typeDataPtr()); } - inline const QString *getstringptr() const { return reinterpret_cast<const QString *>(typeDataPtr()); } - inline const QUrl *geturlptr() const { return reinterpret_cast<const QUrl *>(typeDataPtr()); } - inline const v8::Handle<v8::Value> *gethandleptr() const { return reinterpret_cast<const v8::Handle<v8::Value> *>(typeDataPtr()); } - inline const QJSValue *getjsvalueptr() const { return reinterpret_cast<const QJSValue *>(typeDataPtr()); } - - size_t dataSize() { return sizeof(data); } - inline void *typeDataPtr() { return (void *)&data; } - inline void *typeMemory() { return (void *)data; } - inline const void *typeDataPtr() const { return (void *)&data; } - inline const void *typeMemory() const { return (void *)data; } - - inline Type gettype() const { return dataType; } - inline void settype(Type t) { dataType = t; } - - Type dataType; // Type of data - union { - QObject *qobjectValue; - double numberValue; - float floatValue; - int intValue; - bool boolValue; - void *data[sizeof(QVariant)]; - qint64 q_for_alignment_1; - double q_for_alignment_2; - }; - - inline void cleanup(); - inline void cleanupString(); - inline void cleanupUrl(); - inline void cleanupColor(); - inline void cleanupVariant(); - inline void cleanupHandle(); - inline void cleanupJSValue(); - - inline void copy(const Register &other); - inline void init(Type type); -#ifdef REGISTER_CLEANUP_DEBUG - Register() { - type = 0; - } - - ~Register() { - if (dataType >= FirstCleanupType) - qWarning("Register leaked of type %d", dataType); - } -#endif - - template <typename T> - inline static void copyConstructPointee(T *p, const T *other) - { - new (p) T(*other); - } - - template <typename T> - inline static void defaultConstructPointee(T *p) - { - new (p) T(); - } - - template <typename T> - inline static void destroyPointee(T *p) - { - p->~T(); - } -}; - -void Register::cleanup() -{ - if (dataType >= FirstCleanupType) { - if (dataType == QStringType) { - destroyPointee(getstringptr()); - } else if (dataType == QUrlType) { - destroyPointee(geturlptr()); - } else if (dataType == QColorType) { - QQml_valueTypeProvider()->destroyValueType(QMetaType::QColor, typeDataPtr(), dataSize()); - } else if (dataType == QVariantType) { - destroyPointee(getvariantptr()); - } else if (dataType == qMetaTypeId<v8::Handle<v8::Value> >()) { - destroyPointee(gethandleptr()); - } else if (dataType == qMetaTypeId<QJSValue>()) { - destroyPointee(getjsvalueptr()); - } - } - setUndefined(); -} - -void Register::cleanupString() -{ - destroyPointee(getstringptr()); - setUndefined(); -} - -void Register::cleanupUrl() -{ - destroyPointee(geturlptr()); - setUndefined(); -} - -void Register::cleanupColor() -{ - QQml_valueTypeProvider()->destroyValueType(QMetaType::QColor, typeDataPtr(), dataSize()); - setUndefined(); -} - -void Register::cleanupVariant() -{ - destroyPointee(getvariantptr()); - setUndefined(); -} - -void Register::cleanupHandle() -{ - destroyPointee(gethandleptr()); - setUndefined(); -} - -void Register::cleanupJSValue() -{ - destroyPointee(getjsvalueptr()); - setUndefined(); -} - -void Register::copy(const Register &other) -{ - *this = other; - if (other.dataType >= FirstCleanupType) { - if (other.dataType == QStringType) - copyConstructPointee(getstringptr(), other.getstringptr()); - else if (other.dataType == QUrlType) - copyConstructPointee(geturlptr(), other.geturlptr()); - else if (other.dataType == QColorType) - QQml_valueTypeProvider()->copyValueType(QMetaType::QColor, other.typeDataPtr(), typeDataPtr(), dataSize()); - else if (other.dataType == QVariantType) - copyConstructPointee(getvariantptr(), other.getvariantptr()); - else if (other.dataType == qMetaTypeId<v8::Handle<v8::Value> >()) - copyConstructPointee(gethandleptr(), other.gethandleptr()); - else if (other.dataType == qMetaTypeId<QJSValue>()) - copyConstructPointee(getjsvalueptr(), other.getjsvalueptr()); - } -} - -void Register::init(Type type) -{ - dataType = type; - if (dataType >= FirstCleanupType) { - if (dataType == QStringType) - defaultConstructPointee(getstringptr()); - else if (dataType == QUrlType) - defaultConstructPointee(geturlptr()); - else if (dataType == QColorType) - QQml_valueTypeProvider()->initValueType(QMetaType::QColor, typeDataPtr(), dataSize()); - else if (dataType == QVariantType) - defaultConstructPointee(getvariantptr()); - else if (dataType == qMetaTypeId<v8::Handle<v8::Value> >()) - defaultConstructPointee(gethandleptr()); - else if (dataType == qMetaTypeId<QJSValue>()) - defaultConstructPointee(getjsvalueptr()); - } -} - -} // end of anonymous namespace - -QV4Bindings::QV4Bindings(const char *programData, QQmlContextData *context) -: subscriptions(0), program(0), bindings(0) -{ - program = (QV4Program *)programData; - if (program) { - subscriptions = new Subscription[program->subscriptions]; - bindings = new Binding[program->bindings]; - - QQmlAbstractExpression::setContext(context); - } -} - -QV4Bindings::~QV4Bindings() -{ - delete [] bindings; bindings = 0; - delete [] subscriptions; subscriptions = 0; -} - -QQmlAbstractBinding *QV4Bindings::configBinding(QObject *target, QObject *scope, - const QQmlInstruction::instr_assignV4Binding *i) -{ - Binding *rv = bindings + i->value; - - rv->instruction = i; - rv->target = target; - rv->scope = scope; - rv->parent = this; - - addref(); // This is decremented in Binding::destroy() - - return rv; -} - -void QV4Bindings::Binding::setEnabled(QQmlAbstractBinding *_This, - bool e, QQmlPropertyPrivate::WriteFlags flags) -{ - QV4Bindings::Binding *This = static_cast<QV4Bindings::Binding *>(_This); - - if (This->enabledFlag() != e) { - This->setEnabledFlag(e); - - if (e) update(_This, flags); - } -} - -void QV4Bindings::Binding::update(QQmlAbstractBinding *_This, QQmlPropertyPrivate::WriteFlags flags) -{ - QV4Bindings::Binding *This = static_cast<QV4Bindings::Binding *>(_This); - This->parent->run(This, flags); -} - -void QV4Bindings::Binding::destroy(QQmlAbstractBinding *_This, QQmlAbstractBinding::DestroyMode mode) -{ - QV4Bindings::Binding *This = static_cast<QV4Bindings::Binding *>(_This); - - if (mode == QQmlAbstractBinding::DisconnectBinding) - This->disconnect(); - - This->setEnabledFlag(false); - This->removeFromObject(); - This->clear(); - This->removeError(); - This->parent->release(); -} - -int QV4Bindings::Binding::propertyIndex(const QQmlAbstractBinding *_This) -{ - const QV4Bindings::Binding *This = static_cast<const QV4Bindings::Binding *>(_This); - - if (This->target.hasValue()) return This->target.constValue()->targetProperty; - else return This->instruction->property; -} - -QObject *QV4Bindings::Binding::object(const QQmlAbstractBinding *_This) -{ - const QV4Bindings::Binding *This = static_cast<const QV4Bindings::Binding *>(_This); - - if (This->target.hasValue()) return This->target.constValue()->target; - return *This->target; -} - -void QV4Bindings::Binding::retargetBinding(QQmlAbstractBinding *_This, QObject *t, int i) -{ - QV4Bindings::Binding *This = static_cast<QV4Bindings::Binding *>(_This); - - This->target.value().target = t; - This->target.value().targetProperty = i; -} - -void QV4Bindings::Binding::disconnect() -{ - // We iterate over the signal table to find all subscriptions associated with this binding. - // This is slow, but disconnect() is not called in the common case, only in special cases - // like when the binding is overwritten. - QV4Program * const program = parent->program; - for (quint16 subIndex = 0; subIndex < program->subscriptions; subIndex++) { - QV4Program::BindingReferenceList * const list = program->signalTable(subIndex); - for (quint32 bindingIndex = 0; bindingIndex < list->count; ++bindingIndex) { - QV4Program::BindingReference * const bindingRef = list->bindings + bindingIndex; - Binding * const binding = parent->bindings + bindingRef->binding; - if (binding == this) { - Subscription * const sub = parent->subscriptions + subIndex; - if (sub->active()) { - sub->setActive(false); - sub->disconnect(); - } - } - } - } -} - -void QV4Bindings::Binding::dump() -{ - qWarning() << parent->context()->url << instruction->line << instruction->column; -} - -QV4Bindings::Subscription::Subscription() - : m_bindings(0) -{ - setCallback(QQmlNotifierEndpoint::QV4BindingsSubscription); -} - -int QV4Bindings::Subscription::method() const -{ - Q_ASSERT(bindings() != 0); - return (this - bindings()->subscriptions); -} - -void QV4Bindings::Subscription::setBindings(QV4Bindings *bindings) -{ - m_bindings = bindings; -} - -QV4Bindings *QV4Bindings::Subscription::bindings() const -{ - return *m_bindings; -} - -bool QV4Bindings::Subscription::active() const -{ - return m_bindings.flag(); -} - -void QV4Bindings::Subscription::setActive(bool active) -{ - m_bindings.setFlagValue(active); -} - -void QV4BindingsSubscription_callback(QQmlNotifierEndpoint *e, void **) -{ - QV4Bindings::Subscription *s = static_cast<QV4Bindings::Subscription *>(e); - Q_ASSERT(s->bindings()); - s->bindings()->subscriptionNotify(s->method()); -} - -void QV4Bindings::subscriptionNotify(int id) -{ - QV4Program::BindingReferenceList *list = program->signalTable(id); - - for (quint32 ii = 0; ii < list->count; ++ii) { - QV4Program::BindingReference *bindingRef = list->bindings + ii; - - Binding *binding = bindings + bindingRef->binding; - - if (binding->executedBlocks & bindingRef->blockMask) { - run(binding, QQmlPropertyPrivate::DontRemoveBinding); - } - } -} - -void QV4Bindings::run(Binding *binding, QQmlPropertyPrivate::WriteFlags flags) -{ - if (!binding->enabledFlag()) - return; - - QQmlContextData *context = QQmlAbstractExpression::context(); - if (!context || !context->isValid()) - return; - - // Check that the target has not been deleted - if (QQmlData::wasDeleted(*binding->target)) - return; - - QQmlTrace trace("V4 Binding Update"); - trace.addDetail("URL", context->url); - trace.addDetail("Line", binding->instruction->line); - trace.addDetail("Column", binding->instruction->column); - - QQmlBindingProfiler prof(context->urlString, binding->instruction->line, binding->instruction->column, QQmlProfilerService::V4Binding); - - const int propType = binding->instruction->propType; - const int property = binding->instruction->property; - - if (binding->updatingFlag()) { - QString name; - if (propType) { - QQmlValueType *vt = QQmlValueTypeFactory::valueType(propType); - Q_ASSERT(vt); - - name = QLatin1String(binding->target->metaObject()->property(property & 0x0000FFFF).name()); - name.append(QLatin1Char('.')); - name.append(QLatin1String(vt->metaObject()->property(property >> 16).name())); - } else { - name = QLatin1String(binding->target->metaObject()->property(property).name()); - } - qmlInfo(*binding->target) << tr("Binding loop detected for property \"%1\"").arg(name); - return; - } - - int index = binding->instruction->value; - int fallbackIndex = binding->instruction->fallbackValue; - - bool invalidated = false; - bool *inv = (fallbackIndex != -1) ? &invalidated : 0; - - binding->setUpdatingFlag(true); - if (propType) { - QQmlValueType *vt = QQmlValueTypeFactory::valueType(propType); - Q_ASSERT(vt); - vt->read(*binding->target, property & 0x0000FFFF); - - QObject *target = vt; - run(index, binding->executedBlocks, context, binding, binding->scope, target, flags, inv); - - if (!invalidated) { - vt->write(*binding->target, property & 0x0000FFFF, flags); - } - } else { - QQmlData *data = QQmlData::get(*binding->target); - QQmlPropertyData *propertyData = (data && data->propertyCache ? data->propertyCache->property(property) : 0); - - if (propertyData && propertyData->isVarProperty()) { - // We will allocate a V8 handle in this conversion/store - v8::HandleScope handle_scope; - v8::Context::Scope context_scope(QQmlEnginePrivate::get(context->engine)->v8engine()->context()); - - run(index, binding->executedBlocks, context, binding, binding->scope, *binding->target, flags, inv); - } else { - run(index, binding->executedBlocks, context, binding, binding->scope, *binding->target, flags, inv); - } - } - binding->setUpdatingFlag(false); - - if (invalidated) { - // This binding is no longer valid - fallback to V8 - Q_ASSERT(fallbackIndex > -1); - QQmlAbstractBinding *b = QQmlPropertyPrivate::activateSharedBinding(context, fallbackIndex, flags); - Q_ASSERT(b == binding); - b->destroy(); - } -} - -void QV4Bindings::subscribeId(QQmlContextData *p, int idIndex, int subIndex) -{ - Subscription *sub = (subscriptions + subIndex); - sub->disconnect(); - - if (p->idValues[idIndex]) { - sub->setBindings(this); - sub->connect(&p->idValues[idIndex].bindings); - sub->setActive(true); - } else { - sub->setActive(false); - } -} - -void QV4Bindings::subscribe(QObject *o, int notifyIndex, int subIndex, QQmlEngine *e) -{ - Subscription *sub = (subscriptions + subIndex); - if (sub->isConnected(o, notifyIndex)) - return; - sub->setBindings(this); - if (o) { - sub->connect(o, notifyIndex, e); - sub->setActive(true); - } else { - sub->disconnect(); - sub->setActive(false); - } -} - -static bool testCompareVariants(const QVariant &qtscriptRaw, const QVariant &v4) -{ - QVariant qtscript = qtscriptRaw; - - if (qtscript.userType() == v4.userType()) { - } else if (qtscript.canConvert(v4.userType())) { - qtscript.convert(v4.userType()); - } else if (qtscript.userType() == QVariant::Invalid && v4.userType() == QMetaType::QObjectStar) { - qtscript = qVariantFromValue<QObject *>(0); - } else { - return false; - } - - int type = qtscript.userType(); - - if (type == QQmlMetaType::QQuickAnchorLineMetaTypeId()) { - return QQmlMetaType::QQuickAnchorLineCompare(qtscript.constData(), v4.constData()); - } else if (type == QMetaType::Double) { - - double la = qvariant_cast<double>(qtscript); - double lr = qvariant_cast<double>(v4); - - return la == lr || (qIsNaN(la) && qIsNaN(lr)); - - } else if (type == QMetaType::Float) { - - float la = qvariant_cast<float>(qtscript); - float lr = qvariant_cast<float>(v4); - - return la == lr || (qIsNaN(la) && qIsNaN(lr)); - - } else { - return qtscript == v4; - } -} - -QByteArray testResultToString(const QVariant &result, bool undefined) -{ - if (undefined) { - return "undefined"; - } else { - QString rv; - QDebug d(&rv); - d << result; - return rv.toUtf8(); - } -} - -static void testBindingResult(const QString &binding, quint16 line, quint16 column, - QQmlContextData *context, QObject *scope, - const Register &result, int resultType) -{ - QQmlExpression expression(context->asQQmlContext(), scope, binding); - bool isUndefined = false; - QVariant value = expression.evaluate(&isUndefined); - - bool iserror = false; - QByteArray qtscriptResult; - QByteArray v4Result; - - const int handleType = qMetaTypeId<v8::Handle<v8::Value> >(); - - if (expression.hasError()) { - iserror = true; - qtscriptResult = "exception"; - } else if ((value.userType() != resultType) && - (resultType != QMetaType::QVariant) && - (resultType != qMetaTypeId<QJSValue>()) && - (resultType != handleType)) { - // Override the QMetaType conversions to make them more JS friendly. - if (value.userType() == QMetaType::Double && (resultType == QMetaType::QString || - resultType == QMetaType::QUrl)) { - // number to string-like conversion. - value = QVariant::fromValue<QString>(QString::number(value.toDouble(), 'g', 16)); - } else if (value.userType() == QMetaType::QUrl && resultType == QMetaType::Bool) { - // url to bool conversion - value = QVariant::fromValue<bool>(!value.toUrl().isEmpty()); - } - - if (!value.isNull() && !value.convert(resultType)) { - iserror = true; - qtscriptResult = "exception"; - } else if (resultType == QMetaType::QUrl) { - // a V8 value was converted to QUrl. - value = QVariant::fromValue<QUrl>(context->resolvedUrl(value.toUrl())); - } - } - - if (! iserror) - qtscriptResult = testResultToString(value, isUndefined); - - if (isUndefined && result.isUndefined()) { - return; - } else if(isUndefined != result.isUndefined()) { - iserror = true; - } - - QVariant v4value; - if (!result.isUndefined()) { - switch (resultType) { - case QMetaType::QString: - v4value = *result.getstringptr(); - break; - case QMetaType::QUrl: - v4value = *result.geturlptr(); - break; - case QMetaType::QObjectStar: - v4value = qVariantFromValue<QObject *>(result.getQObject()); - break; - case QMetaType::Bool: - v4value = result.getbool(); - break; - case QMetaType::Int: - v4value = result.getint(); - break; - case QMetaType::Double: - v4value = result.getnumber(); - break; - case QMetaType::QColor: - v4value = QVariant(QMetaType::QColor, result.typeDataPtr()); - break; - case QMetaType::QVariant: - v4value = *result.getvariantptr(); - break; - default: - if (resultType == QQmlMetaType::QQuickAnchorLineMetaTypeId()) { - v4value = QVariant(QQmlMetaType::QQuickAnchorLineMetaTypeId(), result.typeDataPtr()); - } else if (resultType == qMetaTypeId<QJSValue>()) { - v4value = result.getjsvalueptr()->toVariant(); - } else if (resultType == handleType) { - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); - v4value = ep->v8engine()->toVariant(*result.gethandleptr(), resultType); - } else { - iserror = true; - v4Result = "Unknown V4 type"; - } - } - } - if (v4Result.isEmpty()) - v4Result = testResultToString(v4value, result.isUndefined()); - - if (!testCompareVariants(value, v4value)) - iserror = true; - - if (iserror) { - qWarning().nospace() << "QV4: Optimization error @" << context->url.toString().toUtf8().constData() << ':' << line << ':' << column; - - qWarning().nospace() << " Binding: " << binding; - qWarning().nospace() << " QtScript: " << qtscriptResult.constData(); - qWarning().nospace() << " V4: " << v4Result.constData(); - } -} - -static void testBindingException(const QString &binding, quint16 line, quint16 column, - QQmlContextData *context, QObject *scope) -{ - QQmlExpression expression(context->asQQmlContext(), scope, binding); - bool isUndefined = false; - QVariant value = expression.evaluate(&isUndefined); - - if (!expression.hasError()) { - QByteArray qtscriptResult = testResultToString(value, isUndefined); - qWarning().nospace() << "QV4: Optimization error @" << context->url.toString().toUtf8().constData() << ':' << line << ':' << column; - qWarning().nospace() << " Binding: " << binding; - qWarning().nospace() << " QtScript: " << qtscriptResult.constData(); - qWarning().nospace() << " V4: exception"; - } -} - -static void throwException(int id, QQmlDelayedError *error, - QV4Program *program, QQmlContextData *context, - const QString &description = QString()) -{ - if (description.isEmpty()) - error->setErrorDescription(QLatin1String("TypeError: Result of expression is not an object")); - else - error->setErrorDescription(description); - error->setErrorObject(context->contextObject); - if (id != 0xFF) { - quint32 e = *((quint32 *)(program->data() + program->exceptionDataOffset) + id); - error->setErrorLocation(context->url, (e >> 16), (e & 0xFFFF)); - } else { - error->setErrorLocation(context->url, -1, -1); - } - if (!context->engine || !error->addError(QQmlEnginePrivate::get(context->engine))) - QQmlEnginePrivate::warning(context->engine, error); -} - -const double QV4Bindings::D32 = 4294967296.0; - -qint32 QV4Bindings::toInt32(double n) -{ - if (qIsNaN(n) || qIsInf(n) || (n == 0)) - return 0; - - double sign = (n < 0) ? -1.0 : 1.0; - double abs_n = fabs(n); - - n = ::fmod(sign * ::floor(abs_n), D32); - const double D31 = D32 / 2.0; - - if (sign == -1 && n < -D31) - n += D32; - - else if (sign != -1 && n >= D31) - n -= D32; - - return qint32 (n); -} - -inline quint32 QV4Bindings::toUint32(double n) -{ - if (qIsNaN(n) || qIsInf(n) || (n == 0)) - return 0; - - double sign = (n < 0) ? -1.0 : 1.0; - double abs_n = fabs(n); - - n = ::fmod(sign * ::floor(abs_n), D32); - - if (n < 0) - n += D32; - - return quint32 (n); -} - -#define THROW_EXCEPTION_STR(id, str) { \ - if (testBinding) testBindingException(*testBindingSource, bindingLine, bindingColumn, context, scope); \ - throwException((id), error, program, context, (str)); \ - goto exceptionExit; \ -} - -#define THROW_VALUE_EXCEPTION_STR(id, str) { \ - throwException((id), error, program, context, (str)); \ - goto exceptionExit; \ -} - -#define THROW_EXCEPTION(id) THROW_EXCEPTION_STR(id, QString()) - -#define MARK_REGISTER(reg) cleanupRegisterMask |= (1 << (reg)) -#define MARK_CLEAN_REGISTER(reg) cleanupRegisterMask &= ~(1 << (reg)) - -#define STRING_REGISTER(reg) { \ - registers[(reg)].settype(QStringType); \ - MARK_REGISTER(reg); \ -} - -#define URL_REGISTER(reg) { \ - registers[(reg)].settype(QUrlType); \ - MARK_REGISTER(reg); \ -} - -#define COLOR_REGISTER(reg) { \ - registers[(reg)].settype(QColorType); \ - MARK_REGISTER(reg); \ -} - -#define VARIANT_REGISTER(reg) { \ - registers[(reg)].settype(QVariantType); \ - MARK_REGISTER(reg); \ -} - -#define V8HANDLE_REGISTER(reg) { \ - registers[(reg)].settype(V8HandleType); \ - MARK_REGISTER(reg); \ -} - -#define JSVALUE_REGISTER(reg) { \ - registers[(reg)].settype(QJSValueType); \ - MARK_REGISTER(reg); \ -} - -namespace { - -bool bindingInvalidated(bool *invalidated, QObject *obj, QQmlContextData *context, int index) -{ - if (invalidated != 0) { - if (QQmlData *data = QQmlData::get(obj, true)) { - if (!data->propertyCache) { - data->propertyCache = QQmlEnginePrivate::get(context->engine)->cache(obj); - if (data->propertyCache) data->propertyCache->addref(); - } - - if (QQmlPropertyData *prop = data->propertyCache ? data->propertyCache->property(index) : 0) { - if (prop->isOverridden()) { - // TODO: avoid construction of name and name-based lookup - int resolvedIndex = data->propertyCache->property(prop->name(obj), obj, context)->coreIndex; - if (index < resolvedIndex) { - *invalidated = true; - return true; - } - } - } - } - } - - return false; -} - -} - -#ifdef QML_THREADED_INTERPRETER -void **QV4Bindings::getDecodeInstrTable() -{ - static void **decode_instr; - if (!decode_instr) { - QV4Bindings *dummy = new QV4Bindings(0, 0); - quint32 executedBlocks = 0; - dummy->run(0, executedBlocks, 0, 0, 0, 0, - QQmlPropertyPrivate::BypassInterceptor, - 0, &decode_instr); - dummy->release(); - } - return decode_instr; -} -#endif - -void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, - QQmlContextData *context, QQmlDelayedError *error, - QObject *scope, QObject *output, - QQmlPropertyPrivate::WriteFlags storeFlags, - bool *invalidated -#ifdef QML_THREADED_INTERPRETER - ,void ***table -#endif - ) -{ -#ifdef QML_THREADED_INTERPRETER - if (table) { - static void *decode_instr[] = { - FOR_EACH_V4_INSTR(QML_V4_INSTR_ADDR) - }; - - *table = decode_instr; - return; - } -#endif - - - error->removeError(); - - Register registers[32]; - quint32 cleanupRegisterMask = 0; - - executedBlocks = 0; - - const char *code = program->instructions(); - code += instrIndex * QML_V4_INSTR_SIZE(Jump, jump); - const V4Instr *instr = reinterpret_cast<const V4Instr *>(code); - - const char *data = program->data(); - - QString *testBindingSource = 0; - bool testBinding = false; - int bindingLine = 0; - int bindingColumn = 0; - -#ifdef QML_THREADED_INTERPRETER - goto *instr->common.code; -#else - for (;;) { - switch (instr->common.type) { -#endif - - QML_V4_BEGIN_INSTR(Noop, common) - QML_V4_END_INSTR(Noop, common) - - QML_V4_BEGIN_INSTR(BindingId, id) - bindingLine = instr->id.line; - bindingColumn = instr->id.column; - QML_V4_END_INSTR(BindingId, id) - - QML_V4_BEGIN_INSTR(SubscribeId, subscribeop) - subscribeId(context, instr->subscribeop.index, instr->subscribeop.offset); - QML_V4_END_INSTR(SubscribeId, subscribeop) - - QML_V4_BEGIN_INSTR(FetchAndSubscribe, fetchAndSubscribe) - { - Register ® = registers[instr->fetchAndSubscribe.reg]; - - if (reg.isUndefined()) - THROW_EXCEPTION(instr->fetchAndSubscribe.exceptionId); - - QObject *object = reg.getQObject(); - if (!object) { - THROW_EXCEPTION(instr->fetchAndSubscribe.exceptionId); - } else { - if (bindingInvalidated(invalidated, object, context, instr->fetchAndSubscribe.property.coreIndex)) - goto programExit; - - const Register::Type valueType = (Register::Type)instr->fetchAndSubscribe.valueType; - reg.init(valueType); - if (instr->fetchAndSubscribe.valueType >= FirstCleanupType) - MARK_REGISTER(instr->fetchAndSubscribe.reg); - - QQmlData::flushPendingBinding(object, instr->fetchAndSubscribe.property.coreIndex); - - QQmlAccessors *accessors = instr->fetchAndSubscribe.property.accessors; - accessors->read(object, instr->fetchAndSubscribe.property.accessorData, - reg.typeDataPtr()); - - if (valueType == FloatType) { - // promote floats - const double v = reg.getfloat(); - reg.setnumber(v); - } - - if (accessors->notifier) { - QQmlNotifier *notifier = 0; - accessors->notifier(object, instr->fetchAndSubscribe.property.accessorData, ¬ifier); - if (notifier) { - int subIdx = instr->fetchAndSubscribe.subscription; - Subscription *sub = 0; - if (subIdx != -1) { - sub = (subscriptions + subIdx); - sub->setBindings(this); - } - sub->connect(notifier); - } - } else { - const int notifyIndex = instr->fetchAndSubscribe.property.notifyIndex; - if (notifyIndex != -1) { - const int subIdx = instr->fetchAndSubscribe.subscription; - subscribe(object, notifyIndex, subIdx, context->engine); - } - } - } - } - QML_V4_END_INSTR(FetchAndSubscribe, fetchAndSubscribe) - - QML_V4_BEGIN_INSTR(LoadId, load) - registers[instr->load.reg].setQObject(context->idValues[instr->load.index].data()); - QML_V4_END_INSTR(LoadId, load) - - QML_V4_BEGIN_INSTR(LoadScope, load) - registers[instr->load.reg].setQObject(scope); - QML_V4_END_INSTR(LoadScope, load) - - QML_V4_BEGIN_INSTR(LoadRoot, load) - registers[instr->load.reg].setQObject(context->contextObject); - QML_V4_END_INSTR(LoadRoot, load) - - QML_V4_BEGIN_INSTR(LoadSingletonObject, load) - { - Register ® = registers[instr->load.reg]; - - const QString *name = reg.getstringptr(); - QQmlTypeNameCache::Result r = context->imports->query(*name); - reg.cleanupString(); - - if (r.isValid() && r.type) { - if (r.type->isSingleton()) { - QQmlEngine *e = context->engine; - QQmlType::SingletonInstanceInfo *siinfo = r.type->singletonInstanceInfo(); - siinfo->init(e); // note: this will also create QJSValue singleton, which is not strictly required here. - QObject *qobjectSingleton = siinfo->qobjectApi(e); - if (qobjectSingleton) - reg.setQObject(qobjectSingleton); - } - } - } - QML_V4_END_INSTR(LoadSingletonObject, load) - - QML_V4_BEGIN_INSTR(LoadAttached, attached) - { - const Register &input = registers[instr->attached.reg]; - Register &output = registers[instr->attached.output]; - if (input.isUndefined()) - THROW_EXCEPTION(instr->attached.exceptionId); - - QObject *object = registers[instr->attached.reg].getQObject(); - if (!object) { - output.setUndefined(); - } else { - QObject *attached = qmlAttachedPropertiesObjectById(instr->attached.id, input.getQObject(), true); - Q_ASSERT(attached); - output.setQObject(attached); - } - } - QML_V4_END_INSTR(LoadAttached, attached) - - QML_V4_BEGIN_INSTR(UnaryNot, unaryop) - { - registers[instr->unaryop.output].setbool(!registers[instr->unaryop.src].getbool()); - } - QML_V4_END_INSTR(UnaryNot, unaryop) - - QML_V4_BEGIN_INSTR(UnaryMinusNumber, unaryop) - { - registers[instr->unaryop.output].setnumber(-registers[instr->unaryop.src].getnumber()); - } - QML_V4_END_INSTR(UnaryMinusNumber, unaryop) - - QML_V4_BEGIN_INSTR(UnaryMinusInt, unaryop) - { - registers[instr->unaryop.output].setint(-registers[instr->unaryop.src].getint()); - } - QML_V4_END_INSTR(UnaryMinusInt, unaryop) - - QML_V4_BEGIN_INSTR(UnaryPlusNumber, unaryop) - { - registers[instr->unaryop.output].setnumber(+registers[instr->unaryop.src].getnumber()); - } - QML_V4_END_INSTR(UnaryPlusNumber, unaryop) - - QML_V4_BEGIN_INSTR(UnaryPlusInt, unaryop) - { - registers[instr->unaryop.output].setint(+registers[instr->unaryop.src].getint()); - } - QML_V4_END_INSTR(UnaryPlusInt, unaryop) - - QML_V4_BEGIN_INSTR(ConvertBoolToInt, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) output.setUndefined(); - else output.setint(src.getbool()); - } - QML_V4_END_INSTR(ConvertBoolToInt, unaryop) - - QML_V4_BEGIN_INSTR(ConvertBoolToJSValue, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - new (output.getjsvalueptr()) QJSValue(src.getbool()); - JSVALUE_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertBoolToJSValue, unaryop) - - QML_V4_BEGIN_INSTR(ConvertBoolToNumber, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) output.setUndefined(); - else output.setnumber(src.getbool()); - } - QML_V4_END_INSTR(ConvertBoolToNumber, unaryop) - - QML_V4_BEGIN_INSTR(ConvertBoolToString, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - new (output.getstringptr()) QString(QLatin1String(src.getbool() ? "true" : "false")); - STRING_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertBoolToString, unaryop) - - QML_V4_BEGIN_INSTR(ConvertBoolToVariant, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - new (output.getvariantptr()) QVariant(src.getbool()); - VARIANT_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertBoolToVariant, unaryop) - - QML_V4_BEGIN_INSTR(ConvertBoolToVar, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - new (output.gethandleptr()) v8::Handle<v8::Value>(v8::Boolean::New(src.getbool())); - V8HANDLE_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertBoolToVar, unaryop) - - QML_V4_BEGIN_INSTR(ConvertIntToBool, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) output.setUndefined(); - else output.setbool(src.getint()); - } - QML_V4_END_INSTR(ConvertIntToBool, unaryop) - - QML_V4_BEGIN_INSTR(ConvertIntToJSValue, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - new (output.getjsvalueptr()) QJSValue(src.getint()); - JSVALUE_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertIntToJSValue, unaryop) - - QML_V4_BEGIN_INSTR(ConvertIntToNumber, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) output.setUndefined(); - else if (src.isNaN()) output.setNaN(); - else if (src.isInf()) output.setInf(src.getint() == Register::NegativeInfinity); - else if (src.isNegativeZero()) output.setNegativeZero(); - else output.setnumber(double(src.getint())); - } - QML_V4_END_INSTR(ConvertIntToNumber, unaryop) - - QML_V4_BEGIN_INSTR(ConvertIntToString, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - new (output.getstringptr()) QString(QString::number(src.getint())); - STRING_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertIntToString, unaryop) - - QML_V4_BEGIN_INSTR(ConvertIntToVariant, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - new (output.getvariantptr()) QVariant(src.getint()); - VARIANT_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertIntToVariant, unaryop) - - QML_V4_BEGIN_INSTR(ConvertIntToVar, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - new (output.gethandleptr()) v8::Handle<v8::Value>(v8::Integer::New(src.getint())); - V8HANDLE_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertIntToVar, unaryop) - - QML_V4_BEGIN_INSTR(ConvertJSValueToVar, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - QJSValue tmp(*src.getjsvalueptr()); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupJSValue(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - if (tmp.isUndefined()) { - output.setUndefined(); - } else { - QV8Engine *v8engine = QQmlEnginePrivate::get(context->engine)->v8engine(); - new (output.gethandleptr()) v8::Handle<v8::Value>( - QJSValuePrivate::get(tmp)->asV8Value(v8engine)); - V8HANDLE_REGISTER(instr->unaryop.output); - } - } - } - QML_V4_END_INSTR(ConvertJSValueToVar, unaryop) - - QML_V4_BEGIN_INSTR(ConvertNumberToBool, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) output.setUndefined(); - else output.setbool(src.getnumber() != 0); - } - QML_V4_END_INSTR(ConvertNumberToBool, unaryop) - - QML_V4_BEGIN_INSTR(ConvertNumberToInt, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) output.setUndefined(); - else output.setint(toInt32(src.getnumber())); - } - QML_V4_END_INSTR(ConvertNumberToInt, unaryop) - - QML_V4_BEGIN_INSTR(ConvertNumberToJSValue, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - new (output.getjsvalueptr()) QJSValue(src.getnumber()); - JSVALUE_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertNumberToJSValue, unaryop) - - QML_V4_BEGIN_INSTR(ConvertNumberToString, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - // ### NaN - if (src.isUndefined()) { - output.setUndefined(); - } else { - new (output.getstringptr()) QString(QString::number(src.getnumber(), 'g', 16)); - STRING_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertNumberToString, unaryop) - - QML_V4_BEGIN_INSTR(ConvertNumberToVariant, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - new (output.getvariantptr()) QVariant(src.getnumber()); - VARIANT_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertNumberToVariant, unaryop) - - QML_V4_BEGIN_INSTR(ConvertNumberToVar, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - new (output.gethandleptr()) v8::Handle<v8::Value>(v8::Number::New(src.getnumber())); - V8HANDLE_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertNumberToVar, unaryop) - - QML_V4_BEGIN_INSTR(ConvertStringToBool, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - // ### NaN - if (src.isUndefined()) { - output.setUndefined(); - } else { - // Delegate the conversion. This is pretty fast and it doesn't require a QScriptEngine. - // Ideally we should just call the methods in the QScript namespace directly. - QJSValue tmp(*src.getstringptr()); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupString(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - output.setbool(tmp.toBool()); - } - } - QML_V4_END_INSTR(ConvertStringToBool, unaryop) - - QML_V4_BEGIN_INSTR(ConvertStringToInt, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - // ### NaN - if (src.isUndefined()) { - output.setUndefined(); - } else { - // Delegate the conversion. This is pretty fast and it doesn't require a QScriptEngine. - // Ideally we should just call the methods in the QScript namespace directly. - QJSValue tmp(*src.getstringptr()); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupString(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - output.setint(tmp.toInt()); - } - } - QML_V4_END_INSTR(ConvertStringToInt, unaryop) - - QML_V4_BEGIN_INSTR(ConvertStringToJSValue, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - QString tmp(*src.getstringptr()); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupString(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - new (output.getjsvalueptr()) QJSValue(tmp); - JSVALUE_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertStringToJSValue, unaryop) - - QML_V4_BEGIN_INSTR(ConvertStringToNumber, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - // ### NaN - if (src.isUndefined()) { - output.setUndefined(); - } else { - // Delegate the conversion. This is pretty fast and it doesn't require a QScriptEngine. - // Ideally we should just call the methods in the QScript namespace directly. - QJSValue tmp(*src.getstringptr()); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupString(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - output.setnumber(tmp.toNumber()); - } - } - QML_V4_END_INSTR(ConvertStringToNumber, unaryop) - - QML_V4_BEGIN_INSTR(ConvertStringToUrl, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - // ### NaN - if (src.isUndefined()) { - output.setUndefined(); - } else { - QString tmp(*src.getstringptr()); - // Encoded dir-separators defeat QUrl processing - decode them first - tmp.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupString(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - new (output.geturlptr()) QUrl(tmp); - - URL_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertStringToUrl, unaryop) - - QML_V4_BEGIN_INSTR(ConvertStringToColor, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - // ### NaN - if (src.isUndefined()) { - output.setUndefined(); - } else { - const QString tmp(*src.getstringptr()); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupString(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - QQml_valueTypeProvider()->createValueFromString(QMetaType::QColor, tmp, output.typeDataPtr(), output.dataSize()); - - COLOR_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertStringToColor, unaryop) - - QML_V4_BEGIN_INSTR(ConvertStringToVariant, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - const QString tmp(*src.getstringptr()); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupString(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - new (output.getvariantptr()) QVariant(tmp); - - VARIANT_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertStringToVariant, unaryop) - - QML_V4_BEGIN_INSTR(ConvertStringToVar, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - const QString tmp(*src.getstringptr()); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupString(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - new (output.gethandleptr()) v8::Handle<v8::Value>(QJSConverter::toString(tmp)); - V8HANDLE_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertStringToVar, unaryop) - - QML_V4_BEGIN_INSTR(ConvertUrlToBool, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - // ### NaN - if (src.isUndefined()) { - output.setUndefined(); - } else { - const QUrl tmp(*src.geturlptr()); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupUrl(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - output.setbool(!tmp.isEmpty()); - } - } - QML_V4_END_INSTR(ConvertUrlToBool, unaryop) - - QML_V4_BEGIN_INSTR(ConvertUrlToJSValue, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - const QUrl tmp(*src.geturlptr()); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupUrl(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - new (output.getjsvalueptr()) QJSValue(tmp.toString()); - JSVALUE_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertUrlToJSValue, unaryop) - - QML_V4_BEGIN_INSTR(ConvertUrlToString, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - // ### NaN - if (src.isUndefined()) { - output.setUndefined(); - } else { - const QUrl tmp(*src.geturlptr()); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupUrl(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - new (output.getstringptr()) QString(tmp.toString()); - STRING_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertUrlToString, unaryop) - - QML_V4_BEGIN_INSTR(ConvertUrlToVariant, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - // ### NaN - if (src.isUndefined()) { - output.setUndefined(); - } else { - const QUrl tmp(*src.geturlptr()); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupUrl(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - new (output.getvariantptr()) QVariant(tmp); - VARIANT_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertUrlToVariant, unaryop) - - QML_V4_BEGIN_INSTR(ConvertUrlToVar, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - // ### NaN - if (src.isUndefined()) { - output.setUndefined(); - } else { - const QUrl tmp(*src.geturlptr()); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupUrl(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - new (output.gethandleptr()) v8::Handle<v8::Value>(QJSConverter::toString(tmp.toString())); - V8HANDLE_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertUrlToVar, unaryop) - - QML_V4_BEGIN_INSTR(ConvertColorToBool, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - // ### NaN - if (src.isUndefined()) { - output.setUndefined(); - } else { - // for compatibility with color behavior in v8, always true - output.setbool(true); - } - } - QML_V4_END_INSTR(ConvertColorToBool, unaryop) - - QML_V4_BEGIN_INSTR(ConvertColorToJSValue, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - const QVariant tmp(QMetaType::QColor, src.typeDataPtr()); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupColor(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); - QV8Engine *v8engine = ep->v8engine(); - QQmlValueType *vt = QQmlValueTypeFactory::valueType(QMetaType::QColor); - v8::HandleScope handle_scope; - v8::Context::Scope scope(v8engine->context()); - new (output.getjsvalueptr()) QJSValue(v8engine->scriptValueFromInternal( - v8engine->valueTypeWrapper()->newValueType(tmp, vt))); - JSVALUE_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertColorToJSValue, unaryop) - - QML_V4_BEGIN_INSTR(ConvertColorToString, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - // ### NaN - if (src.isUndefined()) { - output.setUndefined(); - } else { - QQml_valueTypeProvider()->createStringFromValue(QMetaType::QColor, src.typeDataPtr(), output.getstringptr()); - STRING_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertColorToString, unaryop) - - QML_V4_BEGIN_INSTR(ConvertColorToVariant, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - // ### NaN - if (src.isUndefined()) { - output.setUndefined(); - } else { - QVariant tmp(QMetaType::QColor, src.typeDataPtr()); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupColor(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - new (output.getvariantptr()) QVariant(tmp); - VARIANT_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertColorToVariant, unaryop) - - QML_V4_BEGIN_INSTR(ConvertColorToVar, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - // ### NaN - if (src.isUndefined()) { - output.setUndefined(); - } else { - const QVariant tmp(QMetaType::QColor, src.typeDataPtr()); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupColor(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); - QQmlValueType *vt = QQmlValueTypeFactory::valueType(QMetaType::QColor); - new (output.gethandleptr()) v8::Handle<v8::Value>(ep->v8engine()->valueTypeWrapper()->newValueType(tmp, vt)); - V8HANDLE_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertColorToVar, unaryop) - - QML_V4_BEGIN_INSTR(ConvertObjectToBool, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - // ### NaN - if (src.isUndefined()) - output.setUndefined(); - else - output.setbool(src.getQObject() != 0); - } - QML_V4_END_INSTR(ConvertObjectToBool, unaryop) - - QML_V4_BEGIN_INSTR(ConvertObjectToJSValue, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); - v8::HandleScope handle_scope; - v8::Context::Scope scope(ep->v8engine()->context()); - new (output.getjsvalueptr()) QJSValue(context->engine->newQObject(src.getQObject())); - JSVALUE_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertObjectToJSValue, unaryop) - - QML_V4_BEGIN_INSTR(ConvertObjectToVariant, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - // ### NaN - if (src.isUndefined()) - output.setUndefined(); - else { - new (output.getvariantptr()) QVariant(qVariantFromValue<QObject *>(src.getQObject())); - VARIANT_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertObjectToVariant, unaryop) - - QML_V4_BEGIN_INSTR(ConvertObjectToVar, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - // ### NaN - if (src.isUndefined()) - output.setUndefined(); - else { - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); - new (output.gethandleptr()) v8::Handle<v8::Value>(ep->v8engine()->newQObject(src.getQObject())); - V8HANDLE_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertObjectToVar, unaryop) - - QML_V4_BEGIN_INSTR(ConvertVarToJSValue, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - v8::Handle<v8::Value> tmp(*src.gethandleptr()); - if (instr->unaryop.src == instr->unaryop.output) { - output.cleanupHandle(); - MARK_CLEAN_REGISTER(instr->unaryop.output); - } - QV8Engine *v8engine = QQmlEnginePrivate::get(context->engine)->v8engine(); - new (output.getjsvalueptr()) QJSValue(v8engine->scriptValueFromInternal(tmp)); - JSVALUE_REGISTER(instr->unaryop.output); - } - } - QML_V4_END_INSTR(ConvertVarToJSValue, unaryop) - - QML_V4_BEGIN_INSTR(ConvertNullToJSValue, unaryop) - { - Register &output = registers[instr->unaryop.output]; - new (output.getjsvalueptr()) QJSValue(QJSValue::NullValue); - JSVALUE_REGISTER(instr->unaryop.output); - } - QML_V4_END_INSTR(ConvertNullToJSValue, unaryop) - - QML_V4_BEGIN_INSTR(ConvertNullToObject, unaryop) - { - Register &output = registers[instr->unaryop.output]; - output.setQObject(0); - } - QML_V4_END_INSTR(ConvertNullToObject, unaryop) - - QML_V4_BEGIN_INSTR(ConvertNullToVariant, unaryop) - { - Register &output = registers[instr->unaryop.output]; - new (output.getvariantptr()) QVariant(); - VARIANT_REGISTER(instr->unaryop.output); - } - QML_V4_END_INSTR(ConvertNullToVariant, unaryop) - - QML_V4_BEGIN_INSTR(ConvertNullToVar, unaryop) - { - Register &output = registers[instr->unaryop.output]; - new (output.gethandleptr()) v8::Handle<v8::Value>(v8::Null()); - V8HANDLE_REGISTER(instr->unaryop.output); - } - QML_V4_END_INSTR(ConvertNullToVar, unaryop) - - QML_V4_BEGIN_INSTR(ResolveUrl, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) { - output.setUndefined(); - } else { - const QUrl tmp(*src.geturlptr()); - if (instr->unaryop.src == instr->unaryop.output) { - *output.geturlptr() = context->resolvedUrl(tmp); - } else { - new (output.geturlptr()) QUrl(context->resolvedUrl(tmp)); - URL_REGISTER(instr->unaryop.output); - } - } - } - QML_V4_END_INSTR(ResolveUrl, unaryop) - - QML_V4_BEGIN_INSTR(MathSinNumber, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) output.setUndefined(); - else output.setnumber(qSin(src.getnumber())); - } - QML_V4_END_INSTR(MathSinNumber, unaryop) - - QML_V4_BEGIN_INSTR(MathCosNumber, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) output.setUndefined(); - else output.setnumber(qCos(src.getnumber())); - } - QML_V4_END_INSTR(MathCosNumber, unaryop) - - QML_V4_BEGIN_INSTR(MathAbsNumber, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) output.setUndefined(); - else output.setnumber(clearSignBit(qAbs(src.getnumber()))); - } - QML_V4_END_INSTR(MathAbsNumber, unaryop) - - QML_V4_BEGIN_INSTR(MathRoundNumber, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) output.setUndefined(); - else output.setint(qRound(src.getnumber())); - } - QML_V4_END_INSTR(MathRoundNumber, unaryop) - - QML_V4_BEGIN_INSTR(MathFloorNumber, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) - output.setUndefined(); - else if (src.isNaN()) - // output should be an int, but still NaN - output.setNaNType(); - else if (src.isInf()) - // output should be an int, but still Inf - output.setInfType(signBitSet(src.getnumber())); - else if (src.isNegativeZero()) - // output should be an int, but still -0 - output.setNegativeZeroType(); - else - output.setint(qFloor(src.getnumber())); - } - QML_V4_END_INSTR(MathFloorNumber, unaryop) - - QML_V4_BEGIN_INSTR(MathCeilNumber, unaryop) - { - const Register &src = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (src.isUndefined()) - output.setUndefined(); - else if (src.isNaN()) - // output should be an int, but still NaN - output.setNaNType(); - else if (src.isInf()) - // output should be an int, but still Inf - output.setInfType(signBitSet(src.getnumber())); - else if (src.isNegativeZero()) - // output should be an int, but still -0 - output.setNegativeZeroType(); - else { - // Ensure that we preserve the sign bit (Math.ceil(-0) -> -0) - const double input = src.getnumber(); - const int ceiled = qCeil(input); - if (ceiled == 0 && signBitSet(input)) { - output.setNegativeZeroType(); - } else { - output.setint(ceiled); - } - } - } - QML_V4_END_INSTR(MathCeilNumber, unaryop) - - QML_V4_BEGIN_INSTR(MathPINumber, unaryop) - { - static const double qmlPI = 2.0 * qAsin(1.0); - Register &output = registers[instr->unaryop.output]; - output.setnumber(qmlPI); - } - QML_V4_END_INSTR(MathPINumber, unaryop) - - QML_V4_BEGIN_INSTR(LoadNull, null_value) - registers[instr->null_value.reg].setNull(); - QML_V4_END_INSTR(LoadNull, null_value) - - QML_V4_BEGIN_INSTR(LoadNumber, number_value) - registers[instr->number_value.reg].setnumber(instr->number_value.value); - QML_V4_END_INSTR(LoadNumber, number_value) - - QML_V4_BEGIN_INSTR(LoadInt, int_value) - registers[instr->int_value.reg].setint(instr->int_value.value); - QML_V4_END_INSTR(LoadInt, int_value) - - QML_V4_BEGIN_INSTR(LoadBool, bool_value) - registers[instr->bool_value.reg].setbool(instr->bool_value.value); - QML_V4_END_INSTR(LoadBool, bool_value) - - QML_V4_BEGIN_INSTR(LoadString, string_value) - { - Register &output = registers[instr->string_value.reg]; - QChar *string = (QChar *)(data + instr->string_value.offset); - new (output.getstringptr()) QString(string, instr->string_value.length); - STRING_REGISTER(instr->string_value.reg); - } - QML_V4_END_INSTR(LoadString, string_value) - - QML_V4_BEGIN_INSTR(EnableV4Test, string_value) - { - testBindingSource = new QString((QChar *)(data + instr->string_value.offset), instr->string_value.length); - testBinding = true; - } - QML_V4_END_INSTR(String, string_value) - - QML_V4_BEGIN_INSTR(BitAndInt, binaryop) - { - registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() & - registers[instr->binaryop.right].getint()); - } - QML_V4_END_INSTR(BitAndInt, binaryop) - - QML_V4_BEGIN_INSTR(BitOrInt, binaryop) - { - registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() | - registers[instr->binaryop.right].getint()); - } - QML_V4_END_INSTR(BitAndInt, binaryop) - - QML_V4_BEGIN_INSTR(BitXorInt, binaryop) - { - registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() ^ - registers[instr->binaryop.right].getint()); - } - QML_V4_END_INSTR(BitXorInt, binaryop) - - QML_V4_BEGIN_INSTR(AddNumber, binaryop) - { - registers[instr->binaryop.output].setnumber(registers[instr->binaryop.left].getnumber() + - registers[instr->binaryop.right].getnumber()); - } - QML_V4_END_INSTR(AddNumber, binaryop) - - QML_V4_BEGIN_INSTR(AddString, binaryop) - { - QString &string = *registers[instr->binaryop.output].getstringptr(); - if (instr->binaryop.output == instr->binaryop.left) { - string += registers[instr->binaryop.right].getstringptr(); - } else { - string = *registers[instr->binaryop.left].getstringptr() + - *registers[instr->binaryop.right].getstringptr(); - } - } - QML_V4_END_INSTR(AddString, binaryop) - - QML_V4_BEGIN_INSTR(SubNumber, binaryop) - { - registers[instr->binaryop.output].setnumber(registers[instr->binaryop.left].getnumber() - - registers[instr->binaryop.right].getnumber()); - } - QML_V4_END_INSTR(SubNumber, binaryop) - - QML_V4_BEGIN_INSTR(MulNumber, binaryop) - { - registers[instr->binaryop.output].setnumber(registers[instr->binaryop.left].getnumber() * - registers[instr->binaryop.right].getnumber()); - } - QML_V4_END_INSTR(MulNumber, binaryop) - - QML_V4_BEGIN_INSTR(DivNumber, binaryop) - { - registers[instr->binaryop.output].setnumber(registers[instr->binaryop.left].getnumber() / - registers[instr->binaryop.right].getnumber()); - } - QML_V4_END_INSTR(DivNumber, binaryop) - - QML_V4_BEGIN_INSTR(ModNumber, binaryop) - { - Register &target = registers[instr->binaryop.output]; - const Register &left = registers[instr->binaryop.left]; - const Register &right = registers[instr->binaryop.right]; - target.setnumber(::fmod(left.getnumber(), right.getnumber())); - } - QML_V4_END_INSTR(ModInt, binaryop) - - QML_V4_BEGIN_INSTR(LShiftInt, binaryop) - { - registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() << - registers[instr->binaryop.right].getint()); - } - QML_V4_END_INSTR(LShiftInt, binaryop) - - QML_V4_BEGIN_INSTR(RShiftInt, binaryop) - { - registers[instr->binaryop.output].setint(registers[instr->binaryop.left].getint() >> - registers[instr->binaryop.right].getint()); - } - QML_V4_END_INSTR(RShiftInt, binaryop) - - QML_V4_BEGIN_INSTR(URShiftInt, binaryop) - { - registers[instr->binaryop.output].setint((unsigned)registers[instr->binaryop.left].getint() >> - registers[instr->binaryop.right].getint()); - } - QML_V4_END_INSTR(URShiftInt, binaryop) - - QML_V4_BEGIN_INSTR(GtNumber, binaryop) - { - registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() > - registers[instr->binaryop.right].getnumber()); - } - QML_V4_END_INSTR(GtNumber, binaryop) - - QML_V4_BEGIN_INSTR(LtNumber, binaryop) - { - registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() < - registers[instr->binaryop.right].getnumber()); - } - QML_V4_END_INSTR(LtNumber, binaryop) - - QML_V4_BEGIN_INSTR(GeNumber, binaryop) - { - registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() >= - registers[instr->binaryop.right].getnumber()); - } - QML_V4_END_INSTR(GeNumber, binaryop) - - QML_V4_BEGIN_INSTR(LeNumber, binaryop) - { - registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() <= - registers[instr->binaryop.right].getnumber()); - } - QML_V4_END_INSTR(LeNumber, binaryop) - - QML_V4_BEGIN_INSTR(EqualNumber, binaryop) - { - registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() == - registers[instr->binaryop.right].getnumber()); - } - QML_V4_END_INSTR(EqualNumber, binaryop) - - QML_V4_BEGIN_INSTR(NotEqualNumber, binaryop) - { - registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() != - registers[instr->binaryop.right].getnumber()); - } - QML_V4_END_INSTR(NotEqualNumber, binaryop) - - QML_V4_BEGIN_INSTR(StrictEqualNumber, binaryop) - { - registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() == - registers[instr->binaryop.right].getnumber()); - } - QML_V4_END_INSTR(StrictEqualNumber, binaryop) - - QML_V4_BEGIN_INSTR(StrictNotEqualNumber, binaryop) - { - registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() != - registers[instr->binaryop.right].getnumber()); - } - QML_V4_END_INSTR(StrictNotEqualNumber, binaryop) - - QML_V4_BEGIN_INSTR(GtString, binaryop) - { - const QString &a = *registers[instr->binaryop.left].getstringptr(); - const QString &b = *registers[instr->binaryop.right].getstringptr(); - bool result = a > b; - if (instr->binaryop.left == instr->binaryop.output) { - registers[instr->binaryop.output].cleanupString(); - MARK_CLEAN_REGISTER(instr->binaryop.output); - } - registers[instr->binaryop.output].setbool(result); - } - QML_V4_END_INSTR(GtString, binaryop) - - QML_V4_BEGIN_INSTR(LtString, binaryop) - { - const QString &a = *registers[instr->binaryop.left].getstringptr(); - const QString &b = *registers[instr->binaryop.right].getstringptr(); - bool result = a < b; - if (instr->binaryop.left == instr->binaryop.output) { - registers[instr->binaryop.output].cleanupString(); - MARK_CLEAN_REGISTER(instr->binaryop.output); - } - registers[instr->binaryop.output].setbool(result); - } - QML_V4_END_INSTR(LtString, binaryop) - - QML_V4_BEGIN_INSTR(GeString, binaryop) - { - const QString &a = *registers[instr->binaryop.left].getstringptr(); - const QString &b = *registers[instr->binaryop.right].getstringptr(); - bool result = a >= b; - if (instr->binaryop.left == instr->binaryop.output) { - registers[instr->binaryop.output].cleanupString(); - MARK_CLEAN_REGISTER(instr->binaryop.output); - } - registers[instr->binaryop.output].setbool(result); - } - QML_V4_END_INSTR(GeString, binaryop) - - QML_V4_BEGIN_INSTR(LeString, binaryop) - { - const QString &a = *registers[instr->binaryop.left].getstringptr(); - const QString &b = *registers[instr->binaryop.right].getstringptr(); - bool result = a <= b; - if (instr->binaryop.left == instr->binaryop.output) { - registers[instr->binaryop.output].cleanupString(); - MARK_CLEAN_REGISTER(instr->binaryop.output); - } - registers[instr->binaryop.output].setbool(result); - } - QML_V4_END_INSTR(LeString, binaryop) - - QML_V4_BEGIN_INSTR(EqualString, binaryop) - { - const QString &a = *registers[instr->binaryop.left].getstringptr(); - const QString &b = *registers[instr->binaryop.right].getstringptr(); - bool result = a == b; - if (instr->binaryop.left == instr->binaryop.output) { - registers[instr->binaryop.output].cleanupString(); - MARK_CLEAN_REGISTER(instr->binaryop.output); - } - registers[instr->binaryop.output].setbool(result); - } - QML_V4_END_INSTR(EqualString, binaryop) - - QML_V4_BEGIN_INSTR(NotEqualString, binaryop) - { - const QString &a = *registers[instr->binaryop.left].getstringptr(); - const QString &b = *registers[instr->binaryop.right].getstringptr(); - bool result = a != b; - if (instr->binaryop.left == instr->binaryop.output) { - registers[instr->binaryop.output].cleanupString(); - MARK_CLEAN_REGISTER(instr->binaryop.output); - } - registers[instr->binaryop.output].setbool(result); - } - QML_V4_END_INSTR(NotEqualString, binaryop) - - QML_V4_BEGIN_INSTR(StrictEqualString, binaryop) - { - const QString &a = *registers[instr->binaryop.left].getstringptr(); - const QString &b = *registers[instr->binaryop.right].getstringptr(); - bool result = a == b; - if (instr->binaryop.left == instr->binaryop.output) { - registers[instr->binaryop.output].cleanupString(); - MARK_CLEAN_REGISTER(instr->binaryop.output); - } - registers[instr->binaryop.output].setbool(result); - } - QML_V4_END_INSTR(StrictEqualString, binaryop) - - QML_V4_BEGIN_INSTR(StrictNotEqualString, binaryop) - { - const QString &a = *registers[instr->binaryop.left].getstringptr(); - const QString &b = *registers[instr->binaryop.right].getstringptr(); - bool result = a != b; - if (instr->binaryop.left == instr->binaryop.output) { - registers[instr->binaryop.output].cleanupString(); - MARK_CLEAN_REGISTER(instr->binaryop.output); - } - registers[instr->binaryop.output].setbool(result); - } - QML_V4_END_INSTR(StrictNotEqualString, binaryop) - - QML_V4_BEGIN_INSTR(EqualObject, binaryop) - { - const Register &left = registers[instr->binaryop.left]; - const Register &right = registers[instr->binaryop.right]; - QObject *leftobj = (left.gettype() == NullType) ? 0 : left.getQObject(); - QObject *rightobj = (right.gettype() == NullType) ? 0 : right.getQObject(); - registers[instr->binaryop.output].setbool(leftobj == rightobj); - } - QML_V4_END_INSTR(EqualObject, binaryop) - - QML_V4_BEGIN_INSTR(NotEqualObject, binaryop) - { - const Register &left = registers[instr->binaryop.left]; - const Register &right = registers[instr->binaryop.right]; - QObject *leftobj = (left.gettype() == NullType) ? 0 : left.getQObject(); - QObject *rightobj = (right.gettype() == NullType) ? 0 : right.getQObject(); - registers[instr->binaryop.output].setbool(leftobj != rightobj); - } - QML_V4_END_INSTR(NotEqualObject, binaryop) - - QML_V4_BEGIN_INSTR(StrictEqualObject, binaryop) - { - const Register &left = registers[instr->binaryop.left]; - const Register &right = registers[instr->binaryop.right]; - QObject *leftobj = (left.gettype() == NullType) ? 0 : left.getQObject(); - QObject *rightobj = (right.gettype() == NullType) ? 0 : right.getQObject(); - registers[instr->binaryop.output].setbool(leftobj == rightobj); - } - QML_V4_END_INSTR(StrictEqualObject, binaryop) - - QML_V4_BEGIN_INSTR(StrictNotEqualObject, binaryop) - { - const Register &left = registers[instr->binaryop.left]; - const Register &right = registers[instr->binaryop.right]; - QObject *leftobj = (left.gettype() == NullType) ? 0 : left.getQObject(); - QObject *rightobj = (right.gettype() == NullType) ? 0 : right.getQObject(); - registers[instr->binaryop.output].setbool(leftobj != rightobj); - } - QML_V4_END_INSTR(StrictNotEqualObject, binaryop) - - QML_V4_BEGIN_INSTR(MathMaxNumber, binaryop) - { - const Register &left = registers[instr->binaryop.left]; - const Register &right = registers[instr->binaryop.right]; - Register &output = registers[instr->binaryop.output]; - if (left.isUndefined() || right.isUndefined()) { - output.setUndefined(); - } else { - const double lhs = left.getnumber(); - const double rhs = right.getnumber(); - double result(lhs); - if (lhs == rhs) { - // If these are both zero, +0 is greater than -0 - if (signBitSet(lhs) && !signBitSet(rhs)) - result = rhs; - } else { - result = qMax(lhs, rhs); - } - output.setnumber(result); - } - } - QML_V4_END_INSTR(MathMaxNumber, binaryop) - - QML_V4_BEGIN_INSTR(MathMinNumber, binaryop) - { - const Register &left = registers[instr->binaryop.left]; - const Register &right = registers[instr->binaryop.right]; - Register &output = registers[instr->binaryop.output]; - if (left.isUndefined() || right.isUndefined()) { - output.setUndefined(); - } else { - const double lhs = left.getnumber(); - const double rhs = right.getnumber(); - double result(lhs); - if (lhs == rhs) { - // If these are both zero, -0 is lesser than +0 - if (!signBitSet(lhs) && signBitSet(rhs)) - result = rhs; - } else { - result = qMin(lhs, rhs); - } - output.setnumber(result); - } - } - QML_V4_END_INSTR(MathMinNumber, binaryop) - - QML_V4_BEGIN_INSTR(NewString, construct) - { - Register &output = registers[instr->construct.reg]; - new (output.getstringptr()) QString; - STRING_REGISTER(instr->construct.reg); - } - QML_V4_END_INSTR(NewString, construct) - - QML_V4_BEGIN_INSTR(NewUrl, construct) - { - Register &output = registers[instr->construct.reg]; - new (output.geturlptr()) QUrl; - URL_REGISTER(instr->construct.reg); - } - QML_V4_END_INSTR(NewUrl, construct) - - QML_V4_BEGIN_INSTR(Fetch, fetch) - { - Register ® = registers[instr->fetch.reg]; - - if (reg.isUndefined()) - THROW_EXCEPTION(instr->fetch.exceptionId); - - QObject *object = reg.getQObject(); - if (!object) { - THROW_EXCEPTION(instr->fetch.exceptionId); - } else { - if (bindingInvalidated(invalidated, object, context, instr->fetch.index)) - goto programExit; - - const Register::Type valueType = (Register::Type)instr->fetch.valueType; - reg.init(valueType); - if (instr->fetch.valueType >= FirstCleanupType) - MARK_REGISTER(instr->fetch.reg); - - QQmlData::flushPendingBinding(object, instr->fetch.index); - - void *argv[] = { reg.typeDataPtr(), 0 }; - QMetaObject::metacall(object, QMetaObject::ReadProperty, instr->fetch.index, argv); - if (valueType == FloatType) { - // promote floats - const double v = reg.getfloat(); - reg.setnumber(v); - } - - if (instr->fetch.subIndex != static_cast<quint32>(-1)) - subscribe(object, instr->fetch.subIndex, instr->fetch.subOffset, context->engine); - - } - } - QML_V4_END_INSTR(Fetch, fetch) - - QML_V4_BEGIN_INSTR(TestV4Store, storetest) - { - Register &data = registers[instr->storetest.reg]; - testBindingResult(*testBindingSource, bindingLine, bindingColumn, context, - scope, data, instr->storetest.regType); - } - QML_V4_END_INSTR(TestV4Store, storetest) - - QML_V4_BEGIN_INSTR(Store, store) - { - Register &data = registers[instr->store.reg]; - - if (data.isUndefined()) - THROW_EXCEPTION_STR(instr->store.exceptionId, QLatin1String("Unable to assign undefined value")); - - if (data.gettype() == QObjectStarType) { - if (QObject *dataObject = data.getQObject()) { - QQmlMetaObject dataMo(dataObject); - - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); - - QQmlMetaObject receiverMo; - - if (QQmlData::get(output, false) && QQmlData::get(output, false)->propertyCache) { - QQmlPropertyData *receiver = - QQmlData::get(output, false)->propertyCache->property(instr->store.index); - receiverMo = ep->rawMetaObjectForType(receiver->propType); - } else { - QMetaProperty receiver = output->metaObject()->property(instr->store.index); - receiverMo = ep->rawMetaObjectForType(receiver.userType()); - } - - // Verify that these types are compatible - if (!QQmlMetaObject::canConvert(dataMo, receiverMo)) { - THROW_EXCEPTION_STR(instr->store.exceptionId, QLatin1String("Unable to assign ") + - QLatin1String(dataMo.className()) + - QLatin1String(" to ") + - QLatin1String(receiverMo.className())); - } - } - } - - if (instr->store.valueType == FloatType) { - // cast numbers to floats - const float v = (float) data.getnumber(); - data.setfloat(v); - } - - if (data.gettype() == V8HandleType) { - // This property must be a VME var property - QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(output); - Q_ASSERT(vmemo); - vmemo->setVMEProperty(instr->store.index, *data.gethandleptr()); - } else { - int status = -1; - void *argv[] = { data.typeDataPtr(), 0, &status, &storeFlags }; - QMetaObject::metacall(output, QMetaObject::WriteProperty, - instr->store.index, argv); - } - - goto programExit; - } - QML_V4_END_INSTR(Store, store) - - QML_V4_BEGIN_INSTR(Copy, copy) - registers[instr->copy.reg].copy(registers[instr->copy.src]); - if (registers[instr->copy.reg].gettype() >= FirstCleanupType) - MARK_REGISTER(instr->copy.reg); - QML_V4_END_INSTR(Copy, copy) - - QML_V4_BEGIN_INSTR(Jump, jump) - if (instr->jump.reg == -1 || !registers[instr->jump.reg].getbool()) - code += instr->jump.count; - QML_V4_END_INSTR(Jump, jump) - - QML_V4_BEGIN_INSTR(BranchTrue, branchop) - if (registers[instr->branchop.reg].getbool()) - code += instr->branchop.offset; - QML_V4_END_INSTR(BranchTrue, branchop) - - QML_V4_BEGIN_INSTR(BranchFalse, branchop) - if (! registers[instr->branchop.reg].getbool()) - code += instr->branchop.offset; - QML_V4_END_INSTR(BranchFalse, branchop) - - QML_V4_BEGIN_INSTR(Branch, branchop) - code += instr->branchop.offset; - QML_V4_END_INSTR(Branch, branchop) - - QML_V4_BEGIN_INSTR(Block, blockop) - executedBlocks |= instr->blockop.block; - QML_V4_END_INSTR(Block, blockop) - - QML_V4_BEGIN_INSTR(CleanupRegister, cleanup) - registers[instr->cleanup.reg].cleanup(); - QML_V4_END_INSTR(CleanupRegister, cleanup) - - QML_V4_BEGIN_INSTR(Throw, throwop) - THROW_VALUE_EXCEPTION_STR(instr->throwop.exceptionId, *registers[instr->throwop.message].getstringptr()); - QML_V4_END_INSTR(Throw, throwop) - -#ifdef QML_THREADED_INTERPRETER - // nothing to do -#else - default: - qFatal("QV4: Unknown instruction %d encountered.", instr->common.type); - break; - } // switch - - } // while -#endif - - Q_ASSERT(!"Unreachable code reached"); - -programExit: -exceptionExit: - delete testBindingSource; - - int reg = 0; - while (cleanupRegisterMask) { - if (cleanupRegisterMask & 0x1) - registers[reg].cleanup(); - - reg++; - cleanupRegisterMask >>= 1; - } -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4bindings_p.h b/src/qml/qml/v4/qv4bindings_p.h deleted file mode 100644 index adb05ba1f4..0000000000 --- a/src/qml/qml/v4/qv4bindings_p.h +++ /dev/null @@ -1,172 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4BINDINGS_P_H -#define QV4BINDINGS_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "private/qqmlexpression_p.h" -#include "private/qqmlbinding_p.h" -#include "private/qqmlinstruction_p.h" -#include "private/qv4instruction_p.h" -#include "private/qpointervaluepair_p.h" - -QT_BEGIN_NAMESPACE - -struct QV4Program; -class QV4Bindings : public QQmlAbstractExpression, - public QQmlRefCount -{ - Q_DECLARE_TR_FUNCTIONS(QV4Bindings) -public: - QV4Bindings(const char *program, QQmlContextData *context); - virtual ~QV4Bindings(); - - QQmlAbstractBinding *configBinding(QObject *target, QObject *scope, - const QQmlInstruction::instr_assignV4Binding *); - -#ifdef QML_THREADED_INTERPRETER - static void **getDecodeInstrTable(); -#endif - - struct Binding : public QQmlAbstractBinding, public QQmlDelayedError { - Binding() - : QQmlAbstractBinding(V4), target(0), scope(0), instruction(0), executedBlocks(0), parent(0) {} - - // Inherited from QQmlAbstractBinding - static void destroy(QQmlAbstractBinding *, QQmlAbstractBinding::DestroyMode mode); - static int propertyIndex(const QQmlAbstractBinding *); - static QObject *object(const QQmlAbstractBinding *); - static void setEnabled(QQmlAbstractBinding *, bool, QQmlPropertyPrivate::WriteFlags); - static void update(QQmlAbstractBinding *, QQmlPropertyPrivate::WriteFlags); - static void retargetBinding(QQmlAbstractBinding *, QObject *, int); - - void disconnect(); - - void dump(); - - struct Retarget { - QObject *target; - int targetProperty; - }; - - QPointerValuePair<QObject, Retarget> target; - QObject *scope; - - // To save memory, we store flags inside the instruction pointer. - // instruction.flag1: enabled - // instruction.flag2: updating - QFlagPointer<const QQmlInstruction::instr_assignV4Binding> instruction; - - quint32 executedBlocks; - QV4Bindings *parent; - - inline bool enabledFlag() const { return instruction.flag(); } - inline void setEnabledFlag(bool v) { instruction.setFlagValue(v); } - inline bool updatingFlag() const { return instruction.flag2(); } - inline void setUpdatingFlag(bool v) { instruction.setFlag2Value(v); } - }; - -private: - Q_DISABLE_COPY(QV4Bindings) - - class Subscription : public QQmlNotifierEndpoint - { - public: - inline Subscription(); - - // Index of this Subscription into the QV4Bindings::subscriptions array. - // This may not be used before setBindings() was called. - inline int method() const; - - inline void setBindings(QV4Bindings *bindings); - inline QV4Bindings *bindings() const; - - inline bool active() const; - inline void setActive(bool active); - - // Pointer to the parent QV4Bindings. The flag is used as the 'active' value. - QFlagPointer<QV4Bindings> m_bindings; - }; - friend void QV4BindingsSubscription_callback(QQmlNotifierEndpoint *e, void **); - - Subscription *subscriptions; - - void subscriptionNotify(int); - void run(Binding *, QQmlPropertyPrivate::WriteFlags flags); - - QV4Program *program; - Binding *bindings; - - void init(); - void run(int instr, quint32 &executedBlocks, QQmlContextData *context, - QQmlDelayedError *error, QObject *scope, QObject *output, - QQmlPropertyPrivate::WriteFlags storeFlags, - bool *invalidated -#ifdef QML_THREADED_INTERPRETER - , void ***decode_instr = 0 -#endif - ); - - - inline void subscribeId(QQmlContextData *p, int idIndex, int subIndex); - inline void subscribe(QObject *o, int notifyIndex, int subIndex, QQmlEngine *); - - inline static qint32 toInt32(double n); - static const double D32; - static quint32 toUint32(double n); - -}; - -QT_END_NAMESPACE - -#endif // QV4BINDINGS_P_H - diff --git a/src/qml/qml/v4/qv4booleanobject.cpp b/src/qml/qml/v4/qv4booleanobject.cpp new file mode 100644 index 0000000000..24678d23dc --- /dev/null +++ b/src/qml/qml/v4/qv4booleanobject.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4booleanobject_p.h" + +using namespace QV4; + +DEFINE_MANAGED_VTABLE(BooleanCtor); + +BooleanCtor::BooleanCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier("Boolean")) +{ + vtbl = &static_vtbl; +} + +Value BooleanCtor::construct(Managed *m, Value *args, int argc) +{ + bool n = argc ? args[0].toBoolean() : false; + return Value::fromObject(m->engine()->newBooleanObject(Value::fromBoolean(n))); +} + +Value BooleanCtor::call(Managed *, const Value &, Value *argv, int argc) +{ + bool value = argc ? argv[0].toBoolean() : 0; + return Value::fromBoolean(value); +} + +void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); +} + +Value BooleanPrototype::method_toString(SimpleCallContext *ctx) +{ + bool result; + if (ctx->thisObject.isBoolean()) { + result = ctx->thisObject.booleanValue(); + } else { + BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); + if (!thisObject) + ctx->throwTypeError(); + result = thisObject->value.booleanValue(); + } + + return Value::fromString(ctx, QLatin1String(result ? "true" : "false")); +} + +Value BooleanPrototype::method_valueOf(SimpleCallContext *ctx) +{ + BooleanObject *thisObject = ctx->thisObject.asBooleanObject(); + if (!thisObject) + ctx->throwTypeError(); + + return thisObject->value; +} diff --git a/src/qml/qml/v4/qv4booleanobject_p.h b/src/qml/qml/v4/qv4booleanobject_p.h new file mode 100644 index 0000000000..3e5e7663f2 --- /dev/null +++ b/src/qml/qml/v4/qv4booleanobject_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4BOOLEANOBJECT_H +#define QBOOLEANOBJECT_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct BooleanCtor: FunctionObject +{ + BooleanCtor(ExecutionContext *scope); + + static Value construct(Managed *, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct BooleanPrototype: BooleanObject +{ + BooleanPrototype(ExecutionEngine *engine): BooleanObject(engine, Value::fromBoolean(false)) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_toString(SimpleCallContext *ctx); + static Value method_valueOf(SimpleCallContext *ctx); +}; + + +} + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4/qv4codegen.cpp b/src/qml/qml/v4/qv4codegen.cpp new file mode 100644 index 0000000000..d0c43c8f56 --- /dev/null +++ b/src/qml/qml/v4/qv4codegen.cpp @@ -0,0 +1,2605 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4codegen_p.h" +#include "qv4util_p.h" +#include "qv4debugging_p.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QStringList> +#include <QtCore/QSet> +#include <QtCore/QBuffer> +#include <QtCore/QBitArray> +#include <QtCore/QLinkedList> +#include <QtCore/QStack> +#include <private/qqmljsast_p.h> +#include <qv4runtime_p.h> +#include <qv4context_p.h> +#include <cmath> +#include <iostream> +#include <cassert> + +#ifdef CONST +#undef CONST +#endif + +#define QV4_NO_LIVENESS +#undef SHOW_SSA + +using namespace QQmlJS; +using namespace AST; + +class Codegen::ScanFunctions: Visitor +{ + typedef QV4::TemporaryAssignment<bool> TemporaryBoolAssignment; +public: + ScanFunctions(Codegen *cg, const QString &sourceCode) + : _cg(cg) + , _sourceCode(sourceCode) + , _env(0) + , _inFuncBody(false) + , _allowFuncDecls(true) + { + } + + void operator()(Node *node) + { + if (node) + node->accept(this); + } + + inline void enterEnvironment(Node *node) + { + Environment *e = _cg->newEnvironment(node, _env); + if (!e->isStrict) + e->isStrict = _cg->_strictMode; + _envStack.append(e); + _env = e; + } + + inline void leaveEnvironment() + { + _envStack.pop(); + _env = _envStack.isEmpty() ? 0 : _envStack.top(); + } + +protected: + using Visitor::visit; + using Visitor::endVisit; + + void checkDirectivePrologue(SourceElements *ast) + { + for (SourceElements *it = ast; it; it = it->next) { + if (StatementSourceElement *stmt = cast<StatementSourceElement *>(it->element)) { + if (ExpressionStatement *expr = cast<ExpressionStatement *>(stmt->statement)) { + if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) { + // Use the source code, because the StringLiteral's + // value might have escape sequences in it, which is not + // allowed. + if (strLit->literalToken.length < 2) + continue; + QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); + if (str == QStringLiteral("use strict")) { + _env->isStrict = true; + } else { + // TODO: give a warning. + } + continue; + } + } + } + + break; + } + } + + void checkName(const QStringRef &name, const SourceLocation &loc) + { + if (_env->isStrict) { + if (name == QLatin1String("implements") + || name == QLatin1String("interface") + || name == QLatin1String("let") + || name == QLatin1String("package") + || name == QLatin1String("private") + || name == QLatin1String("protected") + || name == QLatin1String("public") + || name == QLatin1String("static") + || name == QLatin1String("yield")) { + _cg->throwSyntaxError(loc, QCoreApplication::translate("qv4codegen", "Unexpected strict mode reserved word")); + } + } + } + void checkForArguments(AST::FormalParameterList *parameters) + { + while (parameters) { + if (parameters->name == QStringLiteral("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + parameters = parameters->next; + } + } + + virtual bool visit(Program *ast) + { + enterEnvironment(ast); + checkDirectivePrologue(ast->elements); + return true; + } + + virtual void endVisit(Program *) + { + leaveEnvironment(); + } + + virtual bool visit(CallExpression *ast) + { + if (! _env->hasDirectEval) { + if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) { + if (id->name == QStringLiteral("eval")) { + if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown) + _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; + _env->hasDirectEval = true; + } + } + } + int argc = 0; + for (ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + return true; + } + + virtual bool visit(NewMemberExpression *ast) + { + int argc = 0; + for (ArgumentList *it = ast->arguments; it; it = it->next) + ++argc; + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + return true; + } + + virtual bool visit(ArrayLiteral *ast) + { + int index = 0; + for (ElementList *it = ast->elements; it; it = it->next) { + for (Elision *elision = it->elision; elision; elision = elision->next) + ++index; + ++index; + } + if (ast->elision) { + for (Elision *elision = ast->elision->next; elision; elision = elision->next) + ++index; + } + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, index); + return true; + } + + virtual bool visit(VariableDeclaration *ast) + { + if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); + checkName(ast->name, ast->identifierToken); + if (ast->name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + _env->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration); + return true; + } + + virtual bool visit(IdentifierExpression *ast) + { + checkName(ast->name, ast->identifierToken); + if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; + return true; + } + + virtual bool visit(ExpressionStatement *ast) + { + if (FunctionExpression* expr = AST::cast<AST::FunctionExpression*>(ast->expression)) { + if (!_allowFuncDecls) + _cg->throwSyntaxError(expr->functionToken, QCoreApplication::translate("qv4codegen", "conditional function or closure declaration")); + + enterFunction(expr, /*enterName*/ true); + Node::accept(expr->formals, this); + Node::accept(expr->body, this); + leaveEnvironment(); + return false; + } else { + SourceLocation firstToken = ast->firstSourceLocation(); + if (_sourceCode.midRef(firstToken.offset, firstToken.length) == QStringLiteral("function")) { + _cg->throwSyntaxError(firstToken, QCoreApplication::translate("qv4codegen", "unexpected token")); + } + } + return true; + } + + virtual bool visit(FunctionExpression *ast) + { + enterFunction(ast, /*enterName*/ false); + return true; + } + + void enterFunction(FunctionExpression *ast, bool enterName, bool isExpression = true) + { + if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + _cg->throwSyntaxError(ast->identifierToken, QCoreApplication::translate("qv4codegen", "Function name may not be eval or arguments in strict mode")); + enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0, isExpression); + } + + virtual void endVisit(FunctionExpression *) + { + leaveEnvironment(); + } + + virtual bool visit(ObjectLiteral *ast) + { + int argc = 0; + for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { + ++argc; + if (AST::cast<AST::PropertyGetterSetter *>(it->assignment)) + ++argc; + } + _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); + Node::accept(ast->properties, this); + return false; + } + + virtual bool visit(PropertyGetterSetter *ast) + { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); + enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/0, /*isExpression*/false); + return true; + } + + virtual void endVisit(PropertyGetterSetter *) + { + leaveEnvironment(); + } + + virtual bool visit(FunctionDeclaration *ast) + { + enterFunction(ast, /*enterName*/ true, /*isExpression */false); + return true; + } + + virtual void endVisit(FunctionDeclaration *) + { + leaveEnvironment(); + } + + virtual bool visit(FunctionBody *ast) + { + TemporaryBoolAssignment inFuncBody(_inFuncBody, true); + Node::accept(ast->elements, this); + return false; + } + + virtual bool visit(WithStatement *ast) + { + if (_env->isStrict) { + _cg->throwSyntaxError(ast->withToken, QCoreApplication::translate("qv4codegen", "'with' statement is not allowed in strict mode")); + return false; + } + + return true; + } + + virtual bool visit(IfStatement *ast) { + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_inFuncBody); + Node::accept(ast->ok, this); + Node::accept(ast->ko, this); + + return false; + } + + virtual bool visit(WhileStatement *ast) { + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_inFuncBody); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(DoWhileStatement *ast) { + { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + } + Node::accept(ast->expression, this); + return false; + } + + virtual bool visit(ForStatement *ast) { + Node::accept(ast->initialiser, this); + Node::accept(ast->condition, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(LocalForStatement *ast) { + Node::accept(ast->declarations, this); + Node::accept(ast->condition, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(ForEachStatement *ast) { + Node::accept(ast->initialiser, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(LocalForEachStatement *ast) { + Node::accept(ast->declaration, this); + Node::accept(ast->expression, this); + + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + Node::accept(ast->statement, this); + + return false; + } + + virtual bool visit(Block *ast) { + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _env->isStrict ? false : _allowFuncDecls); + Node::accept(ast->statements, this); + return false; + } + +private: + void enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr, bool isExpression) + { + bool wasStrict = false; + if (_env) { + _env->hasNestedFunctions = true; + // The identifier of a function expression cannot be referenced from the enclosing environment. + if (expr) + _env->enter(name, Environment::FunctionDefinition, expr); + if (name == QLatin1String("arguments")) + _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + wasStrict = _env->isStrict; + } + + enterEnvironment(ast); + checkForArguments(formals); + + _env->isNamedFunctionExpression = isExpression && !name.isEmpty(); + _env->formals = formals; + + if (body) + checkDirectivePrologue(body->elements); + + if (wasStrict || _env->isStrict) { + QStringList args; + for (FormalParameterList *it = formals; it; it = it->next) { + QString arg = it->name.toString(); + if (args.contains(arg)) + _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "Duplicate parameter name '%1' is not allowed in strict mode").arg(arg)); + if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) + _cg->throwSyntaxError(it->identifierToken, QCoreApplication::translate("qv4codegen", "'%1' cannot be used as parameter name in strict mode").arg(arg)); + args += arg; + } + } + } + +private: // fields: + Codegen *_cg; + const QString _sourceCode; + Environment *_env; + QStack<Environment *> _envStack; + + bool _inFuncBody; + bool _allowFuncDecls; +}; + +Codegen::Codegen(QV4::ExecutionContext *context, bool strict) + : _module(0) + , _function(0) + , _block(0) + , _exitBlock(0) + , _throwBlock(0) + , _returnAddress(0) + , _mode(GlobalCode) + , _env(0) + , _loop(0) + , _labelledStatement(0) + , _scopeAndFinally(0) + , _context(context) + , _strictMode(strict) + , _errorHandler(0) +{ +} + +Codegen::Codegen(ErrorHandler *errorHandler, bool strictMode) + : _module(0) + , _function(0) + , _block(0) + , _exitBlock(0) + , _throwBlock(0) + , _returnAddress(0) + , _mode(GlobalCode) + , _env(0) + , _loop(0) + , _labelledStatement(0) + , _scopeAndFinally(0) + , _context(0) + , _strictMode(strictMode) + , _errorHandler(errorHandler) +{ +} + +V4IR::Function *Codegen::operator()(const QString &fileName, + const QString &sourceCode, + Program *node, + V4IR::Module *module, + Mode mode, + const QStringList &inheritedLocals) +{ + assert(node); + + _fileName = fileName; + _module = module; + _env = 0; + + ScanFunctions scan(this, sourceCode); + scan(node); + + V4IR::Function *globalCode = defineFunction(QStringLiteral("%entry"), node, 0, + node->elements, mode, inheritedLocals); + qDeleteAll(_envMap); + _envMap.clear(); + + return globalCode; +} + +V4IR::Function *Codegen::operator()(const QString &fileName, + const QString &sourceCode, + AST::FunctionExpression *ast, + V4IR::Module *module) +{ + _fileName = fileName; + _module = module; + _env = 0; + + ScanFunctions scan(this, sourceCode); + // fake a global environment + scan.enterEnvironment(0); + scan(ast); + scan.leaveEnvironment(); + + V4IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); + + qDeleteAll(_envMap); + _envMap.clear(); + + return function; +} + + +void Codegen::enterEnvironment(Node *node) +{ + _env = _envMap.value(node); + assert(_env); +} + +void Codegen::leaveEnvironment() +{ + assert(_env); + _env = _env->parent; +} + +void Codegen::enterLoop(Statement *node, V4IR::BasicBlock *startBlock, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock) +{ + if (startBlock) + startBlock->markAsGroupStart(); + _loop = new Loop(node, startBlock, breakBlock, continueBlock, _loop); + _loop->labelledStatement = _labelledStatement; // consume the enclosing labelled statement + _loop->scopeAndFinally = _scopeAndFinally; + _labelledStatement = 0; +} + +void Codegen::leaveLoop() +{ + Loop *current = _loop; + _loop = _loop->parent; + delete current; +} + +V4IR::Expr *Codegen::member(V4IR::Expr *base, const QString *name) +{ + if (base->asTemp() /*|| base->asName()*/) + return _block->MEMBER(base->asTemp(), name); + else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), base); + return _block->MEMBER(_block->TEMP(t), name); + } +} + +V4IR::Expr *Codegen::subscript(V4IR::Expr *base, V4IR::Expr *index) +{ + if (! base->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), base); + base = _block->TEMP(t); + } + + if (! index->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), index); + index = _block->TEMP(t); + } + + assert(base->asTemp() && index->asTemp()); + return _block->SUBSCRIPT(base->asTemp(), index->asTemp()); +} + +V4IR::Expr *Codegen::argument(V4IR::Expr *expr) +{ + if (expr && ! expr->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + return expr; +} + +// keeps references alive, converts other expressions to temps +V4IR::Expr *Codegen::reference(V4IR::Expr *expr) +{ + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember() && !expr->asSubscript()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + return expr; +} + +V4IR::Expr *Codegen::unop(V4IR::AluOp op, V4IR::Expr *expr) +{ + Q_ASSERT(op != V4IR::OpIncrement); + Q_ASSERT(op != V4IR::OpDecrement); + + if (V4IR::Const *c = expr->asConst()) { + if (c->type == V4IR::NumberType) { + switch (op) { + case V4IR::OpNot: + return _block->CONST(V4IR::BoolType, !c->value); + case V4IR::OpUMinus: + return _block->CONST(V4IR::NumberType, -c->value); + case V4IR::OpUPlus: + return expr; + case V4IR::OpCompl: + return _block->CONST(V4IR::NumberType, ~QV4::Value::toInt32(c->value)); + case V4IR::OpIncrement: + return _block->CONST(V4IR::NumberType, c->value + 1); + case V4IR::OpDecrement: + return _block->CONST(V4IR::NumberType, c->value - 1); + default: + break; + } + } + } + if (! expr->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + assert(expr->asTemp()); + return _block->UNOP(op, expr->asTemp()); +} + +V4IR::Expr *Codegen::binop(V4IR::AluOp op, V4IR::Expr *left, V4IR::Expr *right) +{ + if (V4IR::Const *c1 = left->asConst()) { + if (V4IR::Const *c2 = right->asConst()) { + if (c1->type == V4IR::NumberType && c2->type == V4IR::NumberType) { + switch (op) { + case V4IR::OpAdd: return _block->CONST(V4IR::NumberType, c1->value + c2->value); + case V4IR::OpAnd: return _block->CONST(V4IR::BoolType, c1->value ? c2->value : 0); + case V4IR::OpBitAnd: return _block->CONST(V4IR::NumberType, int(c1->value) & int(c2->value)); + case V4IR::OpBitOr: return _block->CONST(V4IR::NumberType, int(c1->value) | int(c2->value)); + case V4IR::OpBitXor: return _block->CONST(V4IR::NumberType, int(c1->value) ^ int(c2->value)); + case V4IR::OpDiv: return _block->CONST(V4IR::NumberType, c1->value / c2->value); + case V4IR::OpEqual: return _block->CONST(V4IR::BoolType, c1->value == c2->value); + case V4IR::OpNotEqual: return _block->CONST(V4IR::BoolType, c1->value != c2->value); + case V4IR::OpStrictEqual: return _block->CONST(V4IR::BoolType, c1->value == c2->value); + case V4IR::OpStrictNotEqual: return _block->CONST(V4IR::BoolType, c1->value != c2->value); + case V4IR::OpGe: return _block->CONST(V4IR::BoolType, c1->value >= c2->value); + case V4IR::OpGt: return _block->CONST(V4IR::BoolType, c1->value > c2->value); + case V4IR::OpLe: return _block->CONST(V4IR::BoolType, c1->value <= c2->value); + case V4IR::OpLt: return _block->CONST(V4IR::BoolType, c1->value < c2->value); + case V4IR::OpLShift: return _block->CONST(V4IR::NumberType, QV4::Value::toInt32(c1->value) << (QV4::Value::toUInt32(c2->value) & 0x1f)); + case V4IR::OpMod: return _block->CONST(V4IR::NumberType, std::fmod(c1->value, c2->value)); + case V4IR::OpMul: return _block->CONST(V4IR::NumberType, c1->value * c2->value); + case V4IR::OpOr: return _block->CONST(V4IR::NumberType, c1->value ? c1->value : c2->value); + case V4IR::OpRShift: return _block->CONST(V4IR::NumberType, QV4::Value::toInt32(c1->value) >> (QV4::Value::toUInt32(c2->value) & 0x1f)); + case V4IR::OpSub: return _block->CONST(V4IR::NumberType, c1->value - c2->value); + case V4IR::OpURShift: return _block->CONST(V4IR::NumberType,QV4::Value::toUInt32(c1->value) >> (QV4::Value::toUInt32(c2->value) & 0x1f)); + + case V4IR::OpInstanceof: + case V4IR::OpIn: + break; + + case V4IR::OpIfTrue: // unary ops + case V4IR::OpNot: + case V4IR::OpUMinus: + case V4IR::OpUPlus: + case V4IR::OpCompl: + case V4IR::OpIncrement: + case V4IR::OpDecrement: + case V4IR::OpInvalid: + break; + } + } + } + } else if (op == V4IR::OpAdd) { + if (V4IR::String *s1 = left->asString()) { + if (V4IR::String *s2 = right->asString()) { + return _block->STRING(_function->newString(*s1->value + *s2->value)); + } + } + } + + if (!left->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + if (!right->asTemp()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), right); + right = _block->TEMP(t); + } + + assert(left->asTemp()); + assert(right->asTemp()); + + return _block->BINOP(op, left, right); +} + +V4IR::Expr *Codegen::call(V4IR::Expr *base, V4IR::ExprList *args) +{ + base = reference(base); + return _block->CALL(base, args); +} + +void Codegen::move(V4IR::Expr *target, V4IR::Expr *source, V4IR::AluOp op) +{ + assert(target->isLValue()); + + // TODO: verify the rest of the function for when op == OpInvalid + if (op != V4IR::OpInvalid) { + move(target, binop(op, target, source), V4IR::OpInvalid); + return; + } + + if (!source->asTemp() && !source->asConst() && (op != V4IR::OpInvalid || ! target->asTemp())) { + unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), source); + source = _block->TEMP(t); + } + if (source->asConst() && (!target->asTemp() || op != V4IR::OpInvalid)) { + unsigned t = _block->newTemp(); + _block->MOVE(_block->TEMP(t), source); + source = _block->TEMP(t); + } + + _block->MOVE(target, source, op); +} + +void Codegen::cjump(V4IR::Expr *cond, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse) +{ + if (! (cond->asTemp() || cond->asBinop())) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), cond); + cond = _block->TEMP(t); + } + _block->CJUMP(cond, iftrue, iffalse); +} + +void Codegen::accept(Node *node) +{ + if (node) + node->accept(this); +} + +void Codegen::statement(Statement *ast) +{ + _block->nextLocation = ast->firstSourceLocation(); + accept(ast); +} + +void Codegen::statement(ExpressionNode *ast) +{ + if (! ast) { + return; + } else { + Result r(nx); + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + if (r.format == ex) { + if (r->asCall()) { + _block->EXP(*r); // the nest nx representation for calls is EXP(CALL(c..)) + } else if (r->asTemp()) { + // there is nothing to do + } else { + unsigned t = _block->newTemp(); + move(_block->TEMP(t), *r); + } + } + } +} + +void Codegen::condition(ExpressionNode *ast, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse) +{ + if (ast) { + Result r(iftrue, iffalse); + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + if (r.format == ex) { + cjump(*r, r.iftrue, r.iffalse); + } + } +} + +Codegen::Result Codegen::expression(ExpressionNode *ast) +{ + Result r; + if (ast) { + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + } + return r; +} + +QString Codegen::propertyName(PropertyName *ast) +{ + QString p; + if (ast) { + qSwap(_property, p); + accept(ast); + qSwap(_property, p); + } + return p; +} + +Codegen::Result Codegen::sourceElement(SourceElement *ast) +{ + Result r(nx); + if (ast) { + qSwap(_expr, r); + accept(ast); + qSwap(_expr, r); + } + return r; +} + +Codegen::UiMember Codegen::uiObjectMember(UiObjectMember *ast) +{ + UiMember m; + if (ast) { + qSwap(_uiMember, m); + accept(ast); + qSwap(_uiMember, m); + } + return m; +} + +void Codegen::functionBody(FunctionBody *ast) +{ + if (ast) + sourceElements(ast->elements); +} + +void Codegen::program(Program *ast) +{ + if (ast) { + sourceElements(ast->elements); + } +} + +void Codegen::sourceElements(SourceElements *ast) +{ + for (SourceElements *it = ast; it; it = it->next) { + sourceElement(it->element); + } +} + +void Codegen::variableDeclaration(VariableDeclaration *ast) +{ + V4IR::Expr *initializer = 0; + if (!ast->expression) + return; + Result expr = expression(ast->expression); + assert(expr.code); + initializer = *expr; + + if (! _env->parent || _function->insideWithOrCatch) { + // it's global code. + move(_block->NAME(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), initializer); + } else { + const int index = _env->findMember(ast->name.toString()); + assert(index != -1); + move(_block->LOCAL(index, 0), initializer); + } +} + +void Codegen::variableDeclarationList(VariableDeclarationList *ast) +{ + for (VariableDeclarationList *it = ast; it; it = it->next) { + variableDeclaration(it->declaration); + } +} + + +bool Codegen::visit(ArgumentList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseBlock *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseClause *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(CaseClauses *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Catch *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(DefaultClause *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(ElementList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Elision *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Finally *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(FormalParameterList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(FunctionBody *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Program *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyAssignmentList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyNameAndValue *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(PropertyGetterSetter *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(SourceElements *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(StatementList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiArrayMemberList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiImport *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiImportList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiObjectInitializer *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiObjectMemberList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiParameterList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiProgram *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(UiQualifiedId *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(VariableDeclaration *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(VariableDeclarationList *) +{ + assert(!"unreachable"); + return false; +} + +bool Codegen::visit(Expression *ast) +{ + statement(ast->left); + accept(ast->right); + return false; +} + +bool Codegen::visit(ArrayLiteral *ast) +{ + V4IR::ExprList *args = 0; + V4IR::ExprList *current = 0; + for (ElementList *it = ast->elements; it; it = it->next) { + for (Elision *elision = it->elision; elision; elision = elision->next) { + V4IR::ExprList *arg = _function->New<V4IR::ExprList>(); + if (!current) { + args = arg; + } else { + current->next = arg; + } + current = arg; + current->expr = _block->CONST(V4IR::MissingType, 0); + } + Result expr = expression(it->expression); + + V4IR::ExprList *arg = _function->New<V4IR::ExprList>(); + if (!current) { + args = arg; + } else { + current->next = arg; + } + current = arg; + + V4IR::Expr *exp = *expr; + if (exp->asTemp() || exp->asConst()) { + current->expr = exp; + } else { + unsigned value = _block->newTemp(); + move(_block->TEMP(value), exp); + current->expr = _block->TEMP(value); + } + } + for (Elision *elision = ast->elision; elision; elision = elision->next) { + V4IR::ExprList *arg = _function->New<V4IR::ExprList>(); + if (!current) { + args = arg; + } else { + current->next = arg; + } + current = arg; + current->expr = _block->CONST(V4IR::MissingType, 0); + } + + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), _block->CALL(_block->NAME(V4IR::Name::builtin_define_array, 0, 0), args)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(ArrayMemberExpression *ast) +{ + Result base = expression(ast->base); + Result index = expression(ast->expression); + _expr.code = subscript(*base, *index); + return false; +} + +static V4IR::AluOp baseOp(int op) +{ + switch ((QSOperator::Op) op) { + case QSOperator::InplaceAnd: return V4IR::OpBitAnd; + case QSOperator::InplaceSub: return V4IR::OpSub; + case QSOperator::InplaceDiv: return V4IR::OpDiv; + case QSOperator::InplaceAdd: return V4IR::OpAdd; + case QSOperator::InplaceLeftShift: return V4IR::OpLShift; + case QSOperator::InplaceMod: return V4IR::OpMod; + case QSOperator::InplaceMul: return V4IR::OpMul; + case QSOperator::InplaceOr: return V4IR::OpBitOr; + case QSOperator::InplaceRightShift: return V4IR::OpRShift; + case QSOperator::InplaceURightShift: return V4IR::OpURShift; + case QSOperator::InplaceXor: return V4IR::OpBitXor; + default: return V4IR::OpInvalid; + } +} + +bool Codegen::visit(BinaryExpression *ast) +{ + if (ast->op == QSOperator::And) { + if (_expr.accept(cx)) { + V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); + condition(ast->left, iftrue, _expr.iffalse); + _block = iftrue; + condition(ast->right, _expr.iftrue, _expr.iffalse); + } else { + V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); + + const unsigned r = _block->newTemp(); + + move(_block->TEMP(r), *expression(ast->left)); + cjump(_block->TEMP(r), iftrue, endif); + _block = iftrue; + move(_block->TEMP(r), *expression(ast->right)); + _block->JUMP(endif); + + _expr.code = _block->TEMP(r); + _block = endif; + } + return false; + } else if (ast->op == QSOperator::Or) { + if (_expr.accept(cx)) { + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + condition(ast->left, _expr.iftrue, iffalse); + _block = iffalse; + condition(ast->right, _expr.iftrue, _expr.iffalse); + } else { + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); + + const unsigned r = _block->newTemp(); + move(_block->TEMP(r), *expression(ast->left)); + cjump(_block->TEMP(r), endif, iffalse); + _block = iffalse; + move(_block->TEMP(r), *expression(ast->right)); + _block->JUMP(endif); + + _block = endif; + _expr.code = _block->TEMP(r); + } + return false; + } + + V4IR::Expr* left = *expression(ast->left); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()); + + switch (ast->op) { + case QSOperator::Or: + case QSOperator::And: + break; + + case QSOperator::Assign: { + V4IR::Expr* right = *expression(ast->right); + if (! (left->asTemp() || left->asName() || left->asSubscript() || left->asMember())) + throwReferenceError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of assignment operator is not an lvalue")); + + if (_expr.accept(nx)) { + move(left, right); + } else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), right); + move(left, _block->TEMP(t)); + _expr.code = _block->TEMP(t); + } + break; + } + + case QSOperator::InplaceAnd: + case QSOperator::InplaceSub: + case QSOperator::InplaceDiv: + case QSOperator::InplaceAdd: + case QSOperator::InplaceLeftShift: + case QSOperator::InplaceMod: + case QSOperator::InplaceMul: + case QSOperator::InplaceOr: + case QSOperator::InplaceRightShift: + case QSOperator::InplaceURightShift: + case QSOperator::InplaceXor: { + V4IR::Expr* right = *expression(ast->right); + if (!left->isLValue()) + throwSyntaxError(ast->operatorToken, QCoreApplication::translate("qv4codegen", "left-hand side of inplace operator is not an lvalue")); + + if (_expr.accept(nx)) { + move(left, right, baseOp(ast->op)); + } else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), right); + move(left, _block->TEMP(t), baseOp(ast->op)); + _expr.code = left; + } + break; + } + + case QSOperator::In: + case QSOperator::InstanceOf: + case QSOperator::Equal: + case QSOperator::NotEqual: + case QSOperator::Ge: + case QSOperator::Gt: + case QSOperator::Le: + case QSOperator::Lt: + case QSOperator::StrictEqual: + case QSOperator::StrictNotEqual: { + if (!left->asTemp() && !left->asConst()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + V4IR::Expr* right = *expression(ast->right); + + if (_expr.accept(cx)) { + cjump(binop(V4IR::binaryOperator(ast->op), left, right), _expr.iftrue, _expr.iffalse); + } else { + V4IR::Expr *e = binop(V4IR::binaryOperator(ast->op), left, right); + if (e->asConst() || e->asString()) + _expr.code = e; + else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), e); + _expr.code = _block->TEMP(t); + } + } + break; + } + + case QSOperator::Add: + case QSOperator::BitAnd: + case QSOperator::BitOr: + case QSOperator::BitXor: + case QSOperator::Div: + case QSOperator::LShift: + case QSOperator::Mod: + case QSOperator::Mul: + case QSOperator::RShift: + case QSOperator::Sub: + case QSOperator::URShift: { + if (!left->asTemp() && !left->asConst()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), left); + left = _block->TEMP(t); + } + + V4IR::Expr* right = *expression(ast->right); + + V4IR::Expr *e = binop(V4IR::binaryOperator(ast->op), left, right); + if (e->asConst() || e->asString()) + _expr.code = e; + else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), e); + _expr.code = _block->TEMP(t); + } + break; + } + + } // switch + + return false; +} + +bool Codegen::visit(CallExpression *ast) +{ + Result base = expression(ast->base); + V4IR::ExprList *args = 0, **args_it = &args; + for (ArgumentList *it = ast->arguments; it; it = it->next) { + Result arg = expression(it->expression); + V4IR::Expr *actual = argument(*arg); + *args_it = _function->New<V4IR::ExprList>(); + (*args_it)->init(actual); + args_it = &(*args_it)->next; + } + _expr.code = call(*base, args); + return false; +} + +bool Codegen::visit(ConditionalExpression *ast) +{ + V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); + + const unsigned t = _block->newTemp(); + + condition(ast->expression, iftrue, iffalse); + + _block = iftrue; + move(_block->TEMP(t), *expression(ast->ok)); + _block->JUMP(endif); + + _block = iffalse; + move(_block->TEMP(t), *expression(ast->ko)); + _block->JUMP(endif); + + _block = endif; + + _expr.code = _block->TEMP(t); + + return false; +} + +bool Codegen::visit(DeleteExpression *ast) +{ + V4IR::Expr* expr = *expression(ast->expression); + // Temporaries cannot be deleted + if (expr->asTemp() && expr->asTemp()->index < _env->members.size()) { + // Trying to delete a function argument might throw. + if (_function->isStrict && expr->asTemp()->index < 0) + throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); + _expr.code = _block->CONST(V4IR::BoolType, 0); + return false; + } + if (_function->isStrict && expr->asName()) + throwSyntaxError(ast->deleteToken, "Delete of an unqualified identifier in strict mode."); + + // [[11.4.1]] Return true if it's not a reference + if (expr->asConst() || expr->asString()) { + _expr.code = _block->CONST(V4IR::BoolType, 1); + return false; + } + + // Return values from calls are also not a reference, but we have to + // perform the call to allow for side effects. + if (expr->asCall()) { + _block->EXP(expr); + _expr.code = _block->CONST(V4IR::BoolType, 1); + return false; + } + if (expr->asTemp() && expr->asTemp()->index >= _env->members.size()) { + _expr.code = _block->CONST(V4IR::BoolType, 1); + return false; + } + + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(reference(expr)); + _expr.code = call(_block->NAME(V4IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args); + return false; +} + +bool Codegen::visit(FalseLiteral *) +{ + if (_expr.accept(cx)) { + _block->JUMP(_expr.iffalse); + } else { + _expr.code = _block->CONST(V4IR::BoolType, 0); + } + return false; +} + +bool Codegen::visit(FieldMemberExpression *ast) +{ + Result base = expression(ast->base); + _expr.code = member(*base, _function->newString(ast->name.toString())); + return false; +} + +bool Codegen::visit(FunctionExpression *ast) +{ + V4IR::Function *function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); + _expr.code = _block->CLOSURE(function); + return false; +} + +V4IR::Expr *Codegen::identifier(const QString &name, int line, int col) +{ + uint scope = 0; + Environment *e = _env; + V4IR::Function *f = _function; + + while (f && e->parent) { + if ((f->usesArgumentsObject && name == "arguments") || (!f->isStrict && f->hasDirectEval) || f->insideWithOrCatch || (f->isNamedExpression && f->name == name)) + break; + int index = e->findMember(name); + assert (index < e->members.size()); + if (index != -1) { + return _block->LOCAL(index, scope); + } + const int argIdx = f->indexOfArgument(&name); + if (argIdx != -1) + return _block->ARG(argIdx, scope); + ++scope; + e = e->parent; + f = f->outer; + } + + if (!e->parent && (!f || !f->insideWithOrCatch) && _mode != EvalCode && _mode != QmlBinding && (!f || f->name != name)) + return _block->GLOBALNAME(name, line, col); + + // global context or with. Lookup by name + return _block->NAME(name, line, col); + +} + +bool Codegen::visit(IdentifierExpression *ast) +{ + _expr.code = identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn); + return false; +} + +bool Codegen::visit(NestedExpression *ast) +{ + accept(ast->expression); + return false; +} + +bool Codegen::visit(NewExpression *ast) +{ + Result base = expression(ast->expression); + V4IR::Expr *expr = *base; + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + _expr.code = _block->NEW(expr, 0); + return false; +} + +bool Codegen::visit(NewMemberExpression *ast) +{ + Result base = expression(ast->base); + V4IR::Expr *expr = *base; + if (expr && !expr->asTemp() && !expr->asName() && !expr->asMember()) { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), expr); + expr = _block->TEMP(t); + } + + V4IR::ExprList *args = 0, **args_it = &args; + for (ArgumentList *it = ast->arguments; it; it = it->next) { + Result arg = expression(it->expression); + V4IR::Expr *actual = argument(*arg); + *args_it = _function->New<V4IR::ExprList>(); + (*args_it)->init(actual); + args_it = &(*args_it)->next; + } + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), _block->NEW(expr, args)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(NotExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned r = _block->newTemp(); + move(_block->TEMP(r), unop(V4IR::OpNot, *expr)); + _expr.code = _block->TEMP(r); + return false; +} + +bool Codegen::visit(NullExpression *) +{ + if (_expr.accept(cx)) _block->JUMP(_expr.iffalse); + else _expr.code = _block->CONST(V4IR::NullType, 0); + + return false; +} + +bool Codegen::visit(NumericLiteral *ast) +{ + if (_expr.accept(cx)) { + if (ast->value) _block->JUMP(_expr.iftrue); + else _block->JUMP(_expr.iffalse); + } else { + _expr.code = _block->CONST(V4IR::NumberType, ast->value); + } + return false; +} + +struct ObjectPropertyValue { + V4IR::Expr *value; + V4IR::Function *getter; + V4IR::Function *setter; +}; + +bool Codegen::visit(ObjectLiteral *ast) +{ + QMap<QString, ObjectPropertyValue> valueMap; + + for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { + if (PropertyNameAndValue *nv = AST::cast<AST::PropertyNameAndValue *>(it->assignment)) { + QString name = propertyName(nv->name); + Result value = expression(nv->value); + ObjectPropertyValue &v = valueMap[name]; + if (v.getter || v.setter || (_function->isStrict && v.value)) + throwSyntaxError(nv->lastSourceLocation(), + QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name)); + + valueMap[name].value = *value; + } else if (PropertyGetterSetter *gs = AST::cast<AST::PropertyGetterSetter *>(it->assignment)) { + QString name = propertyName(gs->name); + V4IR::Function *function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0); + ObjectPropertyValue &v = valueMap[name]; + if (v.value || + (gs->type == PropertyGetterSetter::Getter && v.getter) || + (gs->type == PropertyGetterSetter::Setter && v.setter)) + throwSyntaxError(gs->lastSourceLocation(), + QCoreApplication::translate("qv4codegen", "Illegal duplicate key '%1' in object literal").arg(name)); + if (gs->type == PropertyGetterSetter::Getter) + v.getter = function; + else + v.setter = function; + } else { + Q_UNREACHABLE(); + } + } + + V4IR::ExprList *args = 0; + + if (!valueMap.isEmpty()) { + V4IR::ExprList *current; + for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(); it != valueMap.end(); ) { + if (QV4::String(0, it.key()).asArrayIndex() != UINT_MAX) { + ++it; + continue; + } + + if (!args) { + args = _function->New<V4IR::ExprList>(); + current = args; + } else { + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + } + + current->expr = _block->NAME(it.key(), 0, 0); + + if (it->value) { + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + current->expr = _block->CONST(V4IR::BoolType, true); + + unsigned value = _block->newTemp(); + move(_block->TEMP(value), it->value); + + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + current->expr = _block->TEMP(value); + } else { + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + current->expr = _block->CONST(V4IR::BoolType, false); + + unsigned getter = _block->newTemp(); + unsigned setter = _block->newTemp(); + move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(V4IR::UndefinedType, 0)); + move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(V4IR::UndefinedType, 0)); + + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + current->expr = _block->TEMP(getter); + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + current->expr = _block->TEMP(setter); + } + + it = valueMap.erase(it); + } + } + + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), _block->CALL(_block->NAME(V4IR::Name::builtin_define_object_literal, + ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), args)); + + // What's left are array entries + if (!valueMap.isEmpty()) { + unsigned value = 0; + unsigned getter = 0; + unsigned setter = 0; + for (QMap<QString, ObjectPropertyValue>::const_iterator it = valueMap.constBegin(); it != valueMap.constEnd(); ++it) { + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + V4IR::ExprList *current = args; + current->expr = _block->TEMP(t); + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + current->expr = _block->NAME(it.key(), 0, 0); + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + + if (it->value) { + if (!value) + value = _block->newTemp(); + move(_block->TEMP(value), it->value); + // __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx) + current->expr = _block->TEMP(value); + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_define_property, 0, 0), args)); + } else { + if (!getter) { + getter = _block->newTemp(); + setter = _block->newTemp(); + } + move(_block->TEMP(getter), it->getter ? _block->CLOSURE(it->getter) : _block->CONST(V4IR::UndefinedType, 0)); + move(_block->TEMP(setter), it->setter ? _block->CLOSURE(it->setter) : _block->CONST(V4IR::UndefinedType, 0)); + + + // __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx); + current->expr = _block->TEMP(getter); + current->next = _function->New<V4IR::ExprList>(); + current = current->next; + current->expr = _block->TEMP(setter); + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_define_getter_setter, 0, 0), args)); + } + } + } + + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(PostDecrementExpression *ast) +{ + Result expr = expression(ast->base); + if (!expr->isLValue()) + throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); + + if (_expr.accept(nx)) { + move(*expr, binop(V4IR::OpSub, *expr, _block->CONST(V4IR::NumberType, 1))); + } else { + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(*expr); + _expr.code = call(_block->NAME(V4IR::Name::builtin_postdecrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); + } + return false; +} + +bool Codegen::visit(PostIncrementExpression *ast) +{ + Result expr = expression(ast->base); + if (!expr->isLValue()) + throwReferenceError(ast->base->lastSourceLocation(), "Invalid left-hand side expression in postfix operation"); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); + + if (_expr.accept(nx)) { + move(*expr, binop(V4IR::OpAdd, unop(V4IR::OpUPlus, *expr), _block->CONST(V4IR::NumberType, 1))); + } else { + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(*expr); + _expr.code = call(_block->NAME(V4IR::Name::builtin_postincrement, ast->lastSourceLocation().startLine, ast->lastSourceLocation().startColumn), args); + } + return false; +} + +bool Codegen::visit(PreDecrementExpression *ast) +{ + Result expr = expression(ast->expression); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken); + V4IR::Expr *op = binop(V4IR::OpSub, *expr, _block->CONST(V4IR::NumberType, 1)); + if (_expr.accept(nx)) { + move(*expr, op); + } else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), op); + move(*expr, _block->TEMP(t)); + _expr.code = _block->TEMP(t); + } + return false; +} + +bool Codegen::visit(PreIncrementExpression *ast) +{ + Result expr = expression(ast->expression); + throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken); + V4IR::Expr *op = binop(V4IR::OpAdd, unop(V4IR::OpUPlus, *expr), _block->CONST(V4IR::NumberType, 1)); + if (_expr.accept(nx)) { + move(*expr, op); + } else { + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), op); + move(*expr, _block->TEMP(t)); + _expr.code = _block->TEMP(t); + } + return false; +} + +bool Codegen::visit(RegExpLiteral *ast) +{ + _expr.code = _block->REGEXP(_function->newString(ast->pattern.toString()), ast->flags); + return false; +} + +bool Codegen::visit(StringLiteral *ast) +{ + _expr.code = _block->STRING(_function->newString(ast->value.toString())); + return false; +} + +bool Codegen::visit(ThisExpression *ast) +{ + _expr.code = _block->NAME(QStringLiteral("this"), ast->thisToken.startLine, ast->thisToken.startColumn); + return false; +} + +bool Codegen::visit(TildeExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), unop(V4IR::OpCompl, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(TrueLiteral *) +{ + if (_expr.accept(cx)) { + _block->JUMP(_expr.iftrue); + } else { + _expr.code = _block->CONST(V4IR::BoolType, 1); + } + return false; +} + +bool Codegen::visit(TypeOfExpression *ast) +{ + Result expr = expression(ast->expression); + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(reference(*expr)); + _expr.code = call(_block->NAME(V4IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args); + return false; +} + +bool Codegen::visit(UnaryMinusExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), unop(V4IR::OpUMinus, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(UnaryPlusExpression *ast) +{ + Result expr = expression(ast->expression); + const unsigned t = _block->newTemp(); + move(_block->TEMP(t), unop(V4IR::OpUPlus, *expr)); + _expr.code = _block->TEMP(t); + return false; +} + +bool Codegen::visit(VoidExpression *ast) +{ + statement(ast->expression); + _expr.code = _block->CONST(V4IR::UndefinedType, 0); + return false; +} + +bool Codegen::visit(FunctionDeclaration * ast) +{ + if (_mode == QmlBinding) + move(_block->TEMP(_returnAddress), _block->NAME(ast->name.toString(), 0, 0)); + _expr.accept(nx); + return false; +} + +V4IR::Function *Codegen::defineFunction(const QString &name, AST::Node *ast, + AST::FormalParameterList *formals, + AST::SourceElements *body, Mode mode, + const QStringList &inheritedLocals) +{ + qSwap(_mode, mode); // enter function code. + Loop *loop = 0; + qSwap(_loop, loop); + + ScopeAndFinally *scopeAndFinally = 0; + + enterEnvironment(ast); + V4IR::Function *function = _module->newFunction(name, _function); + function->sourceFile = _fileName; + + V4IR::BasicBlock *entryBlock = function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *exitBlock = function->newBasicBlock(groupStartBlock(), V4IR::Function::DontInsertBlock); + V4IR::BasicBlock *throwBlock = function->newBasicBlock(groupStartBlock()); + function->hasDirectEval = _env->hasDirectEval; + function->usesArgumentsObject = (_env->usesArgumentsObject == Environment::ArgumentsObjectUsed); + function->maxNumberOfArguments = _env->maxNumberOfArguments; + function->isStrict = _env->isStrict; + function->isNamedExpression = _env->isNamedFunctionExpression; + + // variables in global code are properties of the global context object, not locals as with other functions. + if (_mode == FunctionCode) { + unsigned t = 0; + for (Environment::MemberMap::iterator it = _env->members.begin(); it != _env->members.end(); ++it) { + const QString &local = it.key(); + function->LOCAL(local); + (*it).index = t; + entryBlock->MOVE(entryBlock->LOCAL(t, 0), entryBlock->CONST(V4IR::UndefinedType, 0)); + ++t; + } + } else { + if (!_env->isStrict) { + foreach (const QString &inheritedLocal, inheritedLocals) { + function->LOCAL(inheritedLocal); + unsigned tempIndex = entryBlock->newTemp(); + Environment::Member member = { Environment::UndefinedMember, + static_cast<int>(tempIndex), 0 }; + _env->members.insert(inheritedLocal, member); + } + } + + V4IR::ExprList *args = 0; + for (Environment::MemberMap::const_iterator it = _env->members.constBegin(); it != _env->members.constEnd(); ++it) { + const QString &local = it.key(); + V4IR::ExprList *next = function->New<V4IR::ExprList>(); + next->expr = entryBlock->NAME(local, 0, 0); + next->next = args; + args = next; + } + if (args) { + V4IR::ExprList *next = function->New<V4IR::ExprList>(); + next->expr = entryBlock->CONST(V4IR::BoolType, (mode == EvalCode || mode == QmlBinding)); + next->next = args; + args = next; + + entryBlock->EXP(entryBlock->CALL(entryBlock->NAME(V4IR::Name::builtin_declare_vars, 0, 0), args)); + } + } + + unsigned returnAddress = entryBlock->newTemp(); + + entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(V4IR::UndefinedType, 0)); + exitBlock->RET(exitBlock->TEMP(returnAddress)); + V4IR::ExprList *throwArgs = function->New<V4IR::ExprList>(); + throwArgs->expr = throwBlock->TEMP(returnAddress); + throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(V4IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); + throwBlock->JUMP(exitBlock); + + qSwap(_function, function); + qSwap(_block, entryBlock); + qSwap(_exitBlock, exitBlock); + qSwap(_throwBlock, throwBlock); + qSwap(_returnAddress, returnAddress); + qSwap(_scopeAndFinally, scopeAndFinally); + + for (FormalParameterList *it = formals; it; it = it->next) { + _function->RECEIVE(it->name.toString()); + } + + foreach (const Environment::Member &member, _env->members) { + if (member.function) { + V4IR::Function *function = defineFunction(member.function->name.toString(), member.function, member.function->formals, + member.function->body ? member.function->body->elements : 0); + if (! _env->parent) { + move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn), + _block->CLOSURE(function)); + } else { + assert(member.index >= 0); + move(_block->LOCAL(member.index, 0), _block->CLOSURE(function)); + } + } + } + + sourceElements(body); + + _function->insertBasicBlock(_exitBlock); + + _block->JUMP(_exitBlock); + + qSwap(_function, function); + qSwap(_block, entryBlock); + qSwap(_exitBlock, exitBlock); + qSwap(_throwBlock, throwBlock); + qSwap(_returnAddress, returnAddress); + qSwap(_scopeAndFinally, scopeAndFinally); + qSwap(_loop, loop); + + leaveEnvironment(); + + qSwap(_mode, mode); + + return function; +} + +bool Codegen::visit(IdentifierPropertyName *ast) +{ + _property = ast->id.toString(); + return false; +} + +bool Codegen::visit(NumericLiteralPropertyName *ast) +{ + _property = QString::number(ast->id, 'g', 16); + return false; +} + +bool Codegen::visit(StringLiteralPropertyName *ast) +{ + _property = ast->id.toString(); + return false; +} + +bool Codegen::visit(FunctionSourceElement *ast) +{ + statement(ast->declaration); + return false; +} + +bool Codegen::visit(StatementSourceElement *ast) +{ + statement(ast->statement); + return false; +} + +bool Codegen::visit(Block *ast) +{ + for (StatementList *it = ast->statements; it; it = it->next) { + statement(it->statement); + } + return false; +} + +bool Codegen::visit(BreakStatement *ast) +{ + if (!_loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Break outside of loop")); + Loop *loop = 0; + if (ast->label.isEmpty()) + loop = _loop; + else { + for (loop = _loop; loop; loop = loop->parent) { + if (loop->labelledStatement && loop->labelledStatement->label == ast->label) + break; + } + if (!loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); + } + unwindException(loop->scopeAndFinally); + _block->JUMP(loop->breakBlock); + return false; +} + +bool Codegen::visit(ContinueStatement *ast) +{ + Loop *loop = 0; + if (ast->label.isEmpty()) { + for (loop = _loop; loop; loop = loop->parent) { + if (loop->continueBlock) + break; + } + } else { + for (loop = _loop; loop; loop = loop->parent) { + if (loop->labelledStatement && loop->labelledStatement->label == ast->label) { + if (!loop->continueBlock) + loop = 0; + break; + } + } + if (!loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "Undefined label '%1'").arg(ast->label.toString())); + } + if (!loop) + throwSyntaxError(ast->lastSourceLocation(), QCoreApplication::translate("qv4codegen", "continue outside of loop")); + unwindException(loop->scopeAndFinally); + _block->JUMP(loop->continueBlock); + return false; +} + +bool Codegen::visit(DebuggerStatement *) +{ + Q_UNIMPLEMENTED(); + return false; +} + +bool Codegen::visit(DoWhileStatement *ast) +{ + V4IR::BasicBlock *loopbody = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *loopcond = _function->newBasicBlock(loopbody); + V4IR::BasicBlock *loopend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, loopbody, loopend, loopcond); + + _block->JUMP(loopbody); + + _block = loopbody; + statement(ast->statement); + _block->JUMP(loopcond); + + _block = loopcond; + condition(ast->expression, loopbody, loopend); + + _block = loopend; + + leaveLoop(); + + return false; +} + +bool Codegen::visit(EmptyStatement *) +{ + return false; +} + +bool Codegen::visit(ExpressionStatement *ast) +{ + if (_mode == EvalCode || _mode == QmlBinding) { + Result e = expression(ast->expression); + if (*e) + move(_block->TEMP(_returnAddress), *e); + } else { + statement(ast->expression); + } + return false; +} + +bool Codegen::visit(ForEachStatement *ast) +{ + V4IR::BasicBlock *foreachin = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *foreachbody = _function->newBasicBlock(foreachin); + V4IR::BasicBlock *foreachend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, foreachin, foreachend, foreachin); + + int objectToIterateOn = _block->newTemp(); + move(_block->TEMP(objectToIterateOn), *expression(ast->expression)); + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(_block->TEMP(objectToIterateOn)); + + int iterator = _block->newTemp(); + move(_block->TEMP(iterator), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_iterator_object, 0, 0), args)); + + _block->JUMP(foreachin); + + _block = foreachbody; + int temp = _block->newTemp(); + move(*expression(ast->initialiser), _block->TEMP(temp)); + statement(ast->statement); + _block->JUMP(foreachin); + + _block = foreachin; + + args = _function->New<V4IR::ExprList>(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(temp), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_next_property_name, 0, 0), args)); + int null = _block->newTemp(); + move(_block->TEMP(null), _block->CONST(V4IR::NullType, 0)); + cjump(_block->BINOP(V4IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend); + _block = foreachend; + + leaveLoop(); + return false; +} + +bool Codegen::visit(ForStatement *ast) +{ + V4IR::BasicBlock *forcond = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *forbody = _function->newBasicBlock(forcond); + V4IR::BasicBlock *forstep = _function->newBasicBlock(forcond); + V4IR::BasicBlock *forend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, forcond, forend, forstep); + + statement(ast->initialiser); + _block->JUMP(forcond); + + _block = forcond; + if (ast->condition) + condition(ast->condition, forbody, forend); + else + _block->JUMP(forbody); + + _block = forbody; + statement(ast->statement); + _block->JUMP(forstep); + + _block = forstep; + statement(ast->expression); + _block->JUMP(forcond); + + _block = forend; + + leaveLoop(); + + return false; +} + +bool Codegen::visit(IfStatement *ast) +{ + V4IR::BasicBlock *iftrue = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock(groupStartBlock()) : 0; + V4IR::BasicBlock *endif = _function->newBasicBlock(groupStartBlock()); + condition(ast->expression, iftrue, ast->ko ? iffalse : endif); + + _block = iftrue; + statement(ast->ok); + _block->JUMP(endif); + + if (ast->ko) { + _block = iffalse; + statement(ast->ko); + _block->JUMP(endif); + } + + _block = endif; + + return false; +} + +bool Codegen::visit(LabelledStatement *ast) +{ + // check that no outer loop contains the label + Loop *l = _loop; + while (l) { + if (l->labelledStatement->label == ast->label) { + QString error = QString(QStringLiteral("Label '%1' has already been declared")).arg(ast->label.toString()); + throwSyntaxError(ast->firstSourceLocation(), error); + } + l = l->parent; + } + _labelledStatement = ast; + + if (AST::cast<AST::SwitchStatement *>(ast->statement) || + AST::cast<AST::WhileStatement *>(ast->statement) || + AST::cast<AST::DoWhileStatement *>(ast->statement) || + AST::cast<AST::ForStatement *>(ast->statement) || + AST::cast<AST::ForEachStatement *>(ast->statement) || + AST::cast<AST::LocalForStatement *>(ast->statement) || + AST::cast<AST::LocalForEachStatement *>(ast->statement)) { + statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop. + } else { + V4IR::BasicBlock *breakBlock = _function->newBasicBlock(groupStartBlock()); + enterLoop(ast->statement, 0, breakBlock, /*continueBlock*/ 0); + statement(ast->statement); + _block->JUMP(breakBlock); + _block = breakBlock; + leaveLoop(); + } + + return false; +} + +bool Codegen::visit(LocalForEachStatement *ast) +{ + V4IR::BasicBlock *foreachin = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *foreachbody = _function->newBasicBlock(foreachin); + V4IR::BasicBlock *foreachend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, foreachin, foreachend, foreachin); + + variableDeclaration(ast->declaration); + + int iterator = _block->newTemp(); + move(_block->TEMP(iterator), *expression(ast->expression)); + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(iterator), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_iterator_object, 0, 0), args)); + + _block->JUMP(foreachin); + + _block = foreachbody; + int temp = _block->newTemp(); + move(identifier(ast->declaration->name.toString()), _block->TEMP(temp)); + statement(ast->statement); + _block->JUMP(foreachin); + + _block = foreachin; + + args = _function->New<V4IR::ExprList>(); + args->init(_block->TEMP(iterator)); + move(_block->TEMP(temp), _block->CALL(_block->NAME(V4IR::Name::builtin_foreach_next_property_name, 0, 0), args)); + int null = _block->newTemp(); + move(_block->TEMP(null), _block->CONST(V4IR::NullType, 0)); + cjump(_block->BINOP(V4IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend); + _block = foreachend; + + leaveLoop(); + return false; +} + +bool Codegen::visit(LocalForStatement *ast) +{ + V4IR::BasicBlock *forcond = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *forbody = _function->newBasicBlock(forcond); + V4IR::BasicBlock *forstep = _function->newBasicBlock(forcond); + V4IR::BasicBlock *forend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, forcond, forend, forstep); + + variableDeclarationList(ast->declarations); + _block->JUMP(forcond); + + _block = forcond; + if (ast->condition) + condition(ast->condition, forbody, forend); + else + _block->JUMP(forbody); + + _block = forbody; + statement(ast->statement); + _block->JUMP(forstep); + + _block = forstep; + statement(ast->expression); + _block->JUMP(forcond); + + _block = forend; + + leaveLoop(); + + return false; +} + +bool Codegen::visit(ReturnStatement *ast) +{ + if (_mode != FunctionCode && _mode != QmlBinding) + throwSyntaxError(ast->returnToken, QCoreApplication::translate("qv4codegen", "Return statement outside of function")); + if (ast->expression) { + Result expr = expression(ast->expression); + move(_block->TEMP(_returnAddress), *expr); + } + unwindException(0); + + _block->JUMP(_exitBlock); + return false; +} + +bool Codegen::visit(SwitchStatement *ast) +{ + V4IR::BasicBlock *switchend = _function->newBasicBlock(groupStartBlock()); + + if (ast->block) { + Result lhs = expression(ast->expression); + V4IR::BasicBlock *switchcond = _function->newBasicBlock(groupStartBlock()); + _block->JUMP(switchcond); + V4IR::BasicBlock *previousBlock = 0; + + QHash<Node *, V4IR::BasicBlock *> blockMap; + + enterLoop(ast, 0, switchend, 0); + + for (CaseClauses *it = ast->block->clauses; it; it = it->next) { + CaseClause *clause = it->clause; + + _block = _function->newBasicBlock(groupStartBlock()); + blockMap[clause] = _block; + + if (previousBlock && !previousBlock->isTerminated()) + previousBlock->JUMP(_block); + + for (StatementList *it2 = clause->statements; it2; it2 = it2->next) + statement(it2->statement); + + previousBlock = _block; + } + + if (ast->block->defaultClause) { + _block = _function->newBasicBlock(groupStartBlock()); + blockMap[ast->block->defaultClause] = _block; + + if (previousBlock && !previousBlock->isTerminated()) + previousBlock->JUMP(_block); + + for (StatementList *it2 = ast->block->defaultClause->statements; it2; it2 = it2->next) + statement(it2->statement); + + previousBlock = _block; + } + + for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { + CaseClause *clause = it->clause; + + _block = _function->newBasicBlock(groupStartBlock()); + blockMap[clause] = _block; + + if (previousBlock && !previousBlock->isTerminated()) + previousBlock->JUMP(_block); + + for (StatementList *it2 = clause->statements; it2; it2 = it2->next) + statement(it2->statement); + + previousBlock = _block; + } + + leaveLoop(); + + _block->JUMP(switchend); + + _block = switchcond; + for (CaseClauses *it = ast->block->clauses; it; it = it->next) { + CaseClause *clause = it->clause; + Result rhs = expression(clause->expression); + V4IR::BasicBlock *iftrue = blockMap[clause]; + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + cjump(binop(V4IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); + _block = iffalse; + } + + for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { + CaseClause *clause = it->clause; + Result rhs = expression(clause->expression); + V4IR::BasicBlock *iftrue = blockMap[clause]; + V4IR::BasicBlock *iffalse = _function->newBasicBlock(groupStartBlock()); + cjump(binop(V4IR::OpStrictEqual, *lhs, *rhs), iftrue, iffalse); + _block = iffalse; + } + + if (ast->block->defaultClause) { + _block->JUMP(blockMap[ast->block->defaultClause]); + } + } + + _block->JUMP(switchend); + + _block = switchend; + return false; +} + +bool Codegen::visit(ThrowStatement *ast) +{ + Result expr = expression(ast->expression); + move(_block->TEMP(_returnAddress), *expr); + _block->JUMP(_throwBlock); + return false; +} + +bool Codegen::visit(TryStatement *ast) +{ + _function->hasTry = true; + + if (_function->isStrict && ast->catchExpression && + (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) + throwSyntaxError(ast->catchExpression->identifierToken, QCoreApplication::translate("qv4codegen", "Catch variable name may not be eval or arguments in strict mode")); + + V4IR::BasicBlock *tryBody = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *catchBody = _function->newBasicBlock(groupStartBlock()); + // We always need a finally body to clean up the exception handler + V4IR::BasicBlock *finallyBody = _function->newBasicBlock(groupStartBlock()); + + V4IR::BasicBlock *throwBlock = _function->newBasicBlock(groupStartBlock()); + V4IR::ExprList *throwArgs = _function->New<V4IR::ExprList>(); + throwArgs->expr = throwBlock->TEMP(_returnAddress); + throwBlock->EXP(throwBlock->CALL(throwBlock->NAME(V4IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); + throwBlock->JUMP(catchBody); + qSwap(_throwBlock, throwBlock); + + int hasException = _block->newTemp(); + move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, false)); + + // Pass the hidden "needRethrow" TEMP to the + // builtin_delete_exception_handler, in order to have those TEMPs alive for + // the duration of the exception handling block. + V4IR::ExprList *finishTryArgs = _function->New<V4IR::ExprList>(); + finishTryArgs->init(_block->TEMP(hasException)); + + ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression, finishTryArgs); + _scopeAndFinally = &tcf; + + int exception_to_rethrow = _block->newTemp(); + + _block->TRY(tryBody, catchBody, + ast->catchExpression ? ast->catchExpression->name.toString() : QString(), + _block->TEMP(exception_to_rethrow)); + + _block = tryBody; + statement(ast->statement); + _block->JUMP(finallyBody); + + _block = catchBody; + + if (ast->catchExpression) { + // check if an exception got thrown within catch. Go to finally + // and then rethrow + V4IR::BasicBlock *b = _function->newBasicBlock(groupStartBlock()); + _block->CJUMP(_block->TEMP(hasException), finallyBody, b); + _block = b; + } + + move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, true)); + + if (ast->catchExpression) { + ++_function->insideWithOrCatch; + { + ScopeAndFinally scope(_scopeAndFinally, ScopeAndFinally::CatchScope); + _scopeAndFinally = &scope; + statement(ast->catchExpression->statement); + _scopeAndFinally = scope.parent; + } + --_function->insideWithOrCatch; + move(_block->TEMP(hasException), _block->CONST(V4IR::BoolType, false)); + } + _block->JUMP(finallyBody); + + _scopeAndFinally = tcf.parent; + + qSwap(_throwBlock, throwBlock); + + V4IR::BasicBlock *after = _function->newBasicBlock(groupStartBlock()); + _block = finallyBody; + + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_finish_try, 0, 0), finishTryArgs)); + + if (ast->finallyExpression && ast->finallyExpression->statement) + statement(ast->finallyExpression->statement); + + V4IR::BasicBlock *rethrowBlock = _function->newBasicBlock(groupStartBlock()); + _block->CJUMP(_block->TEMP(hasException), rethrowBlock, after); + _block = rethrowBlock; + move(_block->TEMP(_returnAddress), _block->TEMP(exception_to_rethrow)); + _block->JUMP(_throwBlock); + + _block = after; + + return false; +} + +void Codegen::unwindException(Codegen::ScopeAndFinally *outest) +{ + int savedDepthForWidthOrCatch = _function->insideWithOrCatch; + ScopeAndFinally *scopeAndFinally = _scopeAndFinally; + qSwap(_scopeAndFinally, scopeAndFinally); + while (_scopeAndFinally != outest) { + switch (_scopeAndFinally->type) { + case ScopeAndFinally::WithScope: + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_pop_scope, 0, 0))); + // fall through + case ScopeAndFinally::CatchScope: + _scopeAndFinally = _scopeAndFinally->parent; + --_function->insideWithOrCatch; + break; + case ScopeAndFinally::TryScope: { + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_finish_try, 0, 0), _scopeAndFinally->finishTryArgs)); + ScopeAndFinally *tc = _scopeAndFinally; + _scopeAndFinally = tc->parent; + if (tc->finally && tc->finally->statement) + statement(tc->finally->statement); + break; + } + } + } + qSwap(_scopeAndFinally, scopeAndFinally); + _function->insideWithOrCatch = savedDepthForWidthOrCatch; +} + +bool Codegen::visit(VariableStatement *ast) +{ + variableDeclarationList(ast->declarations); + return false; +} + +bool Codegen::visit(WhileStatement *ast) +{ + V4IR::BasicBlock *whilecond = _function->newBasicBlock(groupStartBlock()); + V4IR::BasicBlock *whilebody = _function->newBasicBlock(whilecond); + V4IR::BasicBlock *whileend = _function->newBasicBlock(groupStartBlock()); + + enterLoop(ast, whilecond, whileend, whilecond); + + _block->JUMP(whilecond); + _block = whilecond; + condition(ast->expression, whilebody, whileend); + + _block = whilebody; + statement(ast->statement); + _block->JUMP(whilecond); + + _block = whileend; + leaveLoop(); + + return false; +} + +bool Codegen::visit(WithStatement *ast) +{ + _function->hasWith = true; + + V4IR::BasicBlock *withBlock = _function->newBasicBlock(groupStartBlock()); + + _block->JUMP(withBlock); + _block = withBlock; + int withObject = _block->newTemp(); + _block->MOVE(_block->TEMP(withObject), *expression(ast->expression)); + V4IR::ExprList *args = _function->New<V4IR::ExprList>(); + args->init(_block->TEMP(withObject)); + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_push_with_scope, 0, 0), args)); + + ++_function->insideWithOrCatch; + { + ScopeAndFinally scope(_scopeAndFinally); + _scopeAndFinally = &scope; + statement(ast->statement); + _scopeAndFinally = scope.parent; + } + --_function->insideWithOrCatch; + _block->EXP(_block->CALL(_block->NAME(V4IR::Name::builtin_pop_scope, 0, 0), 0)); + + V4IR::BasicBlock *next = _function->newBasicBlock(groupStartBlock()); + _block->JUMP(next); + _block = next; + + return false; +} + +bool Codegen::visit(UiArrayBinding *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiObjectBinding *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiObjectDefinition *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiPublicMember *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiScriptBinding *) +{ + assert(!"not implemented"); + return false; +} + +bool Codegen::visit(UiSourceElement *) +{ + assert(!"not implemented"); + return false; +} + +void Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(V4IR::Expr *expr, const SourceLocation& loc) +{ + if (!_env->isStrict) + return; + V4IR::Name *n = expr->asName(); + if (!n) + return; + if (*n->id == QLatin1String("eval") || *n->id == QLatin1String("arguments")) + throwSyntaxError(loc, QCoreApplication::translate("qv4codegen", "Variable name may not be eval or arguments in strict mode")); +} + +void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) +{ + QV4::DiagnosticMessage *msg = new QV4::DiagnosticMessage; + msg->fileName = _fileName; + msg->offset = loc.begin(); + msg->startLine = loc.startLine; + msg->startColumn = loc.startColumn; + msg->message = detail; + if (_context) + _context->throwSyntaxError(msg); + else if (_errorHandler) + _errorHandler->syntaxError(msg); + else + Q_ASSERT(!"No error handler available."); +} + +void Codegen::throwReferenceError(const SourceLocation &loc, const QString &detail) +{ + if (_context) + _context->throwReferenceError(QV4::Value::fromString(_context, detail), _fileName, loc.startLine); + else if (_errorHandler) + throwSyntaxError(loc, detail); + else + Q_ASSERT(!"No error handler available."); +} diff --git a/src/qml/qml/v4/qv4codegen_p.h b/src/qml/qml/v4/qv4codegen_p.h new file mode 100644 index 0000000000..fe00d87852 --- /dev/null +++ b/src/qml/qml/v4/qv4codegen_p.h @@ -0,0 +1,451 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4CODEGEN_P_H +#define QV4CODEGEN_P_H + +#include "qv4global_p.h" +#include "qv4jsir_p.h" +#include <private/qqmljsastvisitor_p.h> +#include <private/qqmljsast_p.h> +#include <QtCore/QStringList> +#include <assert.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { +struct DiagnosticMessage; +struct ExecutionContext; +} + +namespace QQmlJS { +namespace AST { +class UiParameterList; +} + + + +class ErrorHandler +{ +public: + virtual void syntaxError(QV4::DiagnosticMessage *message) = 0; +}; + +class Q_QML_EXPORT Codegen: protected AST::Visitor +{ +public: + Codegen(QV4::ExecutionContext *ctx, bool strict); + Codegen(ErrorHandler *errorHandler, bool strictMode); + + enum Mode { + GlobalCode, + EvalCode, + FunctionCode, + QmlBinding + }; + + V4IR::Function *operator()(const QString &fileName, + const QString &sourceCode, + AST::Program *ast, + V4IR::Module *module, + Mode mode = GlobalCode, + const QStringList &inheritedLocals = QStringList()); + V4IR::Function *operator()(const QString &fileName, + const QString &sourceCode, + AST::FunctionExpression *ast, + V4IR::Module *module); + +protected: + enum Format { ex, cx, nx }; + struct Result { + V4IR::Expr *code; + V4IR::BasicBlock *iftrue; + V4IR::BasicBlock *iffalse; + Format format; + Format requested; + + explicit Result(Format requested = ex) + : code(0) + , iftrue(0) + , iffalse(0) + , format(ex) + , requested(requested) {} + + explicit Result(V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse) + : code(0) + , iftrue(iftrue) + , iffalse(iffalse) + , format(ex) + , requested(cx) {} + + inline V4IR::Expr *operator*() const { Q_ASSERT(format == ex); return code; } + inline V4IR::Expr *operator->() const { Q_ASSERT(format == ex); return code; } + + bool accept(Format f) + { + if (requested == f) { + format = f; + return true; + } + return false; + } + }; + + struct Environment { + Environment *parent; + + enum MemberType { + UndefinedMember, + VariableDefinition, + VariableDeclaration, + FunctionDefinition + }; + struct Member { + MemberType type; + int index; + AST::FunctionExpression *function; + }; + typedef QMap<QString, Member> MemberMap; + + MemberMap members; + AST::FormalParameterList *formals; + int maxNumberOfArguments; + bool hasDirectEval; + bool hasNestedFunctions; + bool isStrict; + bool isNamedFunctionExpression; + enum UsesArgumentsObject { + ArgumentsObjectUnknown, + ArgumentsObjectNotUsed, + ArgumentsObjectUsed + }; + + UsesArgumentsObject usesArgumentsObject; + + Environment(Environment *parent) + : parent(parent) + , formals(0) + , maxNumberOfArguments(0) + , hasDirectEval(false) + , hasNestedFunctions(false) + , isStrict(false) + , isNamedFunctionExpression(false) + , usesArgumentsObject(ArgumentsObjectUnknown) + { + if (parent && parent->isStrict) + isStrict = true; + } + + int findMember(const QString &name) const + { + MemberMap::const_iterator it = members.find(name); + if (it == members.end()) + return -1; + assert((*it).index != -1 || !parent); + return (*it).index; + } + + bool lookupMember(const QString &name, Environment **scope, int *index, int *distance) + { + Environment *it = this; + *distance = 0; + for (; it; it = it->parent, ++(*distance)) { + int idx = it->findMember(name); + if (idx != -1) { + *scope = it; + *index = idx; + return true; + } + } + return false; + } + + void enter(const QString &name, MemberType type, AST::FunctionExpression *function = 0) + { + if (! name.isEmpty()) { + if (type != FunctionDefinition) { + for (AST::FormalParameterList *it = formals; it; it = it->next) + if (it->name == name) + return; + } + MemberMap::iterator it = members.find(name); + if (it == members.end()) { + Member m; + m.index = -1; + m.type = type; + m.function = function; + members.insert(name, m); + } else { + if ((*it).type <= type) { + (*it).type = type; + (*it).function = function; + } + } + } + } + }; + + Environment *newEnvironment(AST::Node *node, Environment *parent) + { + Environment *env = new Environment(parent); + _envMap.insert(node, env); + return env; + } + + struct UiMember { + }; + + struct ScopeAndFinally { + enum ScopeType { + WithScope, + TryScope, + CatchScope + }; + + ScopeAndFinally *parent; + AST::Finally *finally; + V4IR::ExprList *finishTryArgs; + ScopeType type; + + ScopeAndFinally(ScopeAndFinally *parent, ScopeType t = WithScope) : parent(parent), finally(0), finishTryArgs(0), type(t) {} + ScopeAndFinally(ScopeAndFinally *parent, AST::Finally *finally, V4IR::ExprList *finishTryArgs) + : parent(parent), finally(finally), finishTryArgs(finishTryArgs), type(TryScope) + {} + }; + + struct Loop { + AST::LabelledStatement *labelledStatement; + AST::Statement *node; + V4IR::BasicBlock *groupStartBlock; + V4IR::BasicBlock *breakBlock; + V4IR::BasicBlock *continueBlock; + Loop *parent; + ScopeAndFinally *scopeAndFinally; + + Loop(AST::Statement *node, V4IR::BasicBlock *groupStartBlock, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock, Loop *parent) + : labelledStatement(0), node(node), groupStartBlock(groupStartBlock), breakBlock(breakBlock), continueBlock(continueBlock), parent(parent) {} + }; + + void enterEnvironment(AST::Node *node); + void leaveEnvironment(); + + void enterLoop(AST::Statement *node, V4IR::BasicBlock *startBlock, V4IR::BasicBlock *breakBlock, V4IR::BasicBlock *continueBlock); + void leaveLoop(); + V4IR::BasicBlock *groupStartBlock() const + { + for (Loop *it = _loop; it; it = it->parent) + if (it->groupStartBlock) + return it->groupStartBlock; + return 0; + } + + V4IR::Expr *member(V4IR::Expr *base, const QString *name); + V4IR::Expr *subscript(V4IR::Expr *base, V4IR::Expr *index); + V4IR::Expr *argument(V4IR::Expr *expr); + V4IR::Expr *reference(V4IR::Expr *expr); + V4IR::Expr *unop(V4IR::AluOp op, V4IR::Expr *expr); + V4IR::Expr *binop(V4IR::AluOp op, V4IR::Expr *left, V4IR::Expr *right); + V4IR::Expr *call(V4IR::Expr *base, V4IR::ExprList *args); + void move(V4IR::Expr *target, V4IR::Expr *source, V4IR::AluOp op = V4IR::OpInvalid); + void cjump(V4IR::Expr *cond, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse); + + V4IR::Function *defineFunction(const QString &name, AST::Node *ast, + AST::FormalParameterList *formals, + AST::SourceElements *body, + Mode mode = FunctionCode, + const QStringList &inheritedLocals = QStringList()); + + void unwindException(ScopeAndFinally *outest); + + void statement(AST::Statement *ast); + void statement(AST::ExpressionNode *ast); + void condition(AST::ExpressionNode *ast, V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse); + Result expression(AST::ExpressionNode *ast); + QString propertyName(AST::PropertyName *ast); + Result sourceElement(AST::SourceElement *ast); + UiMember uiObjectMember(AST::UiObjectMember *ast); + + void accept(AST::Node *node); + + void functionBody(AST::FunctionBody *ast); + void program(AST::Program *ast); + void sourceElements(AST::SourceElements *ast); + void variableDeclaration(AST::VariableDeclaration *ast); + void variableDeclarationList(AST::VariableDeclarationList *ast); + + V4IR::Expr *identifier(const QString &name, int line = 0, int col = 0); + + // nodes + virtual bool visit(AST::ArgumentList *ast); + virtual bool visit(AST::CaseBlock *ast); + virtual bool visit(AST::CaseClause *ast); + virtual bool visit(AST::CaseClauses *ast); + virtual bool visit(AST::Catch *ast); + virtual bool visit(AST::DefaultClause *ast); + virtual bool visit(AST::ElementList *ast); + virtual bool visit(AST::Elision *ast); + virtual bool visit(AST::Finally *ast); + virtual bool visit(AST::FormalParameterList *ast); + virtual bool visit(AST::FunctionBody *ast); + virtual bool visit(AST::Program *ast); + virtual bool visit(AST::PropertyNameAndValue *ast); + virtual bool visit(AST::PropertyAssignmentList *ast); + virtual bool visit(AST::PropertyGetterSetter *ast); + virtual bool visit(AST::SourceElements *ast); + virtual bool visit(AST::StatementList *ast); + virtual bool visit(AST::UiArrayMemberList *ast); + virtual bool visit(AST::UiImport *ast); + virtual bool visit(AST::UiImportList *ast); + virtual bool visit(AST::UiObjectInitializer *ast); + virtual bool visit(AST::UiObjectMemberList *ast); + virtual bool visit(AST::UiParameterList *ast); + virtual bool visit(AST::UiProgram *ast); + virtual bool visit(AST::UiQualifiedId *ast); + virtual bool visit(AST::VariableDeclaration *ast); + virtual bool visit(AST::VariableDeclarationList *ast); + + // expressions + virtual bool visit(AST::Expression *ast); + virtual bool visit(AST::ArrayLiteral *ast); + virtual bool visit(AST::ArrayMemberExpression *ast); + virtual bool visit(AST::BinaryExpression *ast); + virtual bool visit(AST::CallExpression *ast); + virtual bool visit(AST::ConditionalExpression *ast); + virtual bool visit(AST::DeleteExpression *ast); + virtual bool visit(AST::FalseLiteral *ast); + virtual bool visit(AST::FieldMemberExpression *ast); + virtual bool visit(AST::FunctionExpression *ast); + virtual bool visit(AST::IdentifierExpression *ast); + virtual bool visit(AST::NestedExpression *ast); + virtual bool visit(AST::NewExpression *ast); + virtual bool visit(AST::NewMemberExpression *ast); + virtual bool visit(AST::NotExpression *ast); + virtual bool visit(AST::NullExpression *ast); + virtual bool visit(AST::NumericLiteral *ast); + virtual bool visit(AST::ObjectLiteral *ast); + virtual bool visit(AST::PostDecrementExpression *ast); + virtual bool visit(AST::PostIncrementExpression *ast); + virtual bool visit(AST::PreDecrementExpression *ast); + virtual bool visit(AST::PreIncrementExpression *ast); + virtual bool visit(AST::RegExpLiteral *ast); + virtual bool visit(AST::StringLiteral *ast); + virtual bool visit(AST::ThisExpression *ast); + virtual bool visit(AST::TildeExpression *ast); + virtual bool visit(AST::TrueLiteral *ast); + virtual bool visit(AST::TypeOfExpression *ast); + virtual bool visit(AST::UnaryMinusExpression *ast); + virtual bool visit(AST::UnaryPlusExpression *ast); + virtual bool visit(AST::VoidExpression *ast); + virtual bool visit(AST::FunctionDeclaration *ast); + + // property names + virtual bool visit(AST::IdentifierPropertyName *ast); + virtual bool visit(AST::NumericLiteralPropertyName *ast); + virtual bool visit(AST::StringLiteralPropertyName *ast); + + // source elements + virtual bool visit(AST::FunctionSourceElement *ast); + virtual bool visit(AST::StatementSourceElement *ast); + + // statements + virtual bool visit(AST::Block *ast); + virtual bool visit(AST::BreakStatement *ast); + virtual bool visit(AST::ContinueStatement *ast); + virtual bool visit(AST::DebuggerStatement *ast); + virtual bool visit(AST::DoWhileStatement *ast); + virtual bool visit(AST::EmptyStatement *ast); + virtual bool visit(AST::ExpressionStatement *ast); + virtual bool visit(AST::ForEachStatement *ast); + virtual bool visit(AST::ForStatement *ast); + virtual bool visit(AST::IfStatement *ast); + virtual bool visit(AST::LabelledStatement *ast); + virtual bool visit(AST::LocalForEachStatement *ast); + virtual bool visit(AST::LocalForStatement *ast); + virtual bool visit(AST::ReturnStatement *ast); + virtual bool visit(AST::SwitchStatement *ast); + virtual bool visit(AST::ThrowStatement *ast); + virtual bool visit(AST::TryStatement *ast); + virtual bool visit(AST::VariableStatement *ast); + virtual bool visit(AST::WhileStatement *ast); + virtual bool visit(AST::WithStatement *ast); + + // ui object members + virtual bool visit(AST::UiArrayBinding *ast); + virtual bool visit(AST::UiObjectBinding *ast); + virtual bool visit(AST::UiObjectDefinition *ast); + virtual bool visit(AST::UiPublicMember *ast); + virtual bool visit(AST::UiScriptBinding *ast); + virtual bool visit(AST::UiSourceElement *ast); + + void throwSyntaxErrorOnEvalOrArgumentsInStrictMode(V4IR::Expr* expr, const AST::SourceLocation &loc); + + void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail); + void throwReferenceError(const AST::SourceLocation &loc, const QString &detail); + +private: + QString _fileName; + Result _expr; + QString _property; + UiMember _uiMember; + V4IR::Module *_module; + V4IR::Function *_function; + V4IR::BasicBlock *_block; + V4IR::BasicBlock *_exitBlock; + V4IR::BasicBlock *_throwBlock; + unsigned _returnAddress; + Mode _mode; + Environment *_env; + Loop *_loop; + AST::LabelledStatement *_labelledStatement; + ScopeAndFinally *_scopeAndFinally; + QHash<AST::Node *, Environment *> _envMap; + QHash<AST::FunctionExpression *, int> _functionMap; + QV4::ExecutionContext *_context; + bool _strictMode; + ErrorHandler *_errorHandler; + + class ScanFunctions; +}; + +} + +QT_END_NAMESPACE + +#endif // QV4CODEGEN_P_H diff --git a/src/qml/qml/v4/qv4compiler.cpp b/src/qml/qml/v4/qv4compiler.cpp deleted file mode 100644 index d6ab73acc2..0000000000 --- a/src/qml/qml/v4/qv4compiler.cpp +++ /dev/null @@ -1,1580 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4compiler_p.h" -#include "qv4compiler_p_p.h" -#include "qv4program_p.h" -#include "qv4ir_p.h" -#include "qv4irbuilder_p.h" - -#include <private/qqmlglobal_p.h> -#include <private/qqmljsast_p.h> -#include <private/qqmlaccessors_p.h> -#include <private/qqmljsengine_p.h> - -QT_BEGIN_NAMESPACE - -DEFINE_BOOL_CONFIG_OPTION(bindingsDump, QML_BINDINGS_DUMP) -DEFINE_BOOL_CONFIG_OPTION(qmlDisableOptimizer, QML_DISABLE_OPTIMIZER) -DEFINE_BOOL_CONFIG_OPTION(qmlVerboseCompiler, QML_VERBOSE_COMPILER) -DEFINE_BOOL_CONFIG_OPTION(qmlBindingsTestEnv, QML_BINDINGS_TEST) - -static bool qmlBindingsTest = false; -static bool qmlEnableV4 = true; - -using namespace QQmlJS; -QV4CompilerPrivate::QV4CompilerPrivate() - : subscriptionOffset(0) - , _function(0) , _block(0) , _discarded(false), registerCount(0) - , bindingLine(0), bindingColumn(0), invalidatable(false) -{ -} - -// -// tracing -// -void QV4CompilerPrivate::trace(quint16 line, quint16 column) -{ - bytecode.clear(); - - this->bindingLine = line; - this->bindingColumn = column; - this->currentReg = _function->tempCount; - this->registerCount = qMax(this->registerCount, this->currentReg); - - foreach (IR::BasicBlock *bb, _function->basicBlocks) { - if (! bb->isTerminated() && (bb->index + 1) < _function->basicBlocks.size()) - bb->JUMP(_function->basicBlocks.at(bb->index + 1)); - } - - QVector<IR::BasicBlock *> blocks; - trace(&blocks); - currentBlockMask = 0x00000001; - - - for (int i = 0; !_discarded && i < blocks.size(); ++i) { - IR::BasicBlock *block = blocks.at(i); - IR::BasicBlock *next = i + 1 < blocks.size() ? blocks.at(i + 1) : 0; - if (IR::Stmt *terminator = block->terminator()) { - if (IR::CJump *cj = terminator->asCJump()) { - if (cj->iffalse != next) { - IR::Jump *jump = _function->pool->New<IR::Jump>(); - jump->init(cj->iffalse); - block->statements.append(jump); - } - } else if (IR::Jump *j = terminator->asJump()) { - if (j->target == next) { - block->statements.resize(block->statements.size() - 1); - } - } - } - - block->offset = bytecode.size(); - - if (bytecode.isEmpty()) { - if (qmlBindingsTest || bindingsDump()) { - Instr::BindingId id; - id.column = column; - id.line = line; - gen(id); - } - - if (qmlBindingsTest) { - QString str = expression->expression.asScript(); - QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar)); - int offset = data.count(); - data += strdata; - - Instr::EnableV4Test test; - test.reg = 0; - test.offset = offset; - test.length = str.length(); - gen(test); - } - } - - bool usic = false; - int patchesCount = patches.count(); - qSwap(usedSubscriptionIdsChanged, usic); - - int blockopIndex = bytecode.size(); - Instr::Block blockop; - blockop.block = currentBlockMask; - gen(blockop); - - foreach (IR::Stmt *s, block->statements) { - if (! _discarded) - s->accept(this); - } - - qSwap(usedSubscriptionIdsChanged, usic); - - if (usic) { - if (currentBlockMask == 0x80000000) { - discard(); - return; - } - currentBlockMask <<= 1; - } else if (! _discarded) { - const int adjust = bytecode.remove(blockopIndex); - // Correct patches - for (int ii = patchesCount; ii < patches.count(); ++ii) - patches[ii].offset -= adjust; - } - } - -#ifdef DEBUG_IR_STRUCTURE - IR::IRDump dump; - for (int i = 0; i < blocks.size(); ++i) { - dump.basicblock(blocks.at(i)); - } -#endif - - - if (! _discarded) { - // back patching - foreach (const Patch &patch, patches) { - V4Instr &instr = bytecode[patch.offset]; - int size = V4Instr::size(instructionType(&instr)); - instr.branchop.offset = patch.block->offset - patch.offset - size; - } - - patches.clear(); - } -} - -void QV4CompilerPrivate::trace(QVector<IR::BasicBlock *> *blocks) -{ - for (int i = 0; i < _function->basicBlocks.size(); ++i) { - IR::BasicBlock *block = _function->basicBlocks.at(i); - - while (! blocks->contains(block)) { - blocks->append(block); - - if (IR::Stmt *terminator = block->terminator()) { - if (IR::CJump *cj = terminator->asCJump()) - block = cj->iffalse; - else if (IR::Jump *j = terminator->asJump()) - block = j->target; - } - } - } -} - -void QV4CompilerPrivate::traceExpression(IR::Expr *e, quint8 r) -{ - if (!e) { - discard(); - } else { - qSwap(currentReg, r); - e->accept(this); - qSwap(currentReg, r); - } -} - -// -// expressions -// -void QV4CompilerPrivate::visitConst(IR::Const *e) -{ - switch (e->type) { - case IR::BoolType: { - Instr::LoadBool i; - i.reg = currentReg; - i.value = e->value; - gen(i); - } break; - - case IR::IntType: { - Instr::LoadInt i; - i.reg = currentReg; - i.value = e->value; - gen(i); - } break; - - case IR::FloatType: - case IR::NumberType: { - Instr::LoadNumber i; - i.reg = currentReg; - i.value = e->value; - gen(i); - } break; - - case IR::NullType: { - Instr::LoadNull i; - i.reg = currentReg; - gen(i); - } break; - - default: - if (qmlVerboseCompiler()) - qWarning() << Q_FUNC_INFO << "unexpected type"; - discard(); - } -} - -void QV4CompilerPrivate::visitString(IR::String *e) -{ - registerLiteralString(currentReg, e->value); -} - -void QV4CompilerPrivate::visitName(IR::Name *e) -{ - if (e->base) { - // fetch the object and store it in reg. - traceExpression(e->base, currentReg); - } else { - _subscribeName.clear(); - } - - if (e->storage == IR::Name::RootStorage) { - - Instr::LoadRoot instr; - instr.reg = currentReg; - gen(instr); - - if (e->symbol == IR::Name::IdObject) { - // The ID is a reference to the root object - return; - } - - } else if (e->storage == IR::Name::ScopeStorage) { - - Instr::LoadScope instr; - instr.reg = currentReg; - gen(instr); - - _subscribeName << contextName(); - - } else if (e->storage == IR::Name::IdStorage) { - - Instr::LoadId instr; - instr.reg = currentReg; - instr.index = e->idObject->idIndex; - gen(instr); - - _subscribeName << QLatin1String("$$$ID_") + *e->id; - - if (blockNeedsSubscription(_subscribeName)) { - Instr::SubscribeId sub; - sub.reg = currentReg; - sub.offset = subscriptionIndex(_subscribeName); - sub.index = instr.index; - gen(sub); - } - - return; - } else { - // No action needed - } - - switch (e->symbol) { - case IR::Name::Unbound: - case IR::Name::IdObject: - case IR::Name::Slot: - case IR::Name::Object: { - Q_ASSERT(!"Unreachable"); - discard(); - } break; - - case IR::Name::AttachType: { - _subscribeName << *e->id; - - Instr::LoadAttached attached; - attached.output = currentReg; - attached.reg = currentReg; - attached.exceptionId = exceptionId(bindingLine, bindingColumn); - if (e->declarativeType->attachedPropertiesId() == -1) - discard(); - attached.id = e->declarativeType->attachedPropertiesId(); - gen(attached); - } break; - - case IR::Name::SingletonObject: { - /* - Existing singleton type object lookup methods include: - 1. string -> singleton object (search via importCache->query(name)) - 2. typeid -> singleton object QQmlType (search via ???) - We currently use 1, which is not ideal for performance - */ - _subscribeName << *e->id; - - registerLiteralString(currentReg, e->id); - - Instr::LoadSingletonObject module; - module.reg = currentReg; - gen(module); - } break; - - case IR::Name::Property: { - _subscribeName << *e->id; - - if (e->property->coreIndex == -1) { - QMetaProperty prop; - e->property->load(prop, QQmlEnginePrivate::get(engine)); - } - - const int propTy = e->property->propType; - QQmlRegisterType regType; - - switch (propTy) { - case QMetaType::Float: - regType = FloatType; - break; - case QMetaType::Double: - regType = NumberType; - break; - case QMetaType::Bool: - regType = BoolType; - break; - case QMetaType::Int: - regType = IntType; - break; - case QMetaType::QString: - regType = QStringType; - break; - case QMetaType::QUrl: - regType = QUrlType; - break; - case QMetaType::QColor: - regType = QColorType; - break; - case QMetaType::QVariant: - regType = QVariantType; - break; - - default: - if (propTy == QQmlMetaType::QQuickAnchorLineMetaTypeId()) { - regType = PODValueType; - } else if (!engine->metaObjectForType(propTy).isNull()) { - regType = QObjectStarType; - } else { - if (qmlVerboseCompiler()) - qWarning() << "Discard unsupported property type:" << QMetaType::typeName(propTy); - discard(); // Unsupported type - return; - } - - break; - } // switch - - if (e->property->hasAccessors()) { - Instr::FetchAndSubscribe fetch; - fetch.reg = currentReg; - fetch.subscription = subscriptionIndex(_subscribeName); - fetch.exceptionId = exceptionId(e->line, e->column); - fetch.valueType = regType; - fetch.property = *e->property; - gen(fetch); - } else { - Instr::Fetch fetch; - fetch.reg = currentReg; - fetch.index = e->property->coreIndex; - fetch.exceptionId = exceptionId(e->line, e->column); - fetch.valueType = regType; - - if (blockNeedsSubscription(_subscribeName) && e->property->notifyIndex != -1) { - fetch.subOffset = subscriptionIndex(_subscribeName); - fetch.subIndex = e->property->notifyIndex; - } else { - fetch.subOffset = static_cast<quint16>(-1); - fetch.subIndex = static_cast<quint32>(-1); - } - - gen(fetch); - } - - } break; - } // switch -} - -void QV4CompilerPrivate::visitTemp(IR::Temp *e) -{ - if (currentReg != e->index) { - Instr::Copy i; - i.reg = currentReg; - i.src = e->index; - gen(i); - } -} - -void QV4CompilerPrivate::visitUnop(IR::Unop *e) -{ - quint8 src = currentReg; - - if (IR::Temp *temp = e->expr->asTemp()) { - src = temp->index; - } else { - traceExpression(e->expr, src); - } - - switch (e->op) { - case IR::OpInvalid: - Q_ASSERT(!"unreachable"); - break; - - case IR::OpIfTrue: - convertToBool(e->expr, src); - if (src != currentReg) { - Instr::Copy i; - i.reg = currentReg; - i.src = src; - gen(i); - } - break; - - case IR::OpNot: { - Instr::UnaryNot i; - convertToBool(e->expr, src); - i.output = currentReg; - i.src = src; - gen(i); - } break; - - case IR::OpUMinus: - if (IR::isRealType(e->expr->type)) { - Instr::UnaryMinusNumber i; - i.output = currentReg; - i.src = src; - gen(i); - } else if (e->expr->type == IR::IntType) { - convertToNumber(e->expr, currentReg); - Instr::UnaryMinusNumber i; - i.output = currentReg; - i.src = src; - gen(i); - } else { - discard(); - } - break; - - case IR::OpUPlus: - if (IR::isRealType(e->expr->type)) { - Instr::UnaryPlusNumber i; - i.output = currentReg; - i.src = src; - gen(i); - } else if (e->expr->type == IR::IntType) { - convertToNumber(e->expr, currentReg); - Instr::UnaryPlusNumber i; - i.output = currentReg; - i.src = src; - gen(i); - } else { - discard(); - } - break; - - case IR::OpCompl: - // TODO - discard(); - break; - - case IR::OpBitAnd: - case IR::OpBitOr: - case IR::OpBitXor: - case IR::OpAdd: - case IR::OpSub: - case IR::OpMul: - case IR::OpDiv: - case IR::OpMod: - case IR::OpLShift: - case IR::OpRShift: - case IR::OpURShift: - case IR::OpGt: - case IR::OpLt: - case IR::OpGe: - case IR::OpLe: - case IR::OpEqual: - case IR::OpNotEqual: - case IR::OpStrictEqual: - case IR::OpStrictNotEqual: - case IR::OpAnd: - case IR::OpOr: - Q_ASSERT(!"unreachable"); - break; - } // switch -} - -void QV4CompilerPrivate::convertToNumber(IR::Expr *expr, int reg) -{ - if (expr->type == IR::NumberType) - return; - - switch (expr->type) { - case IR::BoolType: { - Instr::ConvertBoolToNumber i; - i.output = i.src = reg; - gen(i); - } break; - - case IR::IntType: { - Instr::ConvertIntToNumber i; - i.output = i.src = reg; - gen(i); - } break; - - case IR::FloatType: - case IR::NumberType: - // nothing to do - return; - - default: - discard(); - break; - } // switch -} - -void QV4CompilerPrivate::convertToInt(IR::Expr *expr, int reg) -{ - if (expr->type == IR::IntType) - return; - - switch (expr->type) { - case IR::BoolType: { - Instr::ConvertBoolToInt i; - i.output = i.src = reg; - gen(i); - } break; - - case IR::IntType: - // nothing to do - return; - - case IR::FloatType: - case IR::NumberType: { - Instr::ConvertNumberToInt i; - i.output = i.src = reg; - gen(i); - } break; - - default: - discard(); - break; - } // switch -} - -void QV4CompilerPrivate::convertToBool(IR::Expr *expr, int reg) -{ - if (expr->type == IR::BoolType) - return; - - switch (expr->type) { - case IR::BoolType: - // nothing to do - break; - - case IR::IntType: { - Instr::ConvertIntToBool i; - i.output = i.src = reg; - gen(i); - } break; - - case IR::FloatType: - case IR::NumberType: { - Instr::ConvertNumberToBool i; - i.output = i.src = reg; - gen(i); - } return; - - case IR::StringType: { - Instr::ConvertStringToBool i; - i.output = i.src = reg; - gen(i); - } return; - - case IR::ColorType: { - Instr::ConvertColorToBool i; - i.output = i.src = reg; - gen(i); - } return; - - default: - discard(); - break; - } // switch -} - -quint8 QV4CompilerPrivate::instructionOpcode(IR::Binop *e) -{ - switch (e->op) { - case IR::OpInvalid: - return V4Instr::Noop; - - case IR::OpIfTrue: - case IR::OpNot: - case IR::OpUMinus: - case IR::OpUPlus: - case IR::OpCompl: - return V4Instr::Noop; - - case IR::OpBitAnd: - return V4Instr::BitAndInt; - - case IR::OpBitOr: - return V4Instr::BitOrInt; - - case IR::OpBitXor: - return V4Instr::BitXorInt; - - case IR::OpAdd: - if (e->type == IR::StringType) - return V4Instr::AddString; - return V4Instr::AddNumber; - - case IR::OpSub: - return V4Instr::SubNumber; - - case IR::OpMul: - return V4Instr::MulNumber; - - case IR::OpDiv: - return V4Instr::DivNumber; - - case IR::OpMod: - return V4Instr::ModNumber; - - case IR::OpLShift: - return V4Instr::LShiftInt; - - case IR::OpRShift: - return V4Instr::RShiftInt; - - case IR::OpURShift: - return V4Instr::URShiftInt; - - case IR::OpGt: - if (e->left->type == IR::StringType) - return V4Instr::GtString; - return V4Instr::GtNumber; - - case IR::OpLt: - if (e->left->type == IR::StringType) - return V4Instr::LtString; - return V4Instr::LtNumber; - - case IR::OpGe: - if (e->left->type == IR::StringType) - return V4Instr::GeString; - return V4Instr::GeNumber; - - case IR::OpLe: - if (e->left->type == IR::StringType) - return V4Instr::LeString; - return V4Instr::LeNumber; - - case IR::OpEqual: - if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType) - return V4Instr::EqualObject; - if (e->left->type == IR::StringType) - return V4Instr::EqualString; - return V4Instr::EqualNumber; - - case IR::OpNotEqual: - if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType) - return V4Instr::NotEqualObject; - if (e->left->type == IR::StringType) - return V4Instr::NotEqualString; - return V4Instr::NotEqualNumber; - - case IR::OpStrictEqual: - if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType) - return V4Instr::StrictEqualObject; - if (e->left->type == IR::StringType) - return V4Instr::StrictEqualString; - return V4Instr::StrictEqualNumber; - - case IR::OpStrictNotEqual: - if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType) - return V4Instr::StrictNotEqualObject; - if (e->left->type == IR::StringType) - return V4Instr::StrictNotEqualString; - return V4Instr::StrictNotEqualNumber; - - case IR::OpAnd: - case IR::OpOr: - return V4Instr::Noop; - - } // switch - - return V4Instr::Noop; -} - -void QV4CompilerPrivate::visitBinop(IR::Binop *e) -{ - if (e->type == IR::InvalidType) { - discard(); - return; - } - - int left = currentReg; - int right = currentReg + 1; - - if (e->left->asTemp() && e->type != IR::StringType) - left = e->left->asTemp()->index; - else - traceExpression(e->left, left); - - if (IR::Temp *t = e->right->asTemp()) - right = t->index; - else - traceExpression(e->right, right); - - if (e->left->type != e->right->type) { - if (qmlVerboseCompiler()) - qWarning().nospace() << "invalid operands to binary operator " << IR::binaryOperator(e->op) - << "(`" << IR::binaryOperator(e->left->type) - << "' and `" - << IR::binaryOperator(e->right->type) - << '\''; - discard(); - return; - } - - switch (e->op) { - case IR::OpInvalid: - discard(); - break; - - // unary - case IR::OpIfTrue: - case IR::OpNot: - case IR::OpUMinus: - case IR::OpUPlus: - case IR::OpCompl: - discard(); - break; - - case IR::OpBitAnd: - case IR::OpBitOr: - case IR::OpBitXor: - case IR::OpLShift: - case IR::OpRShift: - case IR::OpURShift: - convertToInt(e->left, left); - convertToInt(e->right, right); - break; - - case IR::OpAdd: - if (e->type != IR::StringType) { - convertToNumber(e->left, left); - convertToNumber(e->right, right); - } - break; - - case IR::OpSub: - case IR::OpMul: - case IR::OpDiv: - case IR::OpMod: - convertToNumber(e->left, left); - convertToNumber(e->right, right); - break; - - case IR::OpGt: - case IR::OpLt: - case IR::OpGe: - case IR::OpLe: - case IR::OpEqual: - case IR::OpNotEqual: - case IR::OpStrictEqual: - case IR::OpStrictNotEqual: - if (e->left->type >= IR::FirstNumberType) { - convertToNumber(e->left, left); - convertToNumber(e->right, right); - } - break; - - case IR::OpAnd: - case IR::OpOr: - discard(); // ### unreachable - break; - } // switch - - const quint8 opcode = instructionOpcode(e); - if (opcode != V4Instr::Noop) { - V4Instr instr; - instr.binaryop.output = currentReg; - instr.binaryop.left = left; - instr.binaryop.right = right; - gen(static_cast<V4Instr::Type>(opcode), instr); - } -} - -void QV4CompilerPrivate::visitCall(IR::Call *call) -{ - if (IR::Name *name = call->base->asName()) { - IR::Expr *arg = call->onlyArgument(); - if (arg != 0 && IR::isRealType(arg->type)) { - traceExpression(arg, currentReg); - - switch (name->builtin) { - case IR::NoBuiltinSymbol: - break; - - case IR::MathSinBultinFunction: { - Instr::MathSinNumber i; - i.output = i.src = currentReg; - gen(i); - } return; - - case IR::MathCosBultinFunction: { - Instr::MathCosNumber i; - i.output = i.src = currentReg; - gen(i); - } return; - - case IR::MathAbsBuiltinFunction: { - Instr::MathAbsNumber i; - i.output = i.src = currentReg; - gen(i); - } return; - - case IR::MathRoundBultinFunction: { - Instr::MathRoundNumber i; - i.output = i.src = currentReg; - gen(i); - } return; - - case IR::MathFloorBultinFunction: { - Instr::MathFloorNumber i; - i.output = i.src = currentReg; - gen(i); - } return; - - case IR::MathCeilBuiltinFunction: { - Instr::MathCeilNumber i; - i.output = i.src = currentReg; - gen(i); - } return; - - case IR::MathPIBuiltinConstant: - default: - break; - } // switch - } else { - if (name->builtin == IR::MathMaxBuiltinFunction || - name->builtin == IR::MathMinBuiltinFunction) { - - //only handles the most common case of exactly two arguments - if (call->args && call->args->next && !call->args->next->next) { - IR::Expr *arg1 = call->args->expr; - IR::Expr *arg2 = call->args->next->expr; - - if (arg1 != 0 && IR::isRealType(arg1->type) && - arg2 != 0 && IR::isRealType(arg2->type)) { - - traceExpression(arg1, currentReg); - traceExpression(arg2, currentReg + 1); - - if (name->builtin == IR::MathMaxBuiltinFunction) { - Instr::MathMaxNumber i; - i.left = currentReg; - i.right = currentReg + 1; - i.output = currentReg; - gen(i); - return; - } else if (name->builtin == IR::MathMinBuiltinFunction) { - Instr::MathMinNumber i; - i.left = currentReg; - i.right = currentReg + 1; - i.output = currentReg; - gen(i); - return; - } - } - } - } - } - } - - if (qmlVerboseCompiler()) - qWarning() << "TODO:" << Q_FUNC_INFO << __LINE__; - discard(); -} - - -// -// statements -// -void QV4CompilerPrivate::visitExp(IR::Exp *s) -{ - traceExpression(s->expr, currentReg); -} - -void QV4CompilerPrivate::visitMove(IR::Move *s) -{ - IR::Temp *target = s->target->asTemp(); - Q_ASSERT(target != 0); - - quint8 dest = target->index; - - IR::Type targetTy = s->target->type; - IR::Type sourceTy = s->source->type; - - // promote the floats - if (sourceTy == IR::FloatType) - sourceTy = IR::NumberType; - - if (targetTy == IR::FloatType) - targetTy = IR::NumberType; - - if (sourceTy != targetTy) { - quint8 src = dest; - - if (IR::Temp *t = s->source->asTemp()) - src = t->index; - else - traceExpression(s->source, dest); - - V4Instr::Type opcode = V4Instr::Noop; - - if (sourceTy == IR::UrlType) { - switch (targetTy) { - case IR::BoolType: - case IR::StringType: - case IR::VariantType: - case IR::VarType: - case IR::JSValueType: - // nothing to do. V4 will generate optimized - // url-to-xxx conversions. - break; - default: { - if (s->isMoveForReturn) { - V4Instr instr; - instr.throwop.exceptionId = exceptionId(bindingLine, bindingColumn); - registerLiteralString(dest, _function->newString(QString::fromUtf8("Unable to assign %1 to %2") - .arg(QLatin1String(IR::typeName(sourceTy))) - .arg(QLatin1String(IR::typeName(targetTy))))); - instr.throwop.message = dest; - gen(V4Instr::Throw, instr); - return; - } - // generate a UrlToString conversion and fix - // the type of the source expression. - V4Instr conv; - conv.unaryop.output = src; - conv.unaryop.src = src; - gen(V4Instr::ConvertUrlToString, conv); - sourceTy = IR::StringType; - break; - } - } // switch - } - - if (targetTy == IR::BoolType) { - switch (sourceTy) { - case IR::IntType: opcode = V4Instr::ConvertIntToBool; break; - case IR::NumberType: opcode = V4Instr::ConvertNumberToBool; break; - case IR::StringType: opcode = V4Instr::ConvertStringToBool; break; - case IR::UrlType: opcode = V4Instr::ConvertUrlToBool; break; - case IR::ColorType: opcode = V4Instr::ConvertColorToBool; break; - case IR::ObjectType: opcode = V4Instr::ConvertObjectToBool; break; - default: break; - } // switch - } else if (targetTy == IR::IntType) { - switch (sourceTy) { - case IR::BoolType: opcode = V4Instr::ConvertBoolToInt; break; - case IR::NumberType: { - if (s->isMoveForReturn) - opcode = V4Instr::MathRoundNumber; - else - opcode = V4Instr::ConvertNumberToInt; - break; - } - case IR::StringType: opcode = V4Instr::ConvertStringToInt; break; - default: break; - } // switch - } else if (IR::isRealType(targetTy)) { - switch (sourceTy) { - case IR::BoolType: opcode = V4Instr::ConvertBoolToNumber; break; - case IR::IntType: opcode = V4Instr::ConvertIntToNumber; break; - case IR::StringType: opcode = V4Instr::ConvertStringToNumber; break; - default: break; - } // switch - } else if (targetTy == IR::StringType) { - switch (sourceTy) { - case IR::BoolType: opcode = V4Instr::ConvertBoolToString; break; - case IR::IntType: opcode = V4Instr::ConvertIntToString; break; - case IR::NumberType: opcode = V4Instr::ConvertNumberToString; break; - case IR::UrlType: opcode = V4Instr::ConvertUrlToString; break; - case IR::ColorType: opcode = V4Instr::ConvertColorToString; break; - default: break; - } // switch - } else if (targetTy == IR::UrlType) { - if (s->isMoveForReturn && sourceTy != IR::StringType) { - V4Instr instr; - instr.throwop.exceptionId = exceptionId(bindingLine, bindingColumn); - registerLiteralString(dest, _function->newString(QString::fromUtf8("Unable to assign %1 to %2") - .arg(QLatin1String(IR::typeName(sourceTy))) - .arg(QLatin1String(IR::typeName(targetTy))))); - instr.throwop.message = dest; - gen(V4Instr::Throw, instr); - return; - } - - V4Instr convToString; - convToString.unaryop.output = dest; - convToString.unaryop.src = src; - - // try to convert the source expression to a string. - switch (sourceTy) { - case IR::BoolType: gen(V4Instr::ConvertBoolToString, convToString); sourceTy = IR::StringType; break; - case IR::IntType: gen(V4Instr::ConvertIntToString, convToString); sourceTy = IR::StringType; break; - case IR::NumberType: gen(V4Instr::ConvertNumberToString, convToString); sourceTy = IR::StringType; break; - case IR::ColorType: gen(V4Instr::ConvertColorToString, convToString); sourceTy = IR::StringType; break; - default: break; - } // switch - - if (sourceTy == IR::StringType) - opcode = V4Instr::ConvertStringToUrl; - } else if (targetTy == IR::ColorType) { - switch (sourceTy) { - case IR::StringType: opcode = V4Instr::ConvertStringToColor; break; - default: break; - } // switch - } else if (targetTy == IR::ObjectType) { - switch (sourceTy) { - case IR::NullType: opcode = V4Instr::ConvertNullToObject; break; - default: break; - } // switch - } else if (targetTy == IR::VariantType) { - if (s->isMoveForReturn) { - switch (sourceTy) { - case IR::BoolType: opcode = V4Instr::ConvertBoolToVariant; break; - case IR::IntType: opcode = V4Instr::ConvertIntToVariant; break; - case IR::NumberType: opcode = V4Instr::ConvertNumberToVariant; break; - case IR::UrlType: opcode = V4Instr::ConvertUrlToVariant; break; - case IR::ColorType: opcode = V4Instr::ConvertColorToVariant; break; - case IR::StringType: opcode = V4Instr::ConvertStringToVariant; break; - case IR::ObjectType: opcode = V4Instr::ConvertObjectToVariant; break; - case IR::NullType: opcode = V4Instr::ConvertNullToVariant; break; - default: break; - } // switch - } - } else if (targetTy == IR::VarType) { - if (s->isMoveForReturn) { - switch (sourceTy) { - case IR::BoolType: opcode = V4Instr::ConvertBoolToVar; break; - case IR::IntType: opcode = V4Instr::ConvertIntToVar; break; - case IR::NumberType: opcode = V4Instr::ConvertNumberToVar; break; - case IR::UrlType: opcode = V4Instr::ConvertUrlToVar; break; - case IR::ColorType: opcode = V4Instr::ConvertColorToVar; break; - case IR::StringType: opcode = V4Instr::ConvertStringToVar; break; - case IR::ObjectType: opcode = V4Instr::ConvertObjectToVar; break; - case IR::NullType: opcode = V4Instr::ConvertNullToVar; break; - case IR::JSValueType: opcode = V4Instr::ConvertJSValueToVar; break; - default: break; - } // switch - } - } else if (targetTy == IR::JSValueType) { - if (s->isMoveForReturn) { - switch (sourceTy) { - case IR::BoolType: opcode = V4Instr::ConvertBoolToJSValue; break; - case IR::IntType: opcode = V4Instr::ConvertIntToJSValue; break; - case IR::NumberType: opcode = V4Instr::ConvertNumberToJSValue; break; - case IR::UrlType: opcode = V4Instr::ConvertUrlToJSValue; break; - case IR::ColorType: opcode = V4Instr::ConvertColorToJSValue; break; - case IR::StringType: opcode = V4Instr::ConvertStringToJSValue; break; - case IR::ObjectType: opcode = V4Instr::ConvertObjectToJSValue; break; - case IR::VarType: opcode = V4Instr::ConvertVarToJSValue; break; - case IR::NullType: opcode = V4Instr::ConvertNullToJSValue; break; - default: break; - } - } - } - if (opcode != V4Instr::Noop) { - V4Instr conv; - conv.unaryop.output = dest; - conv.unaryop.src = src; - gen(opcode, conv); - - if (s->isMoveForReturn && opcode == V4Instr::ConvertStringToUrl) { - V4Instr resolveUrl; - resolveUrl.unaryop.output = dest; - resolveUrl.unaryop.src = dest; - gen(V4Instr::ResolveUrl, resolveUrl); - } - } else { - discard(); - } - } else { - traceExpression(s->source, dest); - } -} - -void QV4CompilerPrivate::visitJump(IR::Jump *s) -{ - patches.append(Patch(s->target, bytecode.size())); - - Instr::Branch i; - i.offset = 0; // ### backpatch - gen(i); -} - -void QV4CompilerPrivate::visitCJump(IR::CJump *s) -{ - traceExpression(s->cond, currentReg); - - patches.append(Patch(s->iftrue, bytecode.size())); - - Instr::BranchTrue i; - i.reg = currentReg; - i.offset = 0; // ### backpatch - gen(i); -} - -void QV4CompilerPrivate::visitRet(IR::Ret *s) -{ - Q_ASSERT(s->expr != 0); - - int storeReg = currentReg; - - if (IR::Temp *temp = s->expr->asTemp()) { - storeReg = temp->index; - } else { - traceExpression(s->expr, storeReg); - } - - if (qmlBindingsTest) { - Instr::TestV4Store test; - test.reg = storeReg; - switch (s->type) { - case IR::StringType: - test.regType = QMetaType::QString; - break; - case IR::UrlType: - test.regType = QMetaType::QUrl; - break; - case IR::ColorType: - test.regType = QMetaType::QColor; - break; - case IR::SGAnchorLineType: - test.regType = QQmlMetaType::QQuickAnchorLineMetaTypeId(); - break; - case IR::ObjectType: - test.regType = QMetaType::QObjectStar; - break; - case IR::VariantType: - test.regType = QMetaType::QVariant; - break; - case IR::VarType: - test.regType = qMetaTypeId<v8::Handle<v8::Value> >(); - break; - case IR::JSValueType: - test.regType = qMetaTypeId<QJSValue>(); - break; - case IR::BoolType: - test.regType = QMetaType::Bool; - break; - case IR::IntType: - test.regType = QMetaType::Int; - break; - case IR::FloatType: - case IR::NumberType: - test.regType = QMetaType::Double; - break; - default: - discard(); - return; - } - gen(test); - } - - Instr::Store store; - store.output = 0; - store.index = expression->property->index; - store.reg = storeReg; - store.valueType = s->type == IR::FloatType ? FloatType : 0; - store.exceptionId = exceptionId(s->line, s->column); - gen(store); -} - -void QV4Compiler::dump(const QByteArray &programData) -{ - const QV4Program *program = (const QV4Program *)programData.constData(); - - qWarning() << "Program.bindings:" << program->bindings; - qWarning() << "Program.dataLength:" << program->dataLength; - qWarning() << "Program.subscriptions:" << program->subscriptions; - - const int programSize = program->instructionCount; - const char *start = program->instructions(); - const char *end = start + programSize; - Bytecode bc; - bc.dump(start, end); -} - -/*! -Clear the state associated with attempting to compile a specific binding. -This does not clear the global "committed binding" states. -*/ -void QV4CompilerPrivate::resetInstanceState() -{ - data = committed.data; - exceptions = committed.exceptions; - usedSubscriptionIds.clear(); - subscriptionIds.clear(); - subscriptionOffset = committed.subscriptionCount; - bytecode.clear(); - patches.clear(); - pool.clear(); - currentReg = 0; - invalidatable = false; -} - -/*! -Mark the last compile as successful, and add it to the "committed data" -section. - -Returns the index for the committed binding. -*/ -int QV4CompilerPrivate::commitCompile() -{ - int rv = committed.count(); - committed.offsets << committed.bytecode.count(); - committed.dependencies << usedSubscriptionIds; - committed.bytecode.append(bytecode.constData(), bytecode.size()); - committed.data = data; - committed.exceptions = exceptions; - committed.subscriptionCount = subscriptionOffset + subscriptionIds.count(); - if (bindingsDump()) - committed.subscriptions.append(subscriptionIds); - return rv; -} - -bool QV4CompilerPrivate::compile(QQmlJS::AST::Node *node) -{ - resetInstanceState(); - - if (expression->property->type == -1) - return false; - - AST::SourceLocation location; - if (AST::ExpressionNode *astExpression = node->expressionCast()) { - location = astExpression->firstSourceLocation(); - } else if (AST::Statement *astStatement = node->statementCast()) { - if (AST::Block *block = AST::cast<AST::Block *>(astStatement)) - location = block->lbraceToken; - else if (AST::IfStatement *ifStmt = AST::cast<AST::IfStatement *>(astStatement)) - location = ifStmt->ifToken; - else - return false; - } else { - return false; - } - - IR::Function thisFunction(&pool), *function = &thisFunction; - - QV4IRBuilder irBuilder(expression, engine); - if (!irBuilder(function, node, &invalidatable)) - return false; - - bool discarded = false; - qSwap(_discarded, discarded); - qSwap(_function, function); - trace(location.startLine, location.startColumn); - qSwap(_function, function); - qSwap(_discarded, discarded); - - if (qmlVerboseCompiler()) { - QTextStream qerr(stderr, QIODevice::WriteOnly); - if (discarded) - qerr << "======== TODO ====== " << endl; - else - qerr << "==================== " << endl; - qerr << "\tline: " << location.startLine - << "\tcolumn: " << location.startColumn - << endl; - foreach (IR::BasicBlock *bb, function->basicBlocks) - bb->dump(qerr); - qerr << endl; - } - - if (discarded || subscriptionIds.count() > 0xFFFF || registerCount > 31) - return false; - - return true; -} - -// Returns a reg -int QV4CompilerPrivate::registerLiteralString(quint8 reg, const QStringRef &str) -{ - // ### string cleanup - - QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar)); - int offset = data.count(); - data += strdata; - - Instr::LoadString string; - string.reg = reg; - string.offset = offset; - string.length = str.length(); - gen(string); - - return reg; -} - -/*! -Returns true if the current expression has not already subscribed to \a sub in currentBlockMask. -*/ -bool QV4CompilerPrivate::blockNeedsSubscription(const QStringList &sub) -{ - QString str = sub.join(QLatin1String(".")); - - int *iter = subscriptionIds.value(str); - if (!iter) - return true; - - quint32 *uiter = usedSubscriptionIds.value(*iter); - if (!uiter) - return true; - else - return !(*uiter & currentBlockMask); -} - -int QV4CompilerPrivate::subscriptionIndex(const QStringList &sub) -{ - QString str = sub.join(QLatin1String(".")); - int *iter = subscriptionIds.value(str); - if (!iter) { - int count = subscriptionOffset + subscriptionIds.count(); - iter = &subscriptionIds[str]; - *iter = count; - } - quint32 &u = usedSubscriptionIds[*iter]; - if (!(u & currentBlockMask)) { - u |= currentBlockMask; - usedSubscriptionIdsChanged = true; - } - return *iter; -} - -quint32 QV4CompilerPrivate::subscriptionBlockMask(const QStringList &sub) -{ - QString str = sub.join(QLatin1String(".")); - - int *iter = subscriptionIds.value(str); - Q_ASSERT(iter != 0); - - quint32 *uiter = usedSubscriptionIds.value(*iter); - Q_ASSERT(uiter != 0); - - return *uiter; -} - -quint8 QV4CompilerPrivate::exceptionId(quint16 line, quint16 column) -{ - quint8 rv = 0xFF; - if (exceptions.count() < 0xFF) { - rv = (quint8)exceptions.count(); - quint32 e = line; - e <<= 16; - e |= column; - exceptions.append(e); - } - return rv; -} - -quint8 QV4CompilerPrivate::exceptionId(QQmlJS::AST::ExpressionNode *n) -{ - quint8 rv = 0xFF; - if (n && exceptions.count() < 0xFF) { - QQmlJS::AST::SourceLocation l = n->firstSourceLocation(); - rv = exceptionId(l.startLine, l.startColumn); - } - return rv; -} - -QV4Compiler::QV4Compiler() -: d(new QV4CompilerPrivate) -{ - qmlBindingsTest |= qmlBindingsTestEnv(); -} - -QV4Compiler::~QV4Compiler() -{ - delete d; d = 0; -} - -/* -Returns true if any bindings were compiled. -*/ -bool QV4Compiler::isValid() const -{ - return !d->committed.bytecode.isEmpty(); -} - -/* --1 on failure, otherwise the binding index to use. -*/ -int QV4Compiler::compile(const Expression &expression, QQmlEnginePrivate *engine, bool *invalidatable) -{ - if (!expression.expression.asAST()) return false; - - if (qmlDisableOptimizer() || !qmlEnableV4) - return -1; - - d->expression = &expression; - d->engine = engine; - - if (d->compile(expression.expression.asAST())) { - *invalidatable = d->isInvalidatable(); - return d->commitCompile(); - } else { - return -1; - } -} - -QByteArray QV4CompilerPrivate::buildSignalTable() const -{ - QHash<int, QList<QPair<int, quint32> > > table; - - for (int ii = 0; ii < committed.count(); ++ii) { - const QQmlAssociationList<int, quint32> &deps = committed.dependencies.at(ii); - for (QQmlAssociationList<int, quint32>::const_iterator iter = deps.begin(); iter != deps.end(); ++iter) - table[iter->first].append(qMakePair(ii, iter->second)); - } - - QVector<quint32> header; - QVector<quint32> data; - for (int ii = 0; ii < committed.subscriptionCount; ++ii) { - header.append(committed.subscriptionCount + data.count()); - const QList<QPair<int, quint32> > &bindings = table[ii]; - data.append(bindings.count()); - for (int jj = 0; jj < bindings.count(); ++jj) { - data.append(bindings.at(jj).first); - data.append(bindings.at(jj).second); - } - } - header << data; - - return QByteArray((const char *)header.constData(), header.count() * sizeof(quint32)); -} - -QByteArray QV4CompilerPrivate::buildExceptionData() const -{ - QByteArray rv; - rv.resize(committed.exceptions.count() * sizeof(quint32)); - ::memcpy(rv.data(), committed.exceptions.constData(), rv.size()); - return rv; -} - -/* -Returns the compiled program. -*/ -QByteArray QV4Compiler::program() const -{ - QByteArray programData; - - if (isValid()) { - QV4Program prog; - prog.bindings = d->committed.count(); - - Bytecode bc; - QV4CompilerPrivate::Instr::Jump jump; - jump.reg = -1; - - for (int ii = 0; ii < d->committed.count(); ++ii) { - jump.count = d->committed.count() - ii - 1; - jump.count*= V4InstrMeta<V4Instr::Jump>::Size; - jump.count+= d->committed.offsets.at(ii); - bc.append(jump); - } - - - QByteArray bytecode; - bytecode.reserve(bc.size() + d->committed.bytecode.size()); - bytecode.append(bc.constData(), bc.size()); - bytecode.append(d->committed.bytecode.constData(), d->committed.bytecode.size()); - - QByteArray data = d->committed.data; - while (data.count() % 4) data.append('\0'); - prog.signalTableOffset = data.count(); - data += d->buildSignalTable(); - while (data.count() % 4) data.append('\0'); - prog.exceptionDataOffset = data.count(); - data += d->buildExceptionData(); - - prog.dataLength = 4 * ((data.size() + 3) / 4); - prog.subscriptions = d->committed.subscriptionCount; - prog.instructionCount = bytecode.count(); - int size = sizeof(QV4Program) + bytecode.count(); - size += prog.dataLength; - - programData.resize(size); - memcpy(programData.data(), &prog, sizeof(QV4Program)); - if (prog.dataLength) - memcpy((char *)((QV4Program *)programData.data())->data(), data.constData(), - data.size()); - memcpy((char *)((QV4Program *)programData.data())->instructions(), bytecode.constData(), - bytecode.count()); - } - - if (bindingsDump()) { - qWarning().nospace() << "Subscription slots:"; - - QQmlAssociationList<QString, int> subscriptionIds; - foreach (subscriptionIds, d->committed.subscriptions) { - for (QQmlAssociationList<QString, int>::ConstIterator iter = subscriptionIds.begin(); - iter != subscriptionIds.end(); ++iter) { - qWarning().nospace() << " " << iter->first << "\t-> " << iter->second; - } - } - QV4Compiler::dump(programData); - } - - return programData; -} - -void QV4Compiler::enableBindingsTest(bool e) -{ - if (e) - qmlBindingsTest = true; - else - qmlBindingsTest = qmlBindingsTestEnv(); -} - -void QV4Compiler::enableV4(bool e) -{ - qmlEnableV4 = e; -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4compiler_p_p.h b/src/qml/qml/v4/qv4compiler_p_p.h deleted file mode 100644 index 58ec521a97..0000000000 --- a/src/qml/qml/v4/qv4compiler_p_p.h +++ /dev/null @@ -1,245 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4COMPILER_P_P_H -#define QV4COMPILER_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qv4instruction_p.h" -#include "qv4ir_p.h" -#include <private/qqmlscript_p.h> -#include <private/qqmlimport_p.h> -#include <private/qqmlengine_p.h> - -QT_BEGIN_NAMESPACE - -template <typename _Key, typename _Value> -class QQmlAssociationList -{ -public: - typedef QVarLengthArray<QPair<_Key, _Value>, 8> Container; - typedef typename Container::const_iterator const_iterator; - typedef typename Container::const_iterator ConstIterator; - - const_iterator begin() const { return _container.begin(); } - const_iterator end() const { return _container.end(); } - int count() const { return _container.count(); } - void clear() { _container.clear(); } - - _Value *value(const _Key &key) { - for (int i = 0; i < _container.size(); ++i) { - QPair<_Key, _Value> &p = _container[i]; - if (p.first == key) - return &p.second; - } - return 0; - } - - _Value &operator[](const _Key &key) { - for (int i = 0; i < _container.size(); ++i) { - QPair<_Key, _Value> &p = _container[i]; - if (p.first == key) - return p.second; - } - int index = _container.size(); - _container.append(qMakePair(key, _Value())); - return _container[index].second; - } - - void insert(const _Key &key, _Value &value) { - for (int i = 0; i < _container.size(); ++i) { - QPair<_Key, _Value> &p = _container[i]; - if (p.first == key) { - p.second = value; - return; - } - } - _container.append(qMakePair(key, value)); - } - -private: - Container _container; -}; - -class QV4CompilerPrivate: protected QQmlJS::IR::ExprVisitor, - protected QQmlJS::IR::StmtVisitor -{ -public: - QV4CompilerPrivate(); - - void resetInstanceState(); - int commitCompile(); - - const QV4Compiler::Expression *expression; - QQmlEnginePrivate *engine; - - QString contextName() const { return QLatin1String("$$$SCOPE_") + QString::number((quintptr)expression->context, 16); } - - bool compile(QQmlJS::AST::Node *); - - bool isInvalidatable() const { return invalidatable; } - - int registerLiteralString(quint8 reg, const QStringRef &); - QByteArray data; - - bool blockNeedsSubscription(const QStringList &); - int subscriptionIndex(const QStringList &); - quint32 subscriptionBlockMask(const QStringList &); - - quint8 exceptionId(quint16 line, quint16 column); - quint8 exceptionId(QQmlJS::AST::ExpressionNode *); - QVector<quint32> exceptions; - - QQmlAssociationList<int, quint32> usedSubscriptionIds; - int subscriptionOffset; - QQmlAssociationList<QString, int> subscriptionIds; - QQmlJS::Bytecode bytecode; - - // back patching - struct Patch { - QQmlJS::IR::BasicBlock *block; // the basic block - int offset; // the index of the instruction to patch - Patch(QQmlJS::IR::BasicBlock *block = 0, int index = -1) - : block(block), offset(index) {} - }; - QVector<Patch> patches; - QQmlPool pool; - - // Committed binding data - struct Committed { - Committed(): subscriptionCount(0) {} - QList<int> offsets; - QList<QQmlAssociationList<int, quint32> > dependencies; - - //QQmlJS::Bytecode bytecode; - QByteArray bytecode; - QByteArray data; - QVector<quint32> exceptions; - int subscriptionCount; - QList<QQmlAssociationList<QString, int> > subscriptions; - - int count() const { return offsets.count(); } - } committed; - - QByteArray buildSignalTable() const; - QByteArray buildExceptionData() const; - - void convertToNumber(QQmlJS::IR::Expr *expr, int reg); - void convertToInt(QQmlJS::IR::Expr *expr, int reg); - void convertToBool(QQmlJS::IR::Expr *expr, int reg); - quint8 instructionOpcode(QQmlJS::IR::Binop *e); - - struct Instr { -#define QML_V4_INSTR_DATA_TYPEDEF(I, FMT) typedef QQmlJS::V4InstrData<QQmlJS::V4Instr::I> I; - FOR_EACH_V4_INSTR(QML_V4_INSTR_DATA_TYPEDEF) -#undef QML_v4_INSTR_DATA_TYPEDEF - private: - Instr(); - }; - -protected: - // - // tracing - // - void trace(quint16 line, quint16 column); - void trace(QVector<QQmlJS::IR::BasicBlock *> *blocks); - void traceExpression(QQmlJS::IR::Expr *e, quint8 r); - - template <int Instr> - inline void gen(const QQmlJS::V4InstrData<Instr> &i) - { bytecode.append(i); } - inline void gen(QQmlJS::V4Instr::Type type, QQmlJS::V4Instr &instr) - { bytecode.append(type, instr); } - - inline QQmlJS::V4Instr::Type instructionType(const QQmlJS::V4Instr *i) const - { return bytecode.instructionType(i); } - - // - // expressions - // - virtual void visitConst(QQmlJS::IR::Const *e); - virtual void visitString(QQmlJS::IR::String *e); - virtual void visitName(QQmlJS::IR::Name *e); - virtual void visitTemp(QQmlJS::IR::Temp *e); - virtual void visitUnop(QQmlJS::IR::Unop *e); - virtual void visitBinop(QQmlJS::IR::Binop *e); - virtual void visitCall(QQmlJS::IR::Call *e); - - // - // statements - // - virtual void visitExp(QQmlJS::IR::Exp *s); - virtual void visitMove(QQmlJS::IR::Move *s); - virtual void visitJump(QQmlJS::IR::Jump *s); - virtual void visitCJump(QQmlJS::IR::CJump *s); - virtual void visitRet(QQmlJS::IR::Ret *s); - -private: - QStringList _subscribeName; - QQmlJS::IR::Function *_function; - QQmlJS::IR::BasicBlock *_block; - void discard() { _discarded = true; } - bool _discarded; - quint8 currentReg; - quint8 registerCount; - - bool usedSubscriptionIdsChanged; - quint32 currentBlockMask; - quint16 bindingLine; - quint16 bindingColumn; - bool invalidatable; -}; - - -QT_END_NAMESPACE - -#endif // QV4COMPILER_P_P_H - diff --git a/src/qml/qml/v4/qv4context.cpp b/src/qml/qml/v4/qv4context.cpp new file mode 100644 index 0000000000..ed01548410 --- /dev/null +++ b/src/qml/qml/v4/qv4context.cpp @@ -0,0 +1,615 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QString> +#include "qv4debugging_p.h" +#include <qv4context_p.h> +#include <qv4object_p.h> +#include <qv4objectproto_p.h> +#include "qv4mm_p.h" +#include <qv4argumentsobject_p.h> +#include "qv4function_p.h" + +using namespace QV4; + +DiagnosticMessage::DiagnosticMessage() + : offset(0) + , length(0) + , startLine(0) + , startColumn(0) + , type(0) + , next(0) +{} + +DiagnosticMessage::~DiagnosticMessage() +{ + delete next; +} + +String *DiagnosticMessage::buildFullMessage(ExecutionContext *ctx) const +{ + QString msg; + if (!fileName.isEmpty()) + msg = fileName + QLatin1Char(':'); + msg += QString::number(startLine) + QLatin1Char(':') + QString::number(startColumn) + QLatin1String(": "); + if (type == QV4::DiagnosticMessage::Error) + msg += QLatin1String("error"); + else + msg += QLatin1String("warning"); + msg += ": " + message; + + return ctx->engine->newString(msg); +} + +void ExecutionContext::createMutableBinding(String *name, bool deletable) +{ + + // find the right context to create the binding on + Object *activation = engine->globalObject; + ExecutionContext *ctx = this; + while (ctx) { + if (ctx->type >= Type_CallContext) { + CallContext *c = static_cast<CallContext *>(ctx); + if (!c->activation) + c->activation = engine->newObject(); + activation = c->activation; + break; + } + ctx = ctx->outer; + } + + if (activation->__hasProperty__(name)) + return; + Property desc = Property::fromValue(Value::undefinedValue()); + PropertyAttributes attrs(Attr_Data); + attrs.setConfigurable(deletable); + activation->__defineOwnProperty__(this, name, desc, attrs); +} + +String * const *ExecutionContext::formals() const +{ + return type >= Type_CallContext ? static_cast<const CallContext *>(this)->function->formalParameterList : 0; +} + +unsigned int ExecutionContext::formalCount() const +{ + return type >= Type_CallContext ? static_cast<const CallContext *>(this)->function->formalParameterCount : 0; +} + +String * const *ExecutionContext::variables() const +{ + return type >= Type_CallContext ? static_cast<const CallContext *>(this)->function->varList : 0; +} + +unsigned int ExecutionContext::variableCount() const +{ + return type >= Type_CallContext ? static_cast<const CallContext *>(this)->function->varCount : 0; +} + + +void GlobalContext::initGlobalContext(ExecutionEngine *eng) +{ + initBaseContext(Type_GlobalContext, eng, /*parentContext*/0); + thisObject = Value::fromObject(eng->globalObject); + global = 0; +} + +void WithContext::initWithContext(ExecutionContext *p, Object *with) +{ + initBaseContext(Type_WithContext, p->engine, p); + thisObject = p->thisObject; + outer = p; + lookups = p->lookups; + + withObject = with; +} + +void CatchContext::initCatchContext(ExecutionContext *p, String *exceptionVarName, const Value &exceptionValue) +{ + initBaseContext(Type_CatchContext, p->engine, p); + strictMode = p->strictMode; + thisObject = p->thisObject; + outer = p; + lookups = p->lookups; + + this->exceptionVarName = exceptionVarName; + this->exceptionValue = exceptionValue; +} + +void CallContext::initCallContext(ExecutionContext *parentContext, FunctionObject *function, Value *_arguments, int _argumentCount, const Value &_thisObject) +{ + initBaseContext(Type_CallContext, parentContext->engine, parentContext); + + this->function = function; + this->arguments = _arguments; + this->argumentCount = _argumentCount; + this->thisObject = _thisObject; + + strictMode = function->strictMode; + marked = false; + outer = function->scope; +#ifndef QT_NO_DEBUG + assert(outer->next != (ExecutionContext *)0x1); +#endif + + activation = 0; + + if (function->function) + lookups = function->function->lookups; + + uint argc = argumentCount; + + locals = (Value *)(this + 1); + if (function->varCount) + std::fill(locals, locals + function->varCount, Value::undefinedValue()); + + if (needsOwnArguments()) { + Value *args = arguments; + argumentCount = qMax(argc, function->formalParameterCount); + arguments = locals + function->varCount; + if (argc) + ::memcpy(arguments, args, argc * sizeof(Value)); + if (argc < function->formalParameterCount) + std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue()); + + } + + if (function->usesArgumentsObject) { + ArgumentsObject *args = new (engine->memoryManager) ArgumentsObject(this, function->formalParameterCount, argc); + args->prototype = engine->objectPrototype; + activation = engine->newObject(); + Property desc = Property::fromValue(Value::fromObject(args)); + activation->__defineOwnProperty__(this, engine->id_arguments, desc, Attr_NotConfigurable); + } +} + +void CallContext::initQmlContext(ExecutionContext *parentContext, Object *qml, FunctionObject *function) +{ + initBaseContext(Type_QmlContext, parentContext->engine, parentContext); + + this->function = function; + this->arguments = 0; + this->argumentCount = 0; + this->thisObject = Value::undefinedValue(); + + strictMode = true; + marked = false; + this->outer = function->scope; +#ifndef QT_NO_DEBUG + assert(outer->next != (ExecutionContext *)0x1); +#endif + + activation = qml; + + lookups = function->function->lookups; + + locals = (Value *)(this + 1); + if (function->varCount) + std::fill(locals, locals + function->varCount, Value::undefinedValue()); +} + + +bool ExecutionContext::deleteProperty(String *name) +{ + bool hasWith = false; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (ctx->type == Type_WithContext) { + hasWith = true; + WithContext *w = static_cast<WithContext *>(ctx); + if (w->withObject->__hasProperty__(name)) + return w->withObject->deleteProperty(name); + } else if (ctx->type == Type_CatchContext) { + CatchContext *c = static_cast<CatchContext *>(ctx); + if (c->exceptionVarName->isEqualTo(name)) + return false; + } else if (ctx->type >= Type_CallContext) { + CallContext *c = static_cast<CallContext *>(ctx); + FunctionObject *f = c->function; + if (f->needsActivation || hasWith) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return false; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return false; + } + if (c->activation && c->activation->__hasProperty__(name)) + return c->activation->deleteProperty(name); + } else if (ctx->type == Type_GlobalContext) { + GlobalContext *g = static_cast<GlobalContext *>(ctx); + if (g->global->__hasProperty__(name)) + return g->global->deleteProperty(name); + } + } + + if (strictMode) + throwSyntaxError(0); + return true; +} + +bool CallContext::needsOwnArguments() const +{ + return function->needsActivation || argumentCount < function->formalParameterCount; +} + +void ExecutionContext::mark() +{ + if (marked) + return; + marked = true; + + if (type != Type_SimpleCallContext && outer) + outer->mark(); + + thisObject.mark(); + + if (type >= Type_SimpleCallContext) { + QV4::CallContext *c = static_cast<CallContext *>(this); + for (unsigned arg = 0, lastArg = c->argumentCount; arg < lastArg; ++arg) + c->arguments[arg].mark(); + if (type >= Type_CallContext) { + for (unsigned local = 0, lastLocal = c->variableCount(); local < lastLocal; ++local) + c->locals[local].mark(); + if (c->activation) + c->activation->mark(); + c->function->mark(); + } + } else if (type == Type_WithContext) { + WithContext *w = static_cast<WithContext *>(this); + w->withObject->mark(); + } else if (type == Type_CatchContext) { + CatchContext *c = static_cast<CatchContext *>(this); + if (c->exceptionVarName) + c->exceptionVarName->mark(); + c->exceptionValue.mark(); + } else if (type == Type_GlobalContext) { + GlobalContext *g = static_cast<GlobalContext *>(this); + g->global->mark(); + } +} + +void ExecutionContext::setProperty(String *name, const Value& value) +{ + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (ctx->type == Type_WithContext) { + Object *w = static_cast<WithContext *>(ctx)->withObject; + if (w->__hasProperty__(name)) { + w->put(name, value); + return; + } + } else if (ctx->type == Type_CatchContext && static_cast<CatchContext *>(ctx)->exceptionVarName->isEqualTo(name)) { + static_cast<CatchContext *>(ctx)->exceptionValue = value; + return; + } else { + Object *activation = 0; + if (ctx->type >= Type_CallContext) { + CallContext *c = static_cast<CallContext *>(ctx); + for (unsigned int i = 0; i < c->function->varCount; ++i) + if (c->function->varList[i]->isEqualTo(name)) { + c->locals[i] = value; + return; + } + for (int i = (int)c->function->formalParameterCount - 1; i >= 0; --i) + if (c->function->formalParameterList[i]->isEqualTo(name)) { + c->arguments[i] = value; + return; + } + activation = c->activation; + } else if (ctx->type == Type_GlobalContext) { + activation = static_cast<GlobalContext *>(ctx)->global; + } + + if (activation && (ctx->type == Type_QmlContext || activation->__hasProperty__(name))) { + activation->put(name, value); + return; + } + } + } + if (strictMode || name->isEqualTo(engine->id_this)) + throwReferenceError(Value::fromString(name)); + engine->globalObject->put(name, value); +} + +Value ExecutionContext::getProperty(String *name) +{ + name->makeIdentifier(); + + if (name->isEqualTo(engine->id_this)) + return thisObject; + + bool hasWith = false; + bool hasCatchScope = false; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (ctx->type == Type_WithContext) { + Object *w = static_cast<WithContext *>(ctx)->withObject; + hasWith = true; + bool hasProperty = false; + Value v = w->get(name, &hasProperty); + if (hasProperty) { + return v; + } + continue; + } + + else if (ctx->type == Type_CatchContext) { + hasCatchScope = true; + CatchContext *c = static_cast<CatchContext *>(ctx); + if (c->exceptionVarName->isEqualTo(name)) + return c->exceptionValue; + } + + else if (ctx->type >= Type_CallContext) { + QV4::CallContext *c = static_cast<CallContext *>(ctx); + FunctionObject *f = c->function; + if (f->needsActivation || hasWith || hasCatchScope) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return c->locals[i]; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return c->arguments[i]; + } + if (c->activation) { + bool hasProperty = false; + Value v = c->activation->get(name, &hasProperty); + if (hasProperty) + return v; + } + if (f->function && f->function->isNamedExpression + && name->isEqualTo(f->function->name)) + return Value::fromObject(c->function); + } + + else if (ctx->type == Type_GlobalContext) { + GlobalContext *g = static_cast<GlobalContext *>(ctx); + bool hasProperty = false; + Value v = g->global->get(name, &hasProperty); + if (hasProperty) + return v; + } + } + throwReferenceError(Value::fromString(name)); + return Value::undefinedValue(); +} + +Value ExecutionContext::getPropertyNoThrow(String *name) +{ + name->makeIdentifier(); + + if (name->isEqualTo(engine->id_this)) + return thisObject; + + bool hasWith = false; + bool hasCatchScope = false; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (ctx->type == Type_WithContext) { + Object *w = static_cast<WithContext *>(ctx)->withObject; + hasWith = true; + bool hasProperty = false; + Value v = w->get(name, &hasProperty); + if (hasProperty) { + return v; + } + continue; + } + + else if (ctx->type == Type_CatchContext) { + hasCatchScope = true; + CatchContext *c = static_cast<CatchContext *>(ctx); + if (c->exceptionVarName->isEqualTo(name)) + return c->exceptionValue; + } + + else if (ctx->type >= Type_CallContext) { + QV4::CallContext *c = static_cast<CallContext *>(ctx); + FunctionObject *f = c->function; + if (f->needsActivation || hasWith || hasCatchScope) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return c->locals[i]; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return c->arguments[i]; + } + if (c->activation) { + bool hasProperty = false; + Value v = c->activation->get(name, &hasProperty); + if (hasProperty) + return v; + } + if (f->function && f->function->isNamedExpression + && name->isEqualTo(f->function->name)) + return Value::fromObject(c->function); + } + + else if (ctx->type == Type_GlobalContext) { + GlobalContext *g = static_cast<GlobalContext *>(ctx); + bool hasProperty = false; + Value v = g->global->get(name, &hasProperty); + if (hasProperty) + return v; + } + } + return Value::undefinedValue(); +} + +Value ExecutionContext::getPropertyAndBase(String *name, Object **base) +{ + *base = 0; + name->makeIdentifier(); + + if (name->isEqualTo(engine->id_this)) + return thisObject; + + bool hasWith = false; + bool hasCatchScope = false; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) { + if (ctx->type == Type_WithContext) { + Object *w = static_cast<WithContext *>(ctx)->withObject; + hasWith = true; + bool hasProperty = false; + Value v = w->get(name, &hasProperty); + if (hasProperty) { + *base = w; + return v; + } + continue; + } + + else if (ctx->type == Type_CatchContext) { + hasCatchScope = true; + CatchContext *c = static_cast<CatchContext *>(ctx); + if (c->exceptionVarName->isEqualTo(name)) + return c->exceptionValue; + } + + else if (ctx->type >= Type_CallContext) { + QV4::CallContext *c = static_cast<CallContext *>(ctx); + FunctionObject *f = c->function; + if (f->needsActivation || hasWith || hasCatchScope) { + for (unsigned int i = 0; i < f->varCount; ++i) + if (f->varList[i]->isEqualTo(name)) + return c->locals[i]; + for (int i = (int)f->formalParameterCount - 1; i >= 0; --i) + if (f->formalParameterList[i]->isEqualTo(name)) + return c->arguments[i]; + } + if (c->activation) { + bool hasProperty = false; + Value v = c->activation->get(name, &hasProperty); + if (hasProperty) { + *base = c->activation; + return v; + } + } + if (f->function && f->function->isNamedExpression + && name->isEqualTo(f->function->name)) + return Value::fromObject(c->function); + } + + else if (ctx->type == Type_GlobalContext) { + GlobalContext *g = static_cast<GlobalContext *>(ctx); + bool hasProperty = false; + Value v = g->global->get(name, &hasProperty); + if (hasProperty) + return v; + } + } + throwReferenceError(Value::fromString(name)); + return Value::undefinedValue(); +} + + + +void ExecutionContext::inplaceBitOp(String *name, const Value &value, BinOp op) +{ + Value lhs = getProperty(name); + Value result; + op(this, &result, lhs, value); + setProperty(name, result); +} + +void ExecutionContext::throwError(const Value &value) +{ + __qmljs_throw(this, value); +} + +void ExecutionContext::throwError(const QString &message) +{ + Value v = Value::fromString(this, message); + throwError(Value::fromObject(engine->newErrorObject(v))); +} + +void ExecutionContext::throwSyntaxError(DiagnosticMessage *message) +{ + throwError(Value::fromObject(engine->newSyntaxErrorObject(this, message))); +} + +void ExecutionContext::throwTypeError() +{ + throwError(Value::fromObject(engine->newTypeErrorObject(QStringLiteral("Type error")))); +} + +void ExecutionContext::throwTypeError(const QString &message) +{ + throwError(Value::fromObject(engine->newTypeErrorObject(message))); +} + +void ExecutionContext::throwUnimplemented(const QString &message) +{ + Value v = Value::fromString(this, QStringLiteral("Unimplemented ") + message); + throwError(Value::fromObject(engine->newErrorObject(v))); +} + +void ExecutionContext::throwReferenceError(Value value) +{ + String *s = value.toString(this); + QString msg = s->toQString() + QStringLiteral(" is not defined"); + throwError(Value::fromObject(engine->newReferenceErrorObject(msg))); +} + +void ExecutionContext::throwReferenceError(Value value, const QString &fileName, int line) +{ + String *s = value.toString(this); + QString msg = s->toQString() + QStringLiteral(" is not defined"); + throwError(Value::fromObject(engine->newReferenceErrorObject(msg, fileName, line))); +} + +void ExecutionContext::throwRangeError(Value value) +{ + String *s = value.toString(this); + QString msg = s->toQString() + QStringLiteral(" out of range"); + throwError(Value::fromObject(engine->newRangeErrorObject(msg))); +} + +void ExecutionContext::throwURIError(Value msg) +{ + throwError(Value::fromObject(engine->newURIErrorObject(msg))); +} + + +void SimpleCallContext::initSimpleCallContext(ExecutionEngine *engine) +{ + initBaseContext(Type_SimpleCallContext, engine, engine->current); + function = 0; + arguments = 0; + argumentCount = 0; +} diff --git a/src/qml/qml/v4/qv4context_p.h b/src/qml/qml/v4/qv4context_p.h new file mode 100644 index 0000000000..dfe02bdcc8 --- /dev/null +++ b/src/qml/qml/v4/qv4context_p.h @@ -0,0 +1,227 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_ENVIRONMENT_H +#define QMLJS_ENVIRONMENT_H + +#include "qv4global_p.h" +#include "qv4runtime_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Object; +struct ExecutionEngine; +struct DeclarativeEnvironment; +struct Lookup; + +struct Q_QML_EXPORT DiagnosticMessage +{ + enum { Error, Warning }; + + QString fileName; + quint32 offset; + quint32 length; + quint32 startLine; + unsigned startColumn: 31; + unsigned type: 1; + QString message; + DiagnosticMessage *next; + + DiagnosticMessage(); + ~DiagnosticMessage(); + String *buildFullMessage(ExecutionContext *ctx) const; +}; + +struct CallContext; + +struct Q_QML_EXPORT ExecutionContext +{ + enum Type { + Type_GlobalContext = 0x1, + Type_CatchContext = 0x2, + Type_WithContext = 0x3, + Type_SimpleCallContext = 0x4, + Type_CallContext = 0x5, + Type_QmlContext = 0x6 + }; + + Type type; + bool strictMode; + bool marked; + + Value thisObject; + + ExecutionEngine *engine; + ExecutionContext *parent; + ExecutionContext *outer; + Lookup *lookups; + ExecutionContext *next; // used in the GC + + struct EvalCode + { + Function *function; + EvalCode *next; + }; + EvalCode *currentEvalCode; + + const uchar **interpreterInstructionPointer; + + void initBaseContext(Type type, ExecutionEngine *engine, ExecutionContext *parentContext) + { + this->type = type; + strictMode = false; + marked = false; + thisObject = Value::undefinedValue(); + this->engine = engine; + parent = parentContext; + outer = 0; + lookups = 0; + currentEvalCode = 0; + interpreterInstructionPointer = 0; + } + + String * const *formals() const; + unsigned int formalCount() const; + String * const *variables() const; + unsigned int variableCount() const; + + void createMutableBinding(String *name, bool deletable); + + void Q_NORETURN throwError(const Value &value); + void Q_NORETURN throwError(const QString &message); + void Q_NORETURN throwSyntaxError(DiagnosticMessage *message); + void Q_NORETURN throwTypeError(); + void Q_NORETURN throwTypeError(const QString &message); + void Q_NORETURN throwReferenceError(Value value); + void Q_NORETURN throwReferenceError(Value value, const QString &fileName, int line); + void Q_NORETURN throwRangeError(Value value); + void Q_NORETURN throwURIError(Value msg); + void Q_NORETURN throwUnimplemented(const QString &message); + + void setProperty(String *name, const Value &value); + Value getProperty(String *name); + Value getPropertyNoThrow(String *name); + Value getPropertyAndBase(String *name, Object **base); + void inplaceBitOp(String *name, const QV4::Value &value, BinOp op); + bool deleteProperty(String *name); + + inline Value argument(unsigned int index = 0); + + void mark(); + + inline CallContext *asCallContext(); + inline const CallContext *asCallContext() const; +}; + +struct SimpleCallContext : public ExecutionContext +{ + void initSimpleCallContext(ExecutionEngine *engine); + FunctionObject *function; + Value *arguments; + unsigned int argumentCount; +}; + +struct CallContext : public SimpleCallContext +{ + void initCallContext(ExecutionContext *parentContext, FunctionObject *function, Value *args, int argc, + const Value &thisObject); + void initQmlContext(ExecutionContext *parentContext, Object *qml, QV4::FunctionObject *function); + bool needsOwnArguments() const; + + Value *locals; + Object *activation; +}; + +struct GlobalContext : public ExecutionContext +{ + void initGlobalContext(ExecutionEngine *e); + + Object *global; +}; + +struct CatchContext : public ExecutionContext +{ + void initCatchContext(ExecutionContext *p, String *exceptionVarName, const QV4::Value &exceptionValue); + + String *exceptionVarName; + Value exceptionValue; +}; + +struct WithContext : public ExecutionContext +{ + Object *withObject; + + void initWithContext(ExecutionContext *p, Object *with); +}; + +inline Value ExecutionContext::argument(unsigned int index) +{ + if (type >= Type_SimpleCallContext) { + CallContext *ctx = static_cast<CallContext *>(this); + if (index < ctx->argumentCount) + return ctx->arguments[index]; + } + return Value::undefinedValue(); +} + +inline CallContext *ExecutionContext::asCallContext() +{ + return type >= Type_CallContext ? static_cast<CallContext *>(this) : 0; +} + +inline const CallContext *ExecutionContext::asCallContext() const +{ + return type >= Type_CallContext ? static_cast<const CallContext *>(this) : 0; +} + +/* Function *f, int argc */ +#define requiredMemoryForExecutionContect(f, argc) \ + sizeof(CallContext) + sizeof(Value) * (f->varCount + qMax((uint)argc, f->formalParameterCount)) +#define requiredMemoryForQmlExecutionContect(f) \ + sizeof(CallContext) + sizeof(Value) * (f->locals.size()) +#define stackContextSize (sizeof(CallContext) + 32*sizeof(Value)) + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4/qv4dateobject.cpp b/src/qml/qml/v4/qv4dateobject.cpp new file mode 100644 index 0000000000..3cf6cb1aeb --- /dev/null +++ b/src/qml/qml/v4/qv4dateobject.cpp @@ -0,0 +1,1316 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4dateobject_p.h" +#include "qv4objectproto_p.h" +#include "qv4mm_p.h" +#include <QtCore/qnumeric.h> +#include <QtCore/qmath.h> +#include <QtCore/QDateTime> +#include <QtCore/QStringList> +#include <QtCore/QDebug> +#include <cmath> +#include <qmath.h> +#include <qnumeric.h> +#include <cassert> +#include <time.h> + +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsast_p.h> +#include <qv4jsir_p.h> +#include <qv4codegen_p.h> +#include <qv4isel_masm_p.h> + +#include <wtf/MathExtras.h> + +#ifdef Q_OS_WIN +# include <windows.h> +#else +# ifndef Q_OS_VXWORKS +# include <sys/time.h> +# else +# include "qplatformdefs.h" +# endif +#endif + +using namespace QV4; + +static const double HoursPerDay = 24.0; +static const double MinutesPerHour = 60.0; +static const double SecondsPerMinute = 60.0; +static const double msPerSecond = 1000.0; +static const double msPerMinute = 60000.0; +static const double msPerHour = 3600000.0; +static const double msPerDay = 86400000.0; + +static double LocalTZA = 0.0; // initialized at startup + +static inline double TimeWithinDay(double t) +{ + double r = ::fmod(t, msPerDay); + return (r >= 0) ? r : r + msPerDay; +} + +static inline int HourFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerHour), HoursPerDay)); + return (r >= 0) ? r : r + int(HoursPerDay); +} + +static inline int MinFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerMinute), MinutesPerHour)); + return (r >= 0) ? r : r + int(MinutesPerHour); +} + +static inline int SecFromTime(double t) +{ + int r = int(::fmod(::floor(t / msPerSecond), SecondsPerMinute)); + return (r >= 0) ? r : r + int(SecondsPerMinute); +} + +static inline int msFromTime(double t) +{ + int r = int(::fmod(t, msPerSecond)); + return (r >= 0) ? r : r + int(msPerSecond); +} + +static inline double Day(double t) +{ + return ::floor(t / msPerDay); +} + +static inline double DaysInYear(double y) +{ + if (::fmod(y, 4)) + return 365; + + else if (::fmod(y, 100)) + return 366; + + else if (::fmod(y, 400)) + return 365; + + return 366; +} + +static inline double DayFromYear(double y) +{ + return 365 * (y - 1970) + + ::floor((y - 1969) / 4) + - ::floor((y - 1901) / 100) + + ::floor((y - 1601) / 400); +} + +static inline double TimeFromYear(double y) +{ + return msPerDay * DayFromYear(y); +} + +static inline double YearFromTime(double t) +{ + int y = 1970; + y += (int) ::floor(t / (msPerDay * 365.2425)); + + double t2 = TimeFromYear(y); + return (t2 > t) ? y - 1 : ((t2 + msPerDay * DaysInYear(y)) <= t) ? y + 1 : y; +} + +static inline bool InLeapYear(double t) +{ + double x = DaysInYear(YearFromTime(t)); + if (x == 365) + return 0; + + assert(x == 366); + return 1; +} + +static inline double DayWithinYear(double t) +{ + return Day(t) - DayFromYear(YearFromTime(t)); +} + +static inline double MonthFromTime(double t) +{ + double d = DayWithinYear(t); + double l = InLeapYear(t); + + if (d < 31.0) + return 0; + + else if (d < 59.0 + l) + return 1; + + else if (d < 90.0 + l) + return 2; + + else if (d < 120.0 + l) + return 3; + + else if (d < 151.0 + l) + return 4; + + else if (d < 181.0 + l) + return 5; + + else if (d < 212.0 + l) + return 6; + + else if (d < 243.0 + l) + return 7; + + else if (d < 273.0 + l) + return 8; + + else if (d < 304.0 + l) + return 9; + + else if (d < 334.0 + l) + return 10; + + else if (d < 365.0 + l) + return 11; + + return qSNaN(); // ### assert? +} + +static inline double DateFromTime(double t) +{ + int m = (int) Value::toInteger(MonthFromTime(t)); + double d = DayWithinYear(t); + double l = InLeapYear(t); + + switch (m) { + case 0: return d + 1.0; + case 1: return d - 30.0; + case 2: return d - 58.0 - l; + case 3: return d - 89.0 - l; + case 4: return d - 119.0 - l; + case 5: return d - 150.0 - l; + case 6: return d - 180.0 - l; + case 7: return d - 211.0 - l; + case 8: return d - 242.0 - l; + case 9: return d - 272.0 - l; + case 10: return d - 303.0 - l; + case 11: return d - 333.0 - l; + } + + return qSNaN(); // ### assert +} + +static inline double WeekDay(double t) +{ + double r = ::fmod (Day(t) + 4.0, 7.0); + return (r >= 0) ? r : r + 7.0; +} + + +static inline double MakeTime(double hour, double min, double sec, double ms) +{ + return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms; +} + +static inline double DayFromMonth(double month, double leap) +{ + switch ((int) month) { + case 0: return 0; + case 1: return 31.0; + case 2: return 59.0 + leap; + case 3: return 90.0 + leap; + case 4: return 120.0 + leap; + case 5: return 151.0 + leap; + case 6: return 181.0 + leap; + case 7: return 212.0 + leap; + case 8: return 243.0 + leap; + case 9: return 273.0 + leap; + case 10: return 304.0 + leap; + case 11: return 334.0 + leap; + } + + return qSNaN(); // ### assert? +} + +static double MakeDay(double year, double month, double day) +{ + year += ::floor(month / 12.0); + + month = ::fmod(month, 12.0); + if (month < 0) + month += 12.0; + + double d = DayFromYear(year); + bool leap = InLeapYear(d*msPerDay); + + d += DayFromMonth(month, leap); + d += day - 1; + + return d; +} + +static inline double MakeDate(double day, double time) +{ + return day * msPerDay + time; +} + +static inline double DaylightSavingTA(double t) +{ + struct tm tmtm; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + __time64_t tt = (__time64_t)(t / msPerSecond); + // _localtime_64_s returns non-zero on failure + if (_localtime64_s(&tmtm, &tt) != 0) +#else + long int tt = (long int)(t / msPerSecond); + if (!localtime_r((const time_t*) &tt, &tmtm)) +#endif + return 0; + return (tmtm.tm_isdst > 0) ? msPerHour : 0; +} + +static inline double LocalTime(double t) +{ + return t + LocalTZA + DaylightSavingTA(t); +} + +static inline double UTC(double t) +{ + return t - LocalTZA - DaylightSavingTA(t - LocalTZA); +} + +static inline double currentTime() +{ +#ifndef Q_OS_WIN + struct timeval tv; + + gettimeofday(&tv, 0); + return ::floor(tv.tv_sec * msPerSecond + (tv.tv_usec / 1000.0)); +#else + SYSTEMTIME st; + GetSystemTime(&st); + FILETIME ft; + SystemTimeToFileTime(&st, &ft); + LARGE_INTEGER li; + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + return double(li.QuadPart - Q_INT64_C(116444736000000000)) / 10000.0; +#endif +} + +static inline double TimeClip(double t) +{ + if (! qIsFinite(t) || fabs(t) > 8.64e15) + return qSNaN(); + return Value::toInteger(t); +} + +static inline double ParseString(const QString &s) +{ + // first try the format defined in 15.9.1.15, only if that fails fall back to + // QDateTime for parsing + + // the define string format is YYYY-MM-DDTHH:mm:ss.sssZ + // It can be date or time only, and the second and later components + // of both fields are optional + // and extended syntax for negative and large positive years exists: +/-YYYYYY + + enum Format { + Year, + Month, + Day, + Hour, + Minute, + Second, + MilliSecond, + TimezoneHour, + TimezoneMinute, + Done + }; + + const QChar *ch = s.constData(); + const QChar *end = ch + s.length(); + + uint format = Year; + int current = 0; + int currentSize = 0; + bool extendedYear = false; + + int yearSign = 1; + int year = 0; + int month = 0; + int day = 1; + int hour = 0; + int minute = 0; + int second = 0; + int msec = 0; + int offsetSign = 1; + int offset = 0; + + bool error = false; + if (*ch == '+' || *ch == '-') { + extendedYear = true; + if (*ch == '-') + yearSign = -1; + ++ch; + } + while (ch <= end) { + if (*ch >= '0' && *ch <= '9') { + current *= 10; + current += ch->unicode() - '0'; + ++currentSize; + } else { // other char, delimits field + switch (format) { + case Year: + year = current; + if (extendedYear) + error = (currentSize != 6); + else + error = (currentSize != 4); + break; + case Month: + month = current - 1; + error = (currentSize != 2) || month > 11; + break; + case Day: + day = current; + error = (currentSize != 2) || day > 31; + break; + case Hour: + hour = current; + error = (currentSize != 2) || hour > 24; + break; + case Minute: + minute = current; + error = (currentSize != 2) || minute > 60; + break; + case Second: + second = current; + error = (currentSize != 2) || second > 60; + break; + case MilliSecond: + msec = current; + error = (currentSize != 3); + break; + case TimezoneHour: + offset = current*60; + error = (currentSize != 2) || offset > 23*60; + break; + case TimezoneMinute: + offset += current; + error = (currentSize != 2) || current >= 60; + break; + } + if (*ch == 'T') { + if (format >= Hour) + error = true; + format = Hour; + } else if (*ch == '-') { + if (format < Day) + ++format; + else if (format < Minute) + error = true; + else if (format >= TimezoneHour) + error = true; + else { + offsetSign = -1; + format = TimezoneHour; + } + } else if (*ch == ':') { + if (format != Hour && format != Minute && format != TimezoneHour) + error = true; + ++format; + } else if (*ch == '.') { + if (format != Second) + error = true; + ++format; + } else if (*ch == '+') { + if (format < Minute || format >= TimezoneHour) + error = true; + format = TimezoneHour; + } else if (*ch == 'Z' || *ch == 0) { + format = Done; + } + current = 0; + currentSize = 0; + } + if (error || format == Done) + break; + ++ch; + } + + if (!error) { + double t = MakeDate(MakeDay(year * yearSign, month, day), MakeTime(hour, minute, second, msec)); + t -= offset * offsetSign * 60 * 1000; + return t; + } + + QDateTime dt = QDateTime::fromString(s, Qt::TextDate); + if (!dt.isValid()) + dt = QDateTime::fromString(s, Qt::ISODate); + if (!dt.isValid()) { + QStringList formats; + formats << QStringLiteral("M/d/yyyy") + << QStringLiteral("M/d/yyyy hh:mm") + << QStringLiteral("M/d/yyyy hh:mm A") + + << QStringLiteral("M/d/yyyy, hh:mm") + << QStringLiteral("M/d/yyyy, hh:mm A") + + << QStringLiteral("MMM d yyyy") + << QStringLiteral("MMM d yyyy hh:mm") + << QStringLiteral("MMM d yyyy hh:mm:ss") + << QStringLiteral("MMM d yyyy, hh:mm") + << QStringLiteral("MMM d yyyy, hh:mm:ss") + + << QStringLiteral("MMMM d yyyy") + << QStringLiteral("MMMM d yyyy hh:mm") + << QStringLiteral("MMMM d yyyy hh:mm:ss") + << QStringLiteral("MMMM d yyyy, hh:mm") + << QStringLiteral("MMMM d yyyy, hh:mm:ss") + + << QStringLiteral("MMM d, yyyy") + << QStringLiteral("MMM d, yyyy hh:mm") + << QStringLiteral("MMM d, yyyy hh:mm:ss") + + << QStringLiteral("MMMM d, yyyy") + << QStringLiteral("MMMM d, yyyy hh:mm") + << QStringLiteral("MMMM d, yyyy hh:mm:ss") + + << QStringLiteral("d MMM yyyy") + << QStringLiteral("d MMM yyyy hh:mm") + << QStringLiteral("d MMM yyyy hh:mm:ss") + << QStringLiteral("d MMM yyyy, hh:mm") + << QStringLiteral("d MMM yyyy, hh:mm:ss") + + << QStringLiteral("d MMMM yyyy") + << QStringLiteral("d MMMM yyyy hh:mm") + << QStringLiteral("d MMMM yyyy hh:mm:ss") + << QStringLiteral("d MMMM yyyy, hh:mm") + << QStringLiteral("d MMMM yyyy, hh:mm:ss") + + << QStringLiteral("d MMM, yyyy") + << QStringLiteral("d MMM, yyyy hh:mm") + << QStringLiteral("d MMM, yyyy hh:mm:ss") + + << QStringLiteral("d MMMM, yyyy") + << QStringLiteral("d MMMM, yyyy hh:mm") + << QStringLiteral("d MMMM, yyyy hh:mm:ss"); + + for (int i = 0; i < formats.size(); ++i) { + dt = QDateTime::fromString(s, formats.at(i)); + if (dt.isValid()) + break; + } + } + if (!dt.isValid()) + return qSNaN(); + return dt.toMSecsSinceEpoch(); +} + +/*! + \internal + + Converts the ECMA Date value \tt (in UTC form) to QDateTime + according to \a spec. +*/ +static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec) +{ + if (std::isnan(t)) + return QDateTime(); + if (spec == Qt::LocalTime) + t = LocalTime(t); + int year = int(YearFromTime(t)); + int month = int(MonthFromTime(t) + 1); + int day = int(DateFromTime(t)); + int hours = HourFromTime(t); + int mins = MinFromTime(t); + int secs = SecFromTime(t); + int ms = msFromTime(t); + return QDateTime(QDate(year, month, day), QTime(hours, mins, secs, ms), spec); +} + +static inline QString ToString(double t) +{ + if (std::isnan(t)) + return QStringLiteral("Invalid Date"); + QString str = ToDateTime(t, Qt::LocalTime).toString() + QStringLiteral(" GMT"); + double tzoffset = LocalTZA + DaylightSavingTA(t); + if (tzoffset) { + int hours = static_cast<int>(::fabs(tzoffset) / 1000 / 60 / 60); + int mins = int(::fabs(tzoffset) / 1000 / 60) % 60; + str.append(QLatin1Char((tzoffset > 0) ? '+' : '-')); + if (hours < 10) + str.append(QLatin1Char('0')); + str.append(QString::number(hours)); + if (mins < 10) + str.append(QLatin1Char('0')); + str.append(QString::number(mins)); + } + return str; +} + +static inline QString ToUTCString(double t) +{ + if (std::isnan(t)) + return QStringLiteral("Invalid Date"); + return ToDateTime(t, Qt::UTC).toString() + QStringLiteral(" GMT"); +} + +static inline QString ToDateString(double t) +{ + return ToDateTime(t, Qt::LocalTime).date().toString(); +} + +static inline QString ToTimeString(double t) +{ + return ToDateTime(t, Qt::LocalTime).time().toString(); +} + +static inline QString ToLocaleString(double t) +{ + return ToDateTime(t, Qt::LocalTime).toString(Qt::LocaleDate); +} + +static inline QString ToLocaleDateString(double t) +{ + return ToDateTime(t, Qt::LocalTime).date().toString(Qt::LocaleDate); +} + +static inline QString ToLocaleTimeString(double t) +{ + return ToDateTime(t, Qt::LocalTime).time().toString(Qt::LocaleDate); +} + +static double getLocalTZA() +{ +#ifndef Q_OS_WIN + struct tm t; + time_t curr; + time(&curr); + localtime_r(&curr, &t); + time_t locl = mktime(&t); + gmtime_r(&curr, &t); + time_t globl = mktime(&t); + return double(locl - globl) * 1000.0; +#else + TIME_ZONE_INFORMATION tzInfo; + GetTimeZoneInformation(&tzInfo); + return -tzInfo.Bias * 60.0 * 1000.0; +#endif +} + +DateObject::DateObject(ExecutionEngine *engine, const QDateTime &date) + : Object(engine) +{ + type = Type_DateObject; + value = Value::fromDouble(date.toMSecsSinceEpoch()); +} + +QDateTime DateObject::toQDateTime() const +{ + return ToDateTime(value.asDouble(), Qt::LocalTime); +} + +DEFINE_MANAGED_VTABLE(DateCtor); + +DateCtor::DateCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Date"))) +{ + vtbl = &static_vtbl; +} + +Value DateCtor::construct(Managed *m, Value *args, int argc) +{ + double t = 0; + + if (argc == 0) + t = currentTime(); + + else if (argc == 1) { + Value arg = args[0]; + if (DateObject *d = arg.asDateObject()) + arg = d->value; + else + arg = __qmljs_to_primitive(arg, PREFERREDTYPE_HINT); + + if (arg.isString()) + t = ParseString(arg.stringValue()->toQString()); + else + t = TimeClip(arg.toNumber()); + } + + else { // argc > 1 + double year = args[0].toNumber(); + double month = args[1].toNumber(); + double day = argc >= 3 ? args[2].toNumber() : 1; + double hours = argc >= 4 ? args[3].toNumber() : 0; + double mins = argc >= 5 ? args[4].toNumber() : 0; + double secs = argc >= 6 ? args[5].toNumber() : 0; + double ms = argc >= 7 ? args[6].toNumber() : 0; + if (year >= 0 && year <= 99) + year += 1900; + t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); + t = TimeClip(UTC(t)); + } + + Object *d = m->engine()->newDateObject(Value::fromDouble(t)); + return Value::fromObject(d); +} + +Value DateCtor::call(Managed *m, const Value &, Value *, int) +{ + double t = currentTime(); + return Value::fromString(m->engine()->current, ToString(t)); +} + +void DatePrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(7)); + LocalTZA = getLocalTZA(); + + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("parse"), method_parse, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("UTC"), method_UTC, 7); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("now"), method_now, 0); + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toDateString"), method_toDateString, 0); + defineDefaultProperty(ctx, QStringLiteral("toTimeString"), method_toTimeString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); + defineDefaultProperty(ctx, QStringLiteral("getTime"), method_getTime, 0); + defineDefaultProperty(ctx, QStringLiteral("getYear"), method_getYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getFullYear"), method_getFullYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCFullYear"), method_getUTCFullYear, 0); + defineDefaultProperty(ctx, QStringLiteral("getMonth"), method_getMonth, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMonth"), method_getUTCMonth, 0); + defineDefaultProperty(ctx, QStringLiteral("getDate"), method_getDate, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCDate"), method_getUTCDate, 0); + defineDefaultProperty(ctx, QStringLiteral("getDay"), method_getDay, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCDay"), method_getUTCDay, 0); + defineDefaultProperty(ctx, QStringLiteral("getHours"), method_getHours, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCHours"), method_getUTCHours, 0); + defineDefaultProperty(ctx, QStringLiteral("getMinutes"), method_getMinutes, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMinutes"), method_getUTCMinutes, 0); + defineDefaultProperty(ctx, QStringLiteral("getSeconds"), method_getSeconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCSeconds"), method_getUTCSeconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getMilliseconds"), method_getMilliseconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getUTCMilliseconds"), method_getUTCMilliseconds, 0); + defineDefaultProperty(ctx, QStringLiteral("getTimezoneOffset"), method_getTimezoneOffset, 0); + defineDefaultProperty(ctx, QStringLiteral("setTime"), method_setTime, 1); + defineDefaultProperty(ctx, QStringLiteral("setMilliseconds"), method_setMilliseconds, 1); + defineDefaultProperty(ctx, QStringLiteral("setUTCMilliseconds"), method_setUTCMilliseconds, 1); + defineDefaultProperty(ctx, QStringLiteral("setSeconds"), method_setSeconds, 2); + defineDefaultProperty(ctx, QStringLiteral("setUTCSeconds"), method_setUTCSeconds, 2); + defineDefaultProperty(ctx, QStringLiteral("setMinutes"), method_setMinutes, 3); + defineDefaultProperty(ctx, QStringLiteral("setUTCMinutes"), method_setUTCMinutes, 3); + defineDefaultProperty(ctx, QStringLiteral("setHours"), method_setHours, 4); + defineDefaultProperty(ctx, QStringLiteral("setUTCHours"), method_setUTCHours, 4); + defineDefaultProperty(ctx, QStringLiteral("setDate"), method_setDate, 1); + defineDefaultProperty(ctx, QStringLiteral("setUTCDate"), method_setUTCDate, 1); + defineDefaultProperty(ctx, QStringLiteral("setMonth"), method_setMonth, 2); + defineDefaultProperty(ctx, QStringLiteral("setUTCMonth"), method_setUTCMonth, 2); + defineDefaultProperty(ctx, QStringLiteral("setYear"), method_setYear, 1); + defineDefaultProperty(ctx, QStringLiteral("setFullYear"), method_setFullYear, 3); + defineDefaultProperty(ctx, QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); + defineDefaultProperty(ctx, QStringLiteral("toUTCString"), method_toUTCString, 0); + defineDefaultProperty(ctx, QStringLiteral("toGMTString"), method_toUTCString, 0); + defineDefaultProperty(ctx, QStringLiteral("toISOString"), method_toISOString, 0); + defineDefaultProperty(ctx, QStringLiteral("toJSON"), method_toJSON, 1); +} + +double DatePrototype::getThisDate(ExecutionContext *ctx) +{ + if (DateObject *thisObject = ctx->thisObject.asDateObject()) + return thisObject->value.asDouble(); + else { + ctx->throwTypeError(); + return 0; + } +} + +Value DatePrototype::method_parse(SimpleCallContext *ctx) +{ + return Value::fromDouble(ParseString(ctx->argument(0).toString(ctx)->toQString())); +} + +Value DatePrototype::method_UTC(SimpleCallContext *ctx) +{ + const int numArgs = ctx->argumentCount; + if (numArgs >= 2) { + double year = ctx->argument(0).toNumber(); + double month = ctx->argument(1).toNumber(); + double day = numArgs >= 3 ? ctx->argument(2).toNumber() : 1; + double hours = numArgs >= 4 ? ctx->argument(3).toNumber() : 0; + double mins = numArgs >= 5 ? ctx->argument(4).toNumber() : 0; + double secs = numArgs >= 6 ? ctx->argument(5).toNumber() : 0; + double ms = numArgs >= 7 ? ctx->argument(6).toNumber() : 0; + if (year >= 0 && year <= 99) + year += 1900; + double t = MakeDate(MakeDay(year, month, day), + MakeTime(hours, mins, secs, ms)); + return Value::fromDouble(TimeClip(t)); + } + return Value::undefinedValue(); +} + +Value DatePrototype::method_now(SimpleCallContext *ctx) +{ + Q_UNUSED(ctx); + double t = currentTime(); + return Value::fromDouble(t); +} + +Value DatePrototype::method_toString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToString(t)); +} + +Value DatePrototype::method_toDateString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToDateString(t)); +} + +Value DatePrototype::method_toTimeString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToTimeString(t)); +} + +Value DatePrototype::method_toLocaleString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToLocaleString(t)); +} + +Value DatePrototype::method_toLocaleDateString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToLocaleDateString(t)); +} + +Value DatePrototype::method_toLocaleTimeString(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromString(ctx, ToLocaleTimeString(t)); +} + +Value DatePrototype::method_valueOf(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getTime(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getYear(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = YearFromTime(LocalTime(t)) - 1900; + return Value::fromDouble(t); +} + +Value DatePrototype::method_getFullYear(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = YearFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCFullYear(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = YearFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getMonth(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = MonthFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCMonth(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = MonthFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getDate(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = DateFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCDate(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = DateFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getDay(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = WeekDay(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCDay(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = WeekDay(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getHours(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = HourFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCHours(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = HourFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getMinutes(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = MinFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCMinutes(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = MinFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getSeconds(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = SecFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCSeconds(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = SecFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getMilliseconds(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = msFromTime(LocalTime(t)); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getUTCMilliseconds(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = msFromTime(t); + return Value::fromDouble(t); +} + +Value DatePrototype::method_getTimezoneOffset(SimpleCallContext *ctx) +{ + double t = getThisDate(ctx); + if (! std::isnan(t)) + t = (t - LocalTime(t)) / msPerMinute; + return Value::fromDouble(t); +} + +Value DatePrototype::method_setTime(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + self->value.setDouble(TimeClip(ctx->argument(0).toNumber())); + return self->value; +} + +Value DatePrototype::method_setMilliseconds(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double ms = ctx->argument(0).toNumber(); + self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); + return self->value; +} + +Value DatePrototype::method_setUTCMilliseconds(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double ms = ctx->argument(0).toNumber(); + self->value.setDouble(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); + return self->value; +} + +Value DatePrototype::method_setSeconds(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double sec = ctx->argument(0).toNumber(); + double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCSeconds(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double sec = ctx->argument(0).toNumber(); + double ms = (ctx->argumentCount < 2) ? msFromTime(t) : ctx->argument(1).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setMinutes(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double min = ctx->argument(0).toNumber(); + double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(); + double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCMinutes(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double min = ctx->argument(0).toNumber(); + double sec = (ctx->argumentCount < 2) ? SecFromTime(t) : ctx->argument(1).toNumber(); + double ms = (ctx->argumentCount < 3) ? msFromTime(t) : ctx->argument(2).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setHours(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double hour = ctx->argument(0).toNumber(); + double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(); + double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(); + double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCHours(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double hour = ctx->argument(0).toNumber(); + double min = (ctx->argumentCount < 2) ? MinFromTime(t) : ctx->argument(1).toNumber(); + double sec = (ctx->argumentCount < 3) ? SecFromTime(t) : ctx->argument(2).toNumber(); + double ms = (ctx->argumentCount < 4) ? msFromTime(t) : ctx->argument(3).toNumber(); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setDate(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double date = ctx->argument(0).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCDate(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double date = ctx->argument(0).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setMonth(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + double month = ctx->argument(0).toNumber(); + double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setUTCMonth(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double month = ctx->argument(0).toNumber(); + double date = (ctx->argumentCount < 2) ? DateFromTime(t) : ctx->argument(1).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setYear(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + if (std::isnan(t)) + t = 0; + else + t = LocalTime(t); + double year = ctx->argument(0).toNumber(); + double r; + if (std::isnan(year)) { + r = qSNaN(); + } else { + if ((Value::toInteger(year) >= 0) && (Value::toInteger(year) <= 99)) + year += 1900; + r = MakeDay(year, MonthFromTime(t), DateFromTime(t)); + r = UTC(MakeDate(r, TimeWithinDay(t))); + r = TimeClip(r); + } + self->value.setDouble(r); + return self->value; +} + +Value DatePrototype::method_setUTCFullYear(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + double year = ctx->argument(0).toNumber(); + double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(); + double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_setFullYear(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = LocalTime(self->value.asDouble()); + if (std::isnan(t)) + t = 0; + double year = ctx->argument(0).toNumber(); + double month = (ctx->argumentCount < 2) ? MonthFromTime(t) : ctx->argument(1).toNumber(); + double date = (ctx->argumentCount < 3) ? DateFromTime(t) : ctx->argument(2).toNumber(); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + self->value.setDouble(t); + return self->value; +} + +Value DatePrototype::method_toUTCString(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + return Value::fromString(ctx, ToUTCString(t)); +} + +static void addZeroPrefixedInt(QString &str, int num, int nDigits) +{ + str.resize(str.size() + nDigits); + + QChar *c = str.data() + str.size() - 1; + while (nDigits) { + *c = QChar(num % 10 + '0'); + num /= 10; + --c; + --nDigits; + } +} + +Value DatePrototype::method_toISOString(SimpleCallContext *ctx) +{ + DateObject *self = ctx->thisObject.asDateObject(); + if (!self) + ctx->throwTypeError(); + + double t = self->value.asDouble(); + if (!std::isfinite(t)) + ctx->throwRangeError(ctx->thisObject); + + QString result; + int year = (int)YearFromTime(t); + if (year < 0 || year > 9999) { + if (qAbs(year) >= 1000000) + return Value::fromString(ctx, QStringLiteral("Invalid Date")); + result += year < 0 ? '-' : '+'; + year = qAbs(year); + addZeroPrefixedInt(result, year, 6); + } else { + addZeroPrefixedInt(result, year, 4); + } + result += '-'; + addZeroPrefixedInt(result, (int)MonthFromTime(t) + 1, 2); + result += '-'; + addZeroPrefixedInt(result, (int)DateFromTime(t), 2); + result += 'T'; + addZeroPrefixedInt(result, HourFromTime(t), 2); + result += ':'; + addZeroPrefixedInt(result, MinFromTime(t), 2); + result += ':'; + addZeroPrefixedInt(result, SecFromTime(t), 2); + result += '.'; + addZeroPrefixedInt(result, msFromTime(t), 3); + result += 'Z'; + + return Value::fromString(ctx, result); +} + +Value DatePrototype::method_toJSON(SimpleCallContext *ctx) +{ + Value O = __qmljs_to_object(ctx, ctx->thisObject); + Value tv = __qmljs_to_primitive(O, NUMBER_HINT); + + if (tv.isNumber() && !std::isfinite(tv.toNumber())) + return Value::nullValue(); + + FunctionObject *toIso = O.objectValue()->get(ctx->engine->newString(QStringLiteral("toISOString"))).asFunctionObject(); + + if (!toIso) + ctx->throwTypeError(); + + return toIso->call(ctx->thisObject, 0, 0); +} + +void DatePrototype::timezoneUpdated() +{ + LocalTZA = getLocalTZA(); +} diff --git a/src/qml/qml/v4/qv4dateobject_p.h b/src/qml/qml/v4/qv4dateobject_p.h new file mode 100644 index 0000000000..4e833e143f --- /dev/null +++ b/src/qml/qml/v4/qv4dateobject_p.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4DATEOBJECT_P_H +#define QV4DATEOBJECT_P_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + +class QDateTime; + +namespace QV4 { + +struct DateObject: Object { + Value value; + DateObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_DateObject; } + DateObject(ExecutionEngine *engine, const QDateTime &value); + + QDateTime toQDateTime() const; +}; + +struct DateCtor: FunctionObject +{ + DateCtor(ExecutionContext *scope); + + static Value construct(Managed *, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct DatePrototype: DateObject +{ + DatePrototype(ExecutionEngine *engine): DateObject(engine, Value::fromDouble(qSNaN())) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static double getThisDate(ExecutionContext *ctx); + + static Value method_parse(SimpleCallContext *ctx); + static Value method_UTC(SimpleCallContext *ctx); + static Value method_now(SimpleCallContext *ctx); + + static Value method_toString(SimpleCallContext *ctx); + static Value method_toDateString(SimpleCallContext *ctx); + static Value method_toTimeString(SimpleCallContext *ctx); + static Value method_toLocaleString(SimpleCallContext *ctx); + static Value method_toLocaleDateString(SimpleCallContext *ctx); + static Value method_toLocaleTimeString(SimpleCallContext *ctx); + static Value method_valueOf(SimpleCallContext *ctx); + static Value method_getTime(SimpleCallContext *ctx); + static Value method_getYear(SimpleCallContext *ctx); + static Value method_getFullYear(SimpleCallContext *ctx); + static Value method_getUTCFullYear(SimpleCallContext *ctx); + static Value method_getMonth(SimpleCallContext *ctx); + static Value method_getUTCMonth(SimpleCallContext *ctx); + static Value method_getDate(SimpleCallContext *ctx); + static Value method_getUTCDate(SimpleCallContext *ctx); + static Value method_getDay(SimpleCallContext *ctx); + static Value method_getUTCDay(SimpleCallContext *ctx); + static Value method_getHours(SimpleCallContext *ctx); + static Value method_getUTCHours(SimpleCallContext *ctx); + static Value method_getMinutes(SimpleCallContext *ctx); + static Value method_getUTCMinutes(SimpleCallContext *ctx); + static Value method_getSeconds(SimpleCallContext *ctx); + static Value method_getUTCSeconds(SimpleCallContext *ctx); + static Value method_getMilliseconds(SimpleCallContext *ctx); + static Value method_getUTCMilliseconds(SimpleCallContext *ctx); + static Value method_getTimezoneOffset(SimpleCallContext *ctx); + static Value method_setTime(SimpleCallContext *ctx); + static Value method_setMilliseconds(SimpleCallContext *ctx); + static Value method_setUTCMilliseconds(SimpleCallContext *ctx); + static Value method_setSeconds(SimpleCallContext *ctx); + static Value method_setUTCSeconds(SimpleCallContext *ctx); + static Value method_setMinutes(SimpleCallContext *ctx); + static Value method_setUTCMinutes(SimpleCallContext *ctx); + static Value method_setHours(SimpleCallContext *ctx); + static Value method_setUTCHours(SimpleCallContext *ctx); + static Value method_setDate(SimpleCallContext *ctx); + static Value method_setUTCDate(SimpleCallContext *ctx); + static Value method_setMonth(SimpleCallContext *ctx); + static Value method_setUTCMonth(SimpleCallContext *ctx); + static Value method_setYear(SimpleCallContext *ctx); + static Value method_setFullYear(SimpleCallContext *ctx); + static Value method_setUTCFullYear(SimpleCallContext *ctx); + static Value method_toUTCString(SimpleCallContext *ctx); + static Value method_toISOString(SimpleCallContext *ctx); + static Value method_toJSON(SimpleCallContext *ctx); + + static void timezoneUpdated(); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4/qv4debugging.cpp b/src/qml/qml/v4/qv4debugging.cpp new file mode 100644 index 0000000000..624390d8f2 --- /dev/null +++ b/src/qml/qml/v4/qv4debugging.cpp @@ -0,0 +1,372 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4debugging_p.h" +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include "qv4function_p.h" +#include "moth/qv4instr_moth_p.h" +#include <iostream> + +using namespace QV4; +using namespace QV4::Debugging; + +Debugger::Debugger(QV4::ExecutionEngine *engine) + : _engine(engine) + , m_agent(0) + , m_state(Running) + , m_pauseRequested(false) + , m_currentInstructionPointer(0) +{ + qMetaTypeId<Debugger*>(); +} + +Debugger::~Debugger() +{ + detachFromAgent(); +} + +void Debugger::attachToAgent(DebuggerAgent *agent) +{ + Q_ASSERT(!m_agent); + m_agent = agent; +} + +void Debugger::detachFromAgent() +{ + DebuggerAgent *agent = 0; + { + QMutexLocker locker(&m_lock); + agent = m_agent; + m_agent = 0; + } + if (agent) + agent->removeDebugger(this); +} + +void Debugger::pause() +{ + QMutexLocker locker(&m_lock); + if (m_state == Paused) + return; + m_pauseRequested = true; +} + +void Debugger::resume() +{ + QMutexLocker locker(&m_lock); + Q_ASSERT(m_state == Paused); + m_runningCondition.wakeAll(); +} + +void Debugger::addBreakPoint(const QString &fileName, int lineNumber) +{ + QMutexLocker locker(&m_lock); + if (!m_pendingBreakPointsToRemove.remove(fileName, lineNumber)) + m_pendingBreakPointsToAdd.add(fileName, lineNumber); + m_havePendingBreakPoints = !m_pendingBreakPointsToAdd.isEmpty() || !m_pendingBreakPointsToRemove.isEmpty(); +} + +void Debugger::removeBreakPoint(const QString &fileName, int lineNumber) +{ + QMutexLocker locker(&m_lock); + if (!m_pendingBreakPointsToAdd.remove(fileName, lineNumber)) + m_pendingBreakPointsToRemove.add(fileName, lineNumber); + m_havePendingBreakPoints = !m_pendingBreakPointsToAdd.isEmpty() || !m_pendingBreakPointsToRemove.isEmpty(); +} + +Debugger::ExecutionState Debugger::currentExecutionState(const uchar *code) const +{ + if (!code) + code = m_currentInstructionPointer; + // ### Locking + ExecutionState state; + + QV4::ExecutionContext *context = _engine->current; + QV4::Function *function = 0; + if (CallContext *callCtx = context->asCallContext()) + function = callCtx->function->function; + else { + Q_ASSERT(context->type == QV4::ExecutionContext::Type_GlobalContext); + function = context->engine->globalCode; + } + + state.function = function; + state.fileName = function->sourceFile; + + qptrdiff relativeProgramCounter = code - function->codeData; + state.lineNumber = function->lineNumberForProgramCounter(relativeProgramCounter); + + return state; +} + +void Debugger::setPendingBreakpoints(Function *function) +{ + m_pendingBreakPointsToAddToFutureCode.applyToFunction(function, /*removeBreakPoints*/ false); +} + +void Debugger::maybeBreakAtInstruction(const uchar *code, bool breakPointHit) +{ + QMutexLocker locker(&m_lock); + m_currentInstructionPointer = code; + + // Do debugger internal work + if (m_havePendingBreakPoints) { + + if (breakPointHit) { + ExecutionState state = currentExecutionState(); + breakPointHit = !m_pendingBreakPointsToRemove.contains(state.fileName, state.lineNumber); + } + + applyPendingBreakPoints(); + } + + // Serve debugging requests from the agent + if (m_pauseRequested) { + m_pauseRequested = false; + pauseAndWait(); + } else if (breakPointHit) + pauseAndWait(); + + if (!m_pendingBreakPointsToAdd.isEmpty() || !m_pendingBreakPointsToRemove.isEmpty()) + applyPendingBreakPoints(); +} + +void Debugger::aboutToThrow(const QV4::Value &value) +{ + qDebug() << "*** We are about to throw..."; +} + +void Debugger::pauseAndWait() +{ + m_state = Paused; + QMetaObject::invokeMethod(m_agent, "debuggerPaused", Qt::QueuedConnection, Q_ARG(QV4::Debugging::Debugger*, this)); + m_runningCondition.wait(&m_lock); + m_state = Running; +} + +void Debugger::applyPendingBreakPoints() +{ + foreach (Function *function, _engine->functions) { + m_pendingBreakPointsToAdd.applyToFunction(function, /*removeBreakPoints*/false); + m_pendingBreakPointsToRemove.applyToFunction(function, /*removeBreakPoints*/true); + } + + for (BreakPoints::ConstIterator it = m_pendingBreakPointsToAdd.constBegin(), + end = m_pendingBreakPointsToAdd.constEnd(); it != end; ++it) { + foreach (int lineNumber, it.value()) + m_pendingBreakPointsToAddToFutureCode.add(it.key(), lineNumber); + } + + m_pendingBreakPointsToAdd.clear(); + m_pendingBreakPointsToRemove.clear(); + m_havePendingBreakPoints = false; +} + +static void realDumpValue(QV4::Value v, QV4::ExecutionContext *ctx, std::string prefix) +{ + using namespace QV4; + using namespace std; + cout << prefix << "tag: " << hex << v.tag << dec << endl << prefix << "\t-> "; + switch (v.type()) { + case Value::Undefined_Type: cout << "Undefined" << endl; return; + case Value::Null_Type: cout << "Null" << endl; return; + case Value::Boolean_Type: cout << "Boolean"; break; + case Value::Integer_Type: cout << "Integer"; break; + case Value::Object_Type: cout << "Object"; break; + case Value::String_Type: cout << "String"; break; + default: cout << "UNKNOWN" << endl; return; + } + cout << endl; + + if (v.isBoolean()) { + cout << prefix << "\t-> " << (v.booleanValue() ? "TRUE" : "FALSE") << endl; + return; + } + + if (v.isInteger()) { + cout << prefix << "\t-> " << v.integerValue() << endl; + return; + } + + if (v.isDouble()) { + cout << prefix << "\t-> " << v.doubleValue() << endl; + return; + } + + if (v.isString()) { + // maybe check something on the Managed object? + cout << prefix << "\t-> @" << hex << v.stringValue() << endl; + cout << prefix << "\t-> \"" << qPrintable(v.stringValue()->toQString()) << "\"" << endl; + return; + } + + Object *o = v.objectValue(); + if (!o) + return; + + cout << prefix << "\t-> @" << hex << o << endl; + cout << prefix << "object type: " << o->internalType() << endl << prefix << "\t-> "; + switch (o->internalType()) { + case QV4::Managed::Type_Invalid: cout << "Invalid"; break; + case QV4::Managed::Type_String: cout << "String"; break; + case QV4::Managed::Type_Object: cout << "Object"; break; + case QV4::Managed::Type_ArrayObject: cout << "ArrayObject"; break; + case QV4::Managed::Type_FunctionObject: cout << "FunctionObject"; break; + case QV4::Managed::Type_BooleanObject: cout << "BooleanObject"; break; + case QV4::Managed::Type_NumberObject: cout << "NumberObject"; break; + case QV4::Managed::Type_StringObject: cout << "StringObject"; break; + case QV4::Managed::Type_DateObject: cout << "DateObject"; break; + case QV4::Managed::Type_RegExpObject: cout << "RegExpObject"; break; + case QV4::Managed::Type_ErrorObject: cout << "ErrorObject"; break; + case QV4::Managed::Type_ArgumentsObject: cout << "ArgumentsObject"; break; + case QV4::Managed::Type_JSONObject: cout << "JSONObject"; break; + case QV4::Managed::Type_MathObject: cout << "MathObject"; break; + case QV4::Managed::Type_ForeachIteratorObject: cout << "ForeachIteratorObject"; break; + default: cout << "UNKNOWN" << endl; return; + } + cout << endl; + + cout << prefix << "properties:" << endl; + ForEachIteratorObject it(ctx, o); + for (Value name = it.nextPropertyName(); !name.isNull(); name = it.nextPropertyName()) { + cout << prefix << "\t\"" << qPrintable(name.stringValue()->toQString()) << "\"" << endl; + PropertyAttributes attrs; + Property *d = o->__getOwnProperty__(name.stringValue(), &attrs); + Value pval = o->getValue(d, attrs); + cout << prefix << "\tvalue:" << endl; + realDumpValue(pval, ctx, prefix + "\t"); + } +} + +void dumpValue(QV4::Value v, QV4::ExecutionContext *ctx) +{ + realDumpValue(v, ctx, std::string("")); +} + + +void DebuggerAgent::addDebugger(Debugger *debugger) +{ + Q_ASSERT(!m_debuggers.contains(debugger)); + m_debuggers << debugger; + debugger->attachToAgent(this); +} + +void DebuggerAgent::removeDebugger(Debugger *debugger) +{ + m_debuggers.removeAll(debugger); + debugger->detachFromAgent(); +} + +void DebuggerAgent::pause(Debugger *debugger) +{ + debugger->pause(); +} + +void DebuggerAgent::addBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber) +{ + debugger->addBreakPoint(fileName, lineNumber); +} + +void DebuggerAgent::removeBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber) +{ + debugger->removeBreakPoint(fileName, lineNumber); +} + +DebuggerAgent::~DebuggerAgent() +{ + Q_ASSERT(m_debuggers.isEmpty()); +} + +void Debugger::BreakPoints::add(const QString &fileName, int lineNumber) +{ + QList<int> &lines = (*this)[fileName]; + if (!lines.contains(lineNumber)) { + lines.append(lineNumber); + qSort(lines); + } +} + +bool Debugger::BreakPoints::remove(const QString &fileName, int lineNumber) +{ + Iterator breakPoints = find(fileName); + if (breakPoints == constEnd()) + return false; + return breakPoints->removeAll(lineNumber) > 0; +} + +bool Debugger::BreakPoints::contains(const QString &fileName, int lineNumber) const +{ + ConstIterator breakPoints = find(fileName); + if (breakPoints == constEnd()) + return false; + return breakPoints->contains(lineNumber); +} + +void Debugger::BreakPoints::applyToFunction(Function *function, bool removeBreakPoints) +{ + Iterator breakPointsForFile = find(function->sourceFile); + if (breakPointsForFile == end()) + return; + + QList<int>::Iterator breakPoint = breakPointsForFile->begin(); + while (breakPoint != breakPointsForFile->end()) { + bool breakPointFound = false; + for (QVector<LineNumberMapping>::ConstIterator mapping = function->lineNumberMappings.constBegin(), + end = function->lineNumberMappings.constEnd(); mapping != end; ++mapping) { + if (mapping->lineNumber == *breakPoint) { + uchar *codePtr = const_cast<uchar *>(function->codeData) + mapping->codeOffset; + QQmlJS::Moth::Instr *instruction = reinterpret_cast<QQmlJS::Moth::Instr*>(codePtr); + instruction->common.breakPoint = !removeBreakPoints; + // Continue setting the next break point. + breakPointFound = true; + break; + } + } + if (breakPointFound) + breakPoint = breakPointsForFile->erase(breakPoint); + else + ++breakPoint; + } + + if (breakPointsForFile->isEmpty()) + erase(breakPointsForFile); +} diff --git a/src/qml/qml/v4/qv4debugging_p.h b/src/qml/qml/v4/qv4debugging_p.h new file mode 100644 index 0000000000..4a273be732 --- /dev/null +++ b/src/qml/qml/v4/qv4debugging_p.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DEBUGGING_H +#define DEBUGGING_H + +#include "qv4global_p.h" +#include "qv4engine_p.h" +#include "qv4context_p.h" +#include "qv4jsir_p.h" + +#include <QHash> +#include <QThread> +#include <QMutex> +#include <QWaitCondition> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Function; + +namespace Debugging { + +class DebuggerAgent; + +class Q_QML_EXPORT Debugger +{ +public: + enum State { + Running, + Paused + }; + + Debugger(ExecutionEngine *_engine); + ~Debugger(); + + void attachToAgent(DebuggerAgent *agent); + void detachFromAgent(); + + void pause(); + void resume(); + + State state() const { return m_state; } + + void addBreakPoint(const QString &fileName, int lineNumber); + void removeBreakPoint(const QString &fileName, int lineNumber); + + struct ExecutionState + { + ExecutionState() : lineNumber(-1), function(0) {} + QString fileName; + int lineNumber; + Function *function; + }; + + ExecutionState currentExecutionState(const uchar *code = 0) const; + + bool pauseAtNextOpportunity() const { + return m_pauseRequested || m_havePendingBreakPoints; + } + void setPendingBreakpoints(Function *function); + +public: // compile-time interface + void maybeBreakAtInstruction(const uchar *code, bool breakPointHit); + +public: // execution hooks + void aboutToThrow(const Value &value); + +private: + // requires lock to be held + void pauseAndWait(); + + void applyPendingBreakPoints(); + + struct BreakPoints : public QHash<QString, QList<int> > + { + void add(const QString &fileName, int lineNumber); + bool remove(const QString &fileName, int lineNumber); + bool contains(const QString &fileName, int lineNumber) const; + void applyToFunction(Function *function, bool removeBreakPoints); + }; + + QV4::ExecutionEngine *_engine; + DebuggerAgent *m_agent; + QMutex m_lock; + QWaitCondition m_runningCondition; + State m_state; + bool m_pauseRequested; + bool m_havePendingBreakPoints; + BreakPoints m_pendingBreakPointsToAdd; + BreakPoints m_pendingBreakPointsToAddToFutureCode; + BreakPoints m_pendingBreakPointsToRemove; + const uchar *m_currentInstructionPointer; +}; + +class Q_QML_EXPORT DebuggerAgent : public QObject +{ + Q_OBJECT +public: + ~DebuggerAgent(); + + void addDebugger(Debugger *debugger); + void removeDebugger(Debugger *debugger); + + void pause(Debugger *debugger); + void addBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber); + void removeBreakPoint(Debugger *debugger, const QString &fileName, int lineNumber); + + Q_INVOKABLE virtual void debuggerPaused(QV4::Debugging::Debugger *debugger) = 0; + +protected: + QList<Debugger *> m_debuggers; +}; + +} // namespace Debugging +} // namespace QV4 + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QV4::Debugging::Debugger*) + +#endif // DEBUGGING_H diff --git a/src/qml/qml/v4/qv4engine.cpp b/src/qml/qml/v4/qv4engine.cpp new file mode 100644 index 0000000000..0b7c850aa6 --- /dev/null +++ b/src/qml/qml/v4/qv4engine.cpp @@ -0,0 +1,837 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qv4engine_p.h> +#include <qv4value_p.h> +#include <qv4object_p.h> +#include <qv4objectproto_p.h> +#include <qv4arrayobject_p.h> +#include <qv4booleanobject_p.h> +#include <qv4globalobject_p.h> +#include <qv4errorobject_p.h> +#include <qv4functionobject_p.h> +#include "qv4function_p.h" +#include <qv4mathobject_p.h> +#include <qv4numberobject_p.h> +#include <qv4regexpobject_p.h> +#include <qv4variantobject_p.h> +#include <qv4runtime_p.h> +#include "qv4mm_p.h" +#include <qv4argumentsobject_p.h> +#include <qv4dateobject_p.h> +#include <qv4jsonobject_p.h> +#include <qv4stringobject_p.h> +#include <qv4identifiertable_p.h> +#include <qv4unwindhelper_p.h> +#include "qv4debugging_p.h" +#include "qv4executableallocator_p.h" +#include "qv4sequenceobject_p.h" +#include "qv4qobjectwrapper_p.h" +#include "qv4qmlextensions_p.h" +#include "qv4stacktrace_p.h" + +#ifdef V4_ENABLE_JIT +#include "qv4isel_masm_p.h" +#endif // V4_ENABLE_JIT + +#include "qv4isel_moth_p.h" + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1); + +ExecutionEngine::ExecutionEngine(QQmlJS::EvalISelFactory *factory) + : memoryManager(new QV4::MemoryManager) + , executableAllocator(new QV4::ExecutableAllocator) + , regExpAllocator(new QV4::ExecutableAllocator) + , bumperPointerAllocator(new WTF::BumpPointerAllocator) + , debugger(0) + , globalObject(0) + , globalCode(0) + , functionsNeedSort(false) + , m_engineId(engineSerial.fetchAndAddOrdered(1)) + , regExpCache(0) + , m_multiplyWrappedQObjects(0) + , m_qmlExtensions(0) +{ + MemoryManager::GCBlocker gcBlocker(memoryManager); + + if (!factory) { +#ifdef V4_ENABLE_JIT + factory = new QQmlJS::MASM::ISelFactory; +#else // !V4_ENABLE_JIT + factory = new QQmlJS::Moth::ISelFactory; +#endif // V4_ENABLE_JIT + } + iselFactory.reset(factory); + + memoryManager->setExecutionEngine(this); + + identifierTable = new IdentifierTable(this); + + emptyClass = new (classPool.allocate(sizeof(InternalClass))) InternalClass(this); + + id_undefined = newIdentifier(QStringLiteral("undefined")); + id_null = newIdentifier(QStringLiteral("null")); + id_true = newIdentifier(QStringLiteral("true")); + id_false = newIdentifier(QStringLiteral("false")); + id_boolean = newIdentifier(QStringLiteral("boolean")); + id_number = newIdentifier(QStringLiteral("number")); + id_string = newIdentifier(QStringLiteral("string")); + id_object = newIdentifier(QStringLiteral("object")); + id_function = newIdentifier(QStringLiteral("function")); + id_length = newIdentifier(QStringLiteral("length")); + id_prototype = newIdentifier(QStringLiteral("prototype")); + id_constructor = newIdentifier(QStringLiteral("constructor")); + id_arguments = newIdentifier(QStringLiteral("arguments")); + id_caller = newIdentifier(QStringLiteral("caller")); + id_this = newIdentifier(QStringLiteral("this")); + id___proto__ = newIdentifier(QStringLiteral("__proto__")); + id_enumerable = newIdentifier(QStringLiteral("enumerable")); + id_configurable = newIdentifier(QStringLiteral("configurable")); + id_writable = newIdentifier(QStringLiteral("writable")); + id_value = newIdentifier(QStringLiteral("value")); + id_get = newIdentifier(QStringLiteral("get")); + id_set = newIdentifier(QStringLiteral("set")); + id_eval = newIdentifier(QStringLiteral("eval")); + id_uintMax = newIdentifier(QStringLiteral("4294967295")); + id_name = newIdentifier(QStringLiteral("name")); + + arrayClass = emptyClass->addMember(id_length, Attr_NotConfigurable|Attr_NotEnumerable); + initRootContext(); + + objectPrototype = new (memoryManager) ObjectPrototype(this); + stringPrototype = new (memoryManager) StringPrototype(this); + numberPrototype = new (memoryManager) NumberPrototype(this); + booleanPrototype = new (memoryManager) BooleanPrototype(this); + arrayPrototype = new (memoryManager) ArrayPrototype(rootContext); + datePrototype = new (memoryManager) DatePrototype(this); + functionPrototype = new (memoryManager) FunctionPrototype(rootContext); + regExpPrototype = new (memoryManager) RegExpPrototype(this); + errorPrototype = new (memoryManager) ErrorPrototype(this); + evalErrorPrototype = new (memoryManager) EvalErrorPrototype(this); + rangeErrorPrototype = new (memoryManager) RangeErrorPrototype(this); + referenceErrorPrototype = new (memoryManager) ReferenceErrorPrototype(this); + syntaxErrorPrototype = new (memoryManager) SyntaxErrorPrototype(this); + typeErrorPrototype = new (memoryManager) TypeErrorPrototype(this); + uRIErrorPrototype = new (memoryManager) URIErrorPrototype(this); + + variantPrototype = new (memoryManager) VariantPrototype(this); + sequencePrototype = new (memoryManager) SequencePrototype(this); + + stringPrototype->prototype = objectPrototype; + numberPrototype->prototype = objectPrototype; + booleanPrototype->prototype = objectPrototype; + arrayPrototype->prototype = objectPrototype; + datePrototype->prototype = objectPrototype; + functionPrototype->prototype = objectPrototype; + regExpPrototype->prototype = objectPrototype; + errorPrototype->prototype = objectPrototype; + evalErrorPrototype->prototype = objectPrototype; + rangeErrorPrototype->prototype = objectPrototype; + referenceErrorPrototype->prototype = objectPrototype; + syntaxErrorPrototype->prototype = objectPrototype; + typeErrorPrototype->prototype = objectPrototype; + uRIErrorPrototype->prototype = objectPrototype; + + objectCtor = Value::fromObject(new (memoryManager) ObjectCtor(rootContext)); + stringCtor = Value::fromObject(new (memoryManager) StringCtor(rootContext)); + numberCtor = Value::fromObject(new (memoryManager) NumberCtor(rootContext)); + booleanCtor = Value::fromObject(new (memoryManager) BooleanCtor(rootContext)); + arrayCtor = Value::fromObject(new (memoryManager) ArrayCtor(rootContext)); + functionCtor = Value::fromObject(new (memoryManager) FunctionCtor(rootContext)); + dateCtor = Value::fromObject(new (memoryManager) DateCtor(rootContext)); + regExpCtor = Value::fromObject(new (memoryManager) RegExpCtor(rootContext)); + errorCtor = Value::fromObject(new (memoryManager) ErrorCtor(rootContext)); + evalErrorCtor = Value::fromObject(new (memoryManager) EvalErrorCtor(rootContext)); + rangeErrorCtor = Value::fromObject(new (memoryManager) RangeErrorCtor(rootContext)); + referenceErrorCtor = Value::fromObject(new (memoryManager) ReferenceErrorCtor(rootContext)); + syntaxErrorCtor = Value::fromObject(new (memoryManager) SyntaxErrorCtor(rootContext)); + typeErrorCtor = Value::fromObject(new (memoryManager) TypeErrorCtor(rootContext)); + uRIErrorCtor = Value::fromObject(new (memoryManager) URIErrorCtor(rootContext)); + + objectCtor.objectValue()->prototype = functionPrototype; + stringCtor.objectValue()->prototype = functionPrototype; + numberCtor.objectValue()->prototype = functionPrototype; + booleanCtor.objectValue()->prototype = functionPrototype; + arrayCtor.objectValue()->prototype = functionPrototype; + functionCtor.objectValue()->prototype = functionPrototype; + dateCtor.objectValue()->prototype = functionPrototype; + regExpCtor.objectValue()->prototype = functionPrototype; + errorCtor.objectValue()->prototype = functionPrototype; + evalErrorCtor.objectValue()->prototype = functionPrototype; + rangeErrorCtor.objectValue()->prototype = functionPrototype; + referenceErrorCtor.objectValue()->prototype = functionPrototype; + syntaxErrorCtor.objectValue()->prototype = functionPrototype; + typeErrorCtor.objectValue()->prototype = functionPrototype; + uRIErrorCtor.objectValue()->prototype = functionPrototype; + + objectPrototype->init(rootContext, objectCtor); + stringPrototype->init(this, stringCtor); + numberPrototype->init(rootContext, numberCtor); + booleanPrototype->init(rootContext, booleanCtor); + arrayPrototype->init(rootContext, arrayCtor); + datePrototype->init(rootContext, dateCtor); + functionPrototype->init(rootContext, functionCtor); + regExpPrototype->init(rootContext, regExpCtor); + errorPrototype->init(this, errorCtor); + evalErrorPrototype->init(this, evalErrorCtor); + rangeErrorPrototype->init(this, rangeErrorCtor); + referenceErrorPrototype->init(this, referenceErrorCtor); + syntaxErrorPrototype->init(this, syntaxErrorCtor); + typeErrorPrototype->init(this, typeErrorCtor); + uRIErrorPrototype->init(this, uRIErrorCtor); + + variantPrototype->init(this); + sequencePrototype->init(this); + + // + // set up the global object + // + globalObject = newObject(/*rootContext*/); + rootContext->global = globalObject; + rootContext->thisObject = Value::fromObject(globalObject); + + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Object"), objectCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("String"), stringCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Number"), numberCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Boolean"), booleanCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Array"), arrayCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Function"), functionCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Date"), dateCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("RegExp"), regExpCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Error"), errorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("EvalError"), evalErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("RangeError"), rangeErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("ReferenceError"), referenceErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("SyntaxError"), syntaxErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("TypeError"), typeErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("URIError"), uRIErrorCtor); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("Math"), Value::fromObject(new (memoryManager) MathObject(rootContext))); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("JSON"), Value::fromObject(new (memoryManager) JsonObject(rootContext))); + + globalObject->defineReadonlyProperty(this, QStringLiteral("undefined"), Value::undefinedValue()); + globalObject->defineReadonlyProperty(this, QStringLiteral("NaN"), Value::fromDouble(std::numeric_limits<double>::quiet_NaN())); + globalObject->defineReadonlyProperty(this, QStringLiteral("Infinity"), Value::fromDouble(Q_INFINITY)); + + evalFunction = new (memoryManager) EvalFunction(rootContext); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("eval"), Value::fromObject(evalFunction)); + + globalObject->defineDefaultProperty(rootContext, QStringLiteral("parseInt"), GlobalFunctions::method_parseInt, 2); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("parseFloat"), GlobalFunctions::method_parseFloat, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("decodeURIComponent"), GlobalFunctions::method_decodeURIComponent, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("encodeURI"), GlobalFunctions::method_encodeURI, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("encodeURIComponent"), GlobalFunctions::method_encodeURIComponent, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("escape"), GlobalFunctions::method_escape, 1); + globalObject->defineDefaultProperty(rootContext, QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1); +} + +ExecutionEngine::~ExecutionEngine() +{ + delete debugger; + delete m_multiplyWrappedQObjects; + m_multiplyWrappedQObjects = 0; + delete memoryManager; + delete m_qmlExtensions; + emptyClass->destroy(); + delete identifierTable; + delete bumperPointerAllocator; + delete regExpCache; + UnwindHelper::deregisterFunctions(functions); + qDeleteAll(functions); + delete regExpAllocator; + delete executableAllocator; +} + +void ExecutionEngine::enableDebugger() +{ + Q_ASSERT(!debugger); + debugger = new Debugging::Debugger(this); + iselFactory.reset(new QQmlJS::Moth::ISelFactory); +} + +void ExecutionEngine::initRootContext() +{ + rootContext = static_cast<GlobalContext *>(memoryManager->allocContext(sizeof(GlobalContext))); + current = rootContext; + current->parent = 0; + rootContext->initGlobalContext(this); +} + +InternalClass *ExecutionEngine::newClass(const InternalClass &other) +{ + return new (classPool.allocate(sizeof(InternalClass))) InternalClass(other); +} + +WithContext *ExecutionEngine::newWithContext(Object *with) +{ + WithContext *w = static_cast<WithContext *>(memoryManager->allocContext(sizeof(WithContext))); + ExecutionContext *p = current; + current = w; + w->initWithContext(p, with); + return w; +} + +CatchContext *ExecutionEngine::newCatchContext(String *exceptionVarName, const Value &exceptionValue) +{ + CatchContext *c = static_cast<CatchContext *>(memoryManager->allocContext(sizeof(CatchContext))); + ExecutionContext *p = current; + current = c; + c->initCatchContext(p, exceptionVarName, exceptionValue); + return c; +} + +CallContext *ExecutionEngine::newCallContext(FunctionObject *f, const Value &thisObject, Value *args, int argc) +{ + CallContext *c = static_cast<CallContext *>(memoryManager->allocContext(requiredMemoryForExecutionContect(f, argc))); + ExecutionContext *p = current; + current = c; + c->initCallContext(p, f, args, argc, thisObject); + + return c; +} + +CallContext *ExecutionEngine::newQmlContext(FunctionObject *f, Object *qml) +{ + CallContext *c = static_cast<CallContext *>(memoryManager->allocContext(requiredMemoryForExecutionContect(f, 0))); + + ExecutionContext *p = current; + current = c; + c->initQmlContext(p, qml, f); + + return c; +} + +CallContext *ExecutionEngine::newCallContext(void *stackSpace, FunctionObject *f, const Value &thisObject, Value *args, int argc) +{ + CallContext *c; + uint memory = requiredMemoryForExecutionContect(f, argc); + if (f->needsActivation || memory > stackContextSize) { + c = static_cast<CallContext *>(memoryManager->allocContext(memory)); + } else { + c = (CallContext *)stackSpace; +#ifndef QT_NO_DEBUG + c->next = (CallContext *)0x1; +#endif + } + + ExecutionContext *p = current; + current = c; + c->initCallContext(p, f, args, argc, thisObject); + + return c; +} + + +ExecutionContext *ExecutionEngine::pushGlobalContext() +{ + GlobalContext *g = static_cast<GlobalContext *>(memoryManager->allocContext(sizeof(GlobalContext))); + ExecutionContext *oldNext = g->next; + *g = *rootContext; + g->next = oldNext; + g->parent = current; + current = g; + + return current; +} + +Function *ExecutionEngine::newFunction(const QString &name) +{ + Function *f = new Function(newIdentifier(name)); + functions.append(f); + functionsNeedSort = true; + return f; +} + +FunctionObject *ExecutionEngine::newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *)) +{ + BuiltinFunctionOld *f = new (memoryManager) BuiltinFunctionOld(scope, name, code); + return f; +} + +FunctionObject *ExecutionEngine::newScriptFunction(ExecutionContext *scope, Function *function) +{ + assert(function); + + ScriptFunction *f = new (memoryManager) ScriptFunction(scope, function); + return f; +} + +BoundFunction *ExecutionEngine::newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs) +{ + assert(target); + + BoundFunction *f = new (memoryManager) BoundFunction(scope, target, boundThis, boundArgs); + return f; +} + + +Object *ExecutionEngine::newObject() +{ + Object *object = new (memoryManager) Object(this); + object->prototype = objectPrototype; + return object; +} + +Object *ExecutionEngine::newObject(InternalClass *internalClass) +{ + Object *object = new (memoryManager) Object(this, internalClass); + object->prototype = objectPrototype; + return object; +} + +String *ExecutionEngine::newString(const QString &s) +{ + return new (memoryManager) String(this, s); +} + +String *ExecutionEngine::newIdentifier(const QString &text) +{ + return identifierTable->insertString(text); +} + +Object *ExecutionEngine::newStringObject(const Value &value) +{ + StringObject *object = new (memoryManager) StringObject(this, value); + object->prototype = stringPrototype; + return object; +} + +Object *ExecutionEngine::newNumberObject(const Value &value) +{ + NumberObject *object = new (memoryManager) NumberObject(this, value); + object->prototype = numberPrototype; + return object; +} + +Object *ExecutionEngine::newBooleanObject(const Value &value) +{ + Object *object = new (memoryManager) BooleanObject(this, value); + object->prototype = booleanPrototype; + return object; +} + +ArrayObject *ExecutionEngine::newArrayObject(int count) +{ + ArrayObject *object = new (memoryManager) ArrayObject(this); + object->prototype = arrayPrototype; + + if (count) { + if (count < 0x1000) + object->arrayReserve(count); + object->setArrayLengthUnchecked(count); + } + return object; +} + +ArrayObject *ExecutionEngine::newArrayObject(const QStringList &list) +{ + ArrayObject *object = new (memoryManager) ArrayObject(this, list); + object->prototype = arrayPrototype; + return object; +} + +DateObject *ExecutionEngine::newDateObject(const Value &value) +{ + DateObject *object = new (memoryManager) DateObject(this, value); + object->prototype = datePrototype; + return object; +} + +DateObject *ExecutionEngine::newDateObject(const QDateTime &dt) +{ + DateObject *object = new (memoryManager) DateObject(this, dt); + object->prototype = datePrototype; + return object; +} + +RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) +{ + bool global = (flags & QQmlJS::V4IR::RegExp::RegExp_Global); + bool ignoreCase = false; + bool multiline = false; + if (flags & QQmlJS::V4IR::RegExp::RegExp_IgnoreCase) + ignoreCase = true; + if (flags & QQmlJS::V4IR::RegExp::RegExp_Multiline) + multiline = true; + + return newRegExpObject(RegExp::create(this, pattern, ignoreCase, multiline), global); +} + +RegExpObject *ExecutionEngine::newRegExpObject(RegExp* re, bool global) +{ + RegExpObject *object = new (memoryManager) RegExpObject(this, re, global); + object->prototype = regExpPrototype; + return object; +} + +RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re) +{ + RegExpObject *object = new (memoryManager) RegExpObject(this, re); + object->prototype = regExpPrototype; + return object; +} + +Object *ExecutionEngine::newErrorObject(const Value &value) +{ + ErrorObject *object = new (memoryManager) ErrorObject(this, value); + object->prototype = errorPrototype; + return object; +} + +Object *ExecutionEngine::newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) +{ + return new (memoryManager) SyntaxErrorObject(ctx, message); +} + +Object *ExecutionEngine::newSyntaxErrorObject(const QString &message) +{ + return new (memoryManager) SyntaxErrorObject(this, message); +} + + +Object *ExecutionEngine::newReferenceErrorObject(const QString &message) +{ + return new (memoryManager) ReferenceErrorObject(this, message); +} + +Object *ExecutionEngine::newReferenceErrorObject(const QString &message, const QString &fileName, int lineNumber) +{ + return new (memoryManager) ReferenceErrorObject(this, message, fileName, lineNumber); +} + + +Object *ExecutionEngine::newTypeErrorObject(const QString &message) +{ + return new (memoryManager) TypeErrorObject(this, message); +} + +Object *ExecutionEngine::newRangeErrorObject(const QString &message) +{ + return new (memoryManager) RangeErrorObject(this, message); +} + +Object *ExecutionEngine::newURIErrorObject(Value message) +{ + return new (memoryManager) URIErrorObject(this, message); +} + +Object *ExecutionEngine::newVariantObject(const QVariant &v) +{ + return new (memoryManager) VariantObject(this, v); +} + +Object *ExecutionEngine::newForEachIteratorObject(ExecutionContext *ctx, Object *o) +{ + return new (memoryManager) ForEachIteratorObject(ctx, o); +} + +Object *ExecutionEngine::qmlContextObject() const +{ + ExecutionContext *ctx = current; + + if (ctx->type == QV4::ExecutionContext::Type_SimpleCallContext) + ctx = ctx->parent; + + if (!ctx->outer) + return 0; + + while (ctx->outer && ctx->outer->type != ExecutionContext::Type_GlobalContext) + ctx = ctx->outer; + + assert(ctx); + if (ctx->type != ExecutionContext::Type_QmlContext) + return 0; + + return static_cast<CallContext *>(ctx)->activation; +} + +namespace { + struct LineNumberResolver { + const ExecutionEngine* engine; + QScopedPointer<QV4::NativeStackTrace> nativeTrace; + + LineNumberResolver(const ExecutionEngine *engine) + : engine(engine) + { + } + + void resolve(ExecutionEngine::StackFrame *frame, ExecutionContext *context, Function *function) + { + if (context->interpreterInstructionPointer) { + qptrdiff offset = *context->interpreterInstructionPointer - 1 - function->codeData; + frame->line = function->lineNumberForProgramCounter(offset); + } else { + if (!nativeTrace) + nativeTrace.reset(new QV4::NativeStackTrace(engine->current)); + + NativeFrame nativeFrame = nativeTrace->nextFrame(); + if (nativeFrame.function == function) + frame->line = nativeFrame.line; + } + } + }; +} + +QVector<ExecutionEngine::StackFrame> ExecutionEngine::stackTrace(int frameLimit) const +{ + LineNumberResolver lineNumbers(this); + + QVector<StackFrame> stack; + + QV4::ExecutionContext *c = current; + while (c && frameLimit) { + if (CallContext *callCtx = c->asCallContext()) { + StackFrame frame; + if (callCtx->function->function) + frame.source = callCtx->function->function->sourceFile; + frame.function = callCtx->function->name->toQString(); + frame.line = -1; + frame.column = -1; + + if (callCtx->function->function) + lineNumbers.resolve(&frame, callCtx, callCtx->function->function); + + stack.append(frame); + --frameLimit; + } + c = c->parent; + } + + if (frameLimit && globalCode) { + StackFrame frame; + frame.source = globalCode->sourceFile; + frame.function = globalCode->name->toQString(); + frame.line = -1; + frame.column = -1; + + lineNumbers.resolve(&frame, rootContext, globalCode); + + stack.append(frame); + } + return stack; +} + +ExecutionEngine::StackFrame ExecutionEngine::currentStackFrame() const +{ + StackFrame frame; + frame.line = -1; + frame.column = -1; + + QVector<StackFrame> trace = stackTrace(/*limit*/ 1); + if (!trace.isEmpty()) + frame = trace.first(); + + return frame; +} + +QUrl ExecutionEngine::resolvedUrl(const QString &file) +{ + QUrl src(file); + if (!src.isRelative()) + return src; + + QUrl base; + QV4::ExecutionContext *c = current; + while (c) { + if (CallContext *callCtx = c->asCallContext()) { + if (callCtx->function->function) + base.setUrl(callCtx->function->function->sourceFile); + break; + } + c = c->parent; + } + + if (base.isEmpty() && globalCode) + base.setUrl(globalCode->sourceFile); + + if (base.isEmpty()) + return src; + + return base.resolved(src); +} + +void ExecutionEngine::requireArgumentsAccessors(int n) +{ + if (n <= argumentsAccessors.size()) + return; + + uint oldSize = argumentsAccessors.size(); + argumentsAccessors.resize(n); + for (int i = oldSize; i < n; ++i) { + FunctionObject *get = new (memoryManager) ArgumentsGetterFunction(rootContext, i); + get->prototype = functionPrototype; + FunctionObject *set = new (memoryManager) ArgumentsSetterFunction(rootContext, i); + set->prototype = functionPrototype; + Property pd = Property::fromAccessor(get, set); + argumentsAccessors[i] = pd; + } +} + +void ExecutionEngine::markObjects() +{ + identifierTable->mark(); + + globalObject->mark(); + + if (globalCode) + globalCode->mark(); + + for (int i = 0; i < argumentsAccessors.size(); ++i) { + const Property &pd = argumentsAccessors.at(i); + if (FunctionObject *getter = pd.getter()) + getter->mark(); + if (FunctionObject *setter = pd.setter()) + setter->mark(); + } + + ExecutionContext *c = current; + while (c) { + c->mark(); + c = c->parent; + } + + for (int i = 0; i < functions.size(); ++i) + functions.at(i)->mark(); + + id_length->mark(); + id_prototype->mark(); + id_constructor->mark(); + id_arguments->mark(); + id_caller->mark(); + id_this->mark(); + id___proto__->mark(); + id_enumerable->mark(); + id_configurable->mark(); + id_writable->mark(); + id_value->mark(); + id_get->mark(); + id_set->mark(); + id_eval->mark(); + id_uintMax->mark(); + id_name->mark(); + + objectCtor.mark(); + stringCtor.mark(); + numberCtor.mark(); + booleanCtor.mark(); + arrayCtor.mark(); + functionCtor.mark(); + dateCtor.mark(); + regExpCtor.mark(); + errorCtor.mark(); + evalErrorCtor.mark(); + rangeErrorCtor.mark(); + referenceErrorCtor.mark(); + syntaxErrorCtor.mark(); + typeErrorCtor.mark(); + uRIErrorCtor.mark(); + + objectPrototype->mark(); + stringPrototype->mark(); + numberPrototype->mark(); + booleanPrototype->mark(); + arrayPrototype->mark(); + functionPrototype->mark(); + datePrototype->mark(); + regExpPrototype->mark(); + errorPrototype->mark(); + evalErrorPrototype->mark(); + rangeErrorPrototype->mark(); + referenceErrorPrototype->mark(); + syntaxErrorPrototype->mark(); + typeErrorPrototype->mark(); + uRIErrorPrototype->mark(); + + variantPrototype->mark(); + sequencePrototype->mark(); + + if (m_qmlExtensions) + m_qmlExtensions->markObjects(); +} + +namespace { + bool functionSortHelper(Function *lhs, Function *rhs) + { + return reinterpret_cast<quintptr>(lhs->code) < reinterpret_cast<quintptr>(rhs->code); + } + + struct FindHelper + { + bool operator()(Function *function, quintptr pc) + { + return reinterpret_cast<quintptr>(function->code) < pc + && (reinterpret_cast<quintptr>(function->code) + function->codeSize) < pc; + } + + bool operator()(quintptr pc, Function *function) + { + return pc < reinterpret_cast<quintptr>(function->code); + } + }; +} + +Function *ExecutionEngine::functionForProgramCounter(quintptr pc) const +{ + if (functionsNeedSort) { + qSort(functions.begin(), functions.end(), functionSortHelper); + functionsNeedSort = false; + } + + QVector<Function*>::ConstIterator it = qBinaryFind(functions.constBegin(), functions.constEnd(), + pc, FindHelper()); + if (it != functions.constEnd()) + return *it; + return 0; +} + +QmlExtensions *ExecutionEngine::qmlExtensions() +{ + if (!m_qmlExtensions) + m_qmlExtensions = new QmlExtensions; + return m_qmlExtensions; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4engine_p.h b/src/qml/qml/v4/qv4engine_p.h new file mode 100644 index 0000000000..20fbf03ae2 --- /dev/null +++ b/src/qml/qml/v4/qv4engine_p.h @@ -0,0 +1,332 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ENGINE_H +#define QV4ENGINE_H + +#include "qv4global_p.h" +#include "qv4isel_p.h" +#include "qv4util_p.h" +#include "qv4context_p.h" +#include "qv4property_p.h" +#include <private/qintrusivelist_p.h> + +namespace WTF { +class BumpPointerAllocator; +} + +QT_BEGIN_NAMESPACE + +class QV8Engine; + +namespace QV4 { +namespace Debugging { +class Debugger; +} // namespace Debugging +} + +namespace QV4 { + +struct Function; +struct Object; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct BoundFunction; +struct RegExpObject; +struct ErrorObject; +struct ArgumentsObject; +struct ExecutionContext; +struct ExecutionEngine; +class MemoryManager; +class UnwindHelper; +class ExecutableAllocator; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct RegExpPrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; +struct VariantPrototype; +struct SequencePrototype; +struct EvalFunction; +struct IdentifierTable; +struct InternalClass; +class MultiplyWrappedQObjectMap; +class RegExp; +class RegExpCache; +struct QmlExtensions; + +struct Q_QML_EXPORT ExecutionEngine +{ + MemoryManager *memoryManager; + ExecutableAllocator *executableAllocator; + ExecutableAllocator *regExpAllocator; + QScopedPointer<QQmlJS::EvalISelFactory> iselFactory; + + ExecutionContext *current; + GlobalContext *rootContext; + + WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine. + + IdentifierTable *identifierTable; + + QV4::Debugging::Debugger *debugger; + + Object *globalObject; + + Function *globalCode; + + QV8Engine *v8Engine; + + Value objectCtor; + Value stringCtor; + Value numberCtor; + Value booleanCtor; + Value arrayCtor; + Value functionCtor; + Value dateCtor; + Value regExpCtor; + Value errorCtor; + Value evalErrorCtor; + Value rangeErrorCtor; + Value referenceErrorCtor; + Value syntaxErrorCtor; + Value typeErrorCtor; + Value uRIErrorCtor; + + ObjectPrototype *objectPrototype; + StringPrototype *stringPrototype; + NumberPrototype *numberPrototype; + BooleanPrototype *booleanPrototype; + ArrayPrototype *arrayPrototype; + FunctionPrototype *functionPrototype; + DatePrototype *datePrototype; + RegExpPrototype *regExpPrototype; + ErrorPrototype *errorPrototype; + EvalErrorPrototype *evalErrorPrototype; + RangeErrorPrototype *rangeErrorPrototype; + ReferenceErrorPrototype *referenceErrorPrototype; + SyntaxErrorPrototype *syntaxErrorPrototype; + TypeErrorPrototype *typeErrorPrototype; + URIErrorPrototype *uRIErrorPrototype; + + VariantPrototype *variantPrototype; + SequencePrototype *sequencePrototype; + + QQmlJS::MemoryPool classPool; + InternalClass *emptyClass; + InternalClass *arrayClass; + + EvalFunction *evalFunction; + + QVector<Property> argumentsAccessors; + + String *id_undefined; + String *id_null; + String *id_true; + String *id_false; + String *id_boolean; + String *id_number; + String *id_string; + String *id_object; + String *id_function; + String *id_length; + String *id_prototype; + String *id_constructor; + String *id_arguments; + String *id_caller; + String *id_this; + String *id___proto__; + String *id_enumerable; + String *id_configurable; + String *id_writable; + String *id_value; + String *id_get; + String *id_set; + String *id_eval; + String *id_uintMax; + String *id_name; + + mutable QVector<Function *> functions; + mutable bool functionsNeedSort; + + quint32 m_engineId; + + RegExpCache *regExpCache; + + // Scarce resources are "exceptionally high cost" QVariant types where allowing the + // normal JavaScript GC to clean them up is likely to lead to out-of-memory or other + // out-of-resource situations. When such a resource is passed into JavaScript we + // add it to the scarceResources list and it is destroyed when we return from the + // JavaScript execution that created it. The user can prevent this behavior by + // calling preserve() on the object which removes it from this scarceResource list. + class ScarceResourceData { + public: + ScarceResourceData(const QVariant &data) : data(data) {} + QVariant data; + QIntrusiveListNode node; + }; + QIntrusiveList<ScarceResourceData, &ScarceResourceData::node> scarceResources; + + // Normally the JS wrappers for QObjects are stored in the QQmlData/QObjectPrivate, + // but any time a QObject is wrapped a second time in another engine, we have to do + // bookkeeping. + MultiplyWrappedQObjectMap *m_multiplyWrappedQObjects; + + ExecutionEngine(QQmlJS::EvalISelFactory *iselFactory = 0); + ~ExecutionEngine(); + + void enableDebugger(); + + WithContext *newWithContext(Object *with); + CatchContext *newCatchContext(String* exceptionVarName, const QV4::Value &exceptionValue); + CallContext *newCallContext(FunctionObject *f, const QV4::Value &thisObject, QV4::Value *args, int argc); + CallContext *newCallContext(void *stackSpace, FunctionObject *f, const QV4::Value &thisObject, QV4::Value *args, int argc); + CallContext *newQmlContext(FunctionObject *f, Object *qml); + ExecutionContext *pushGlobalContext(); + void pushContext(SimpleCallContext *context); + ExecutionContext *popContext(); + + Function *newFunction(const QString &name); + + FunctionObject *newBuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *)); + FunctionObject *newScriptFunction(ExecutionContext *scope, Function *function); + BoundFunction *newBoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs); + + Object *newObject(); + Object *newObject(InternalClass *internalClass); + + String *newString(const QString &s); + String *newIdentifier(const QString &text); + + Object *newStringObject(const Value &value); + Object *newNumberObject(const Value &value); + Object *newBooleanObject(const Value &value); + + ArrayObject *newArrayObject(int count = 0); + ArrayObject *newArrayObject(const QStringList &list); + + DateObject *newDateObject(const Value &value); + DateObject *newDateObject(const QDateTime &dt); + + RegExpObject *newRegExpObject(const QString &pattern, int flags); + RegExpObject *newRegExpObject(RegExp* re, bool global); + RegExpObject *newRegExpObject(const QRegExp &re); + + Object *newErrorObject(const Value &value); + Object *newSyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message); + Object *newSyntaxErrorObject(const QString &message); + Object *newReferenceErrorObject(const QString &message); + Object *newReferenceErrorObject(const QString &message, const QString &fileName, int lineNumber); + Object *newTypeErrorObject(const QString &message); + Object *newRangeErrorObject(const QString &message); + Object *newURIErrorObject(Value message); + + Object *newVariantObject(const QVariant &v); + + Object *newForEachIteratorObject(ExecutionContext *ctx, Object *o); + + Object *qmlContextObject() const; + + struct StackFrame { + QString source; + QString function; + int line; + int column; + }; + typedef QVector<StackFrame> StackTrace; + StackTrace stackTrace(int frameLimit = -1) const; + StackFrame currentStackFrame() const; + QUrl resolvedUrl(const QString &file); + + void requireArgumentsAccessors(int n); + + void markObjects(); + + void initRootContext(); + + InternalClass *newClass(const InternalClass &other); + + Function *functionForProgramCounter(quintptr pc) const; + + QmlExtensions *qmlExtensions(); + +private: + QmlExtensions *m_qmlExtensions; +}; + +inline void ExecutionEngine::pushContext(SimpleCallContext *context) +{ + context->parent = current; + current = context; + current->currentEvalCode = 0; +} + +inline ExecutionContext *ExecutionEngine::popContext() +{ + CallContext *c = current->asCallContext(); + if (c && !c->needsOwnArguments()) { + c->arguments = 0; + c->argumentCount = 0; + } + + current = current->parent; + return current; +} + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4ENGINE_H diff --git a/src/qml/qml/v4/qv4errorobject.cpp b/src/qml/qml/v4/qv4errorobject.cpp new file mode 100644 index 0000000000..516a4d37f8 --- /dev/null +++ b/src/qml/qml/v4/qv4errorobject.cpp @@ -0,0 +1,351 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4errorobject_p.h" +#include "qv4mm_p.h" +#include <QtCore/qnumeric.h> +#include <QtCore/qmath.h> +#include <QtCore/QDateTime> +#include <QtCore/QStringList> +#include <QtCore/QDebug> +#include <cmath> +#include <qmath.h> +#include <qnumeric.h> +#include <cassert> + +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsast_p.h> +#include <qv4jsir_p.h> +#include <qv4codegen_p.h> +#include <qv4isel_masm_p.h> + +#ifndef Q_OS_WIN +# include <time.h> +# ifndef Q_OS_VXWORKS +# include <sys/time.h> +# else +# include "qplatformdefs.h" +# endif +#else +# include <windows.h> +#endif + +using namespace QV4; + +ErrorObject::ErrorObject(ExecutionEngine *engine, const Value &message, ErrorType t) + : Object(engine) + , stack(0) +{ + type = Type_ErrorObject; + vtbl = &static_vtbl; + subtype = t; + defineAccessorProperty(engine, QStringLiteral("stack"), ErrorObject::method_get_stack, 0); + + if (!message.isUndefined()) + defineDefaultProperty(engine->newString(QStringLiteral("message")), message); + defineDefaultProperty(engine, QStringLiteral("name"), Value::fromString(engine, className())); + + stackTrace = engine->stackTrace(); + if (!stackTrace.isEmpty()) { + defineDefaultProperty(engine, QStringLiteral("fileName"), Value::fromString(engine, stackTrace.at(0).source)); + defineDefaultProperty(engine, QStringLiteral("lineNumber"), Value::fromInt32(stackTrace.at(0).line)); + } +} + +Value ErrorObject::method_get_stack(SimpleCallContext *ctx) +{ + ErrorObject *This = ctx->thisObject.asErrorObject(); + if (!This) + ctx->throwTypeError(); + if (!This->stack) { + QString trace; + for (int i = 0; i < This->stackTrace.count(); ++i) { + if (i > 0) + trace += QLatin1Char('\n'); + const ExecutionEngine::StackFrame &frame = This->stackTrace[i]; + trace += frame.function; + trace += QLatin1Char('@'); + trace += frame.source; + if (frame.line >= 0) { + trace += QLatin1Char(':'); + trace += QString::number(frame.line); + } + } + This->stack = ctx->engine->newString(trace); + } + return Value::fromString(This->stack); +} + +void ErrorObject::markObjects(Managed *that) +{ + ErrorObject *This = that->asErrorObject(); + if (This->stack) + This->stack->mark(); + Object::markObjects(that); +} + +DEFINE_MANAGED_VTABLE(ErrorObject); + +DEFINE_MANAGED_VTABLE(SyntaxErrorObject); + +SyntaxErrorObject::SyntaxErrorObject(ExecutionEngine *engine, const Value &msg) + : ErrorObject(engine, msg, SyntaxError) + , msg(0) +{ + vtbl = &static_vtbl; + prototype = engine->syntaxErrorPrototype; +} + +SyntaxErrorObject::SyntaxErrorObject(ExecutionEngine *engine, const QString &msg) + : ErrorObject(engine, Value::fromString(engine, msg), SyntaxError) + , msg(0) +{ + vtbl = &static_vtbl; + prototype = engine->syntaxErrorPrototype; +} + +SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *message) + : ErrorObject(ctx->engine, message ? Value::fromString(message->buildFullMessage(ctx)) : ctx->argument(0), SyntaxError) + , msg(message) +{ + vtbl = &static_vtbl; + prototype = ctx->engine->syntaxErrorPrototype; + if (message) { + defineDefaultProperty(ctx->engine, QStringLiteral("fileName"), Value::fromString(ctx, message->fileName)); + defineDefaultProperty(ctx->engine, QStringLiteral("lineNumber"), Value::fromInt32(message->startLine)); + } +} + + + +EvalErrorObject::EvalErrorObject(ExecutionEngine *engine, const Value &message) + : ErrorObject(engine, message, EvalError) +{ + prototype = engine->evalErrorPrototype; +} + +RangeErrorObject::RangeErrorObject(ExecutionEngine *engine, const Value &message) + : ErrorObject(engine, message, RangeError) +{ + prototype = engine->rangeErrorPrototype; +} + +RangeErrorObject::RangeErrorObject(ExecutionEngine *engine, const QString &message) + : ErrorObject(engine, Value::fromString(engine, message), RangeError) +{ + prototype = engine->rangeErrorPrototype; +} + +ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const Value &message) + : ErrorObject(engine, message, ReferenceError) +{ + prototype = engine->referenceErrorPrototype; +} + +ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const QString &message) + : ErrorObject(engine, Value::fromString(engine, message), ReferenceError) +{ + prototype = engine->referenceErrorPrototype; +} + +ReferenceErrorObject::ReferenceErrorObject(ExecutionEngine *engine, const QString &msg, const QString &fileName, int lineNumber) + : ErrorObject(engine, Value::fromString(engine, msg), ReferenceError) +{ + prototype = engine->referenceErrorPrototype; + defineDefaultProperty(engine, QStringLiteral("fileName"), Value::fromString(engine->rootContext, fileName)); + defineDefaultProperty(engine, QStringLiteral("lineNumber"), Value::fromInt32(lineNumber)); +} + +TypeErrorObject::TypeErrorObject(ExecutionEngine *engine, const Value &message) + : ErrorObject(engine, message, TypeError) +{ + prototype = engine->typeErrorPrototype; +} + +TypeErrorObject::TypeErrorObject(ExecutionEngine *engine, const QString &message) + : ErrorObject(engine, Value::fromString(engine, message), TypeError) +{ + prototype = engine->typeErrorPrototype; +} + +URIErrorObject::URIErrorObject(ExecutionEngine *engine, const Value &message) + : ErrorObject(engine, message, URIError) +{ + prototype = engine->uRIErrorPrototype; +} + +DEFINE_MANAGED_VTABLE(ErrorCtor); +DEFINE_MANAGED_VTABLE(EvalErrorCtor); +DEFINE_MANAGED_VTABLE(RangeErrorCtor); +DEFINE_MANAGED_VTABLE(ReferenceErrorCtor); +DEFINE_MANAGED_VTABLE(SyntaxErrorCtor); +DEFINE_MANAGED_VTABLE(TypeErrorCtor); +DEFINE_MANAGED_VTABLE(URIErrorCtor); + +ErrorCtor::ErrorCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Error"))) +{ + vtbl = &static_vtbl; +} + +ErrorCtor::ErrorCtor(ExecutionContext *scope, String *name) + : FunctionObject(scope, name) +{ + vtbl = &static_vtbl; +} + +Value ErrorCtor::construct(Managed *m, Value *args, int argc) +{ + return Value::fromObject(m->engine()->newErrorObject(argc ? args[0] : Value::undefinedValue())); +} + +Value ErrorCtor::call(Managed *that, const Value &, Value *args, int argc) +{ + return that->construct(args, argc); +} + +EvalErrorCtor::EvalErrorCtor(ExecutionContext *scope) + : ErrorCtor(scope, scope->engine->newIdentifier("EvalError")) +{ + vtbl = &static_vtbl; +} + +Value EvalErrorCtor::construct(Managed *m, Value *args, int argc) +{ + return Value::fromObject(new (m->engine()->memoryManager) EvalErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); +} + +RangeErrorCtor::RangeErrorCtor(ExecutionContext *scope) + : ErrorCtor(scope, scope->engine->newIdentifier("RangeError")) +{ + vtbl = &static_vtbl; +} + +Value RangeErrorCtor::construct(Managed *m, Value *args, int argc) +{ + return Value::fromObject(new (m->engine()->memoryManager) RangeErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); +} + +ReferenceErrorCtor::ReferenceErrorCtor(ExecutionContext *scope) + : ErrorCtor(scope, scope->engine->newIdentifier("ReferenceError")) +{ + vtbl = &static_vtbl; +} + +Value ReferenceErrorCtor::construct(Managed *m, Value *args, int argc) +{ + return Value::fromObject(new (m->engine()->memoryManager) ReferenceErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); +} + +SyntaxErrorCtor::SyntaxErrorCtor(ExecutionContext *scope) + : ErrorCtor(scope, scope->engine->newIdentifier("SyntaxError")) +{ + vtbl = &static_vtbl; +} + +Value SyntaxErrorCtor::construct(Managed *m, Value *args, int argc) +{ + return Value::fromObject(new (m->engine()->memoryManager) SyntaxErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); +} + +TypeErrorCtor::TypeErrorCtor(ExecutionContext *scope) + : ErrorCtor(scope, scope->engine->newIdentifier("TypeError")) +{ + vtbl = &static_vtbl; +} + +Value TypeErrorCtor::construct(Managed *m, Value *args, int argc) +{ + return Value::fromObject(new (m->engine()->memoryManager) TypeErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); +} + +URIErrorCtor::URIErrorCtor(ExecutionContext *scope) + : ErrorCtor(scope, scope->engine->newIdentifier("URIError")) +{ + vtbl = &static_vtbl; +} + +Value URIErrorCtor::construct(Managed *m, Value *args, int argc) +{ + return Value::fromObject(new (m->engine()->memoryManager) URIErrorObject(m->engine(), argc ? args[0] : Value::undefinedValue())); +} + +void ErrorPrototype::init(ExecutionEngine *engine, const Value &ctor, Object *obj) +{ + ctor.objectValue()->defineReadonlyProperty(engine->id_prototype, Value::fromObject(obj)); + ctor.objectValue()->defineReadonlyProperty(engine->id_length, Value::fromInt32(1)); + obj->defineDefaultProperty(engine, QStringLiteral("constructor"), ctor); + obj->defineDefaultProperty(engine, QStringLiteral("toString"), method_toString, 0); + obj->defineDefaultProperty(engine, QStringLiteral("message"), Value::fromString(engine, QString())); +} + +Value ErrorPrototype::method_toString(SimpleCallContext *ctx) +{ + Object *o = ctx->thisObject.asObject(); + if (!o) + ctx->throwTypeError(); + + Value name = o->get(ctx->engine->newString(QString::fromLatin1("name"))); + QString qname; + if (name.isUndefined()) + qname = QString::fromLatin1("Error"); + else + qname = __qmljs_to_string(name, ctx).stringValue()->toQString(); + + Value message = o->get(ctx->engine->newString(QString::fromLatin1("message"))); + QString qmessage; + if (!message.isUndefined()) + qmessage = __qmljs_to_string(message, ctx).stringValue()->toQString(); + + QString str; + if (qname.isEmpty()) { + str = qmessage; + } else if (qmessage.isEmpty()) { + str = qname; + } else { + str = qname + QLatin1String(": ") + qmessage; + } + + return Value::fromString(ctx, str); +} diff --git a/src/qml/qml/v4/qv4errorobject_p.h b/src/qml/qml/v4/qv4errorobject_p.h new file mode 100644 index 0000000000..d3e0f107bc --- /dev/null +++ b/src/qml/qml/v4/qv4errorobject_p.h @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ERROROBJECT_H +#define QV4ERROROBJECT_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct SyntaxErrorObject; + +struct ErrorObject: Object { + Q_MANAGED + + enum ErrorType { + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError + }; + + ErrorObject(ExecutionEngine *engine, const Value &message, ErrorType t = Error); + + SyntaxErrorObject *asSyntaxError(); + + ExecutionEngine::StackTrace stackTrace; + String *stack; + + static Value method_get_stack(SimpleCallContext *ctx); + static void markObjects(Managed *that); + static void destroy(Managed *that) { static_cast<ErrorObject *>(that)->~ErrorObject(); } +}; + +struct EvalErrorObject: ErrorObject { + EvalErrorObject(ExecutionEngine *engine, const Value &message); +}; + +struct RangeErrorObject: ErrorObject { + RangeErrorObject(ExecutionEngine *engine, const Value &message); + RangeErrorObject(ExecutionEngine *engine, const QString &msg); +}; + +struct ReferenceErrorObject: ErrorObject { + ReferenceErrorObject(ExecutionEngine *engine, const Value &message); + ReferenceErrorObject(ExecutionEngine *engine, const QString &msg); + ReferenceErrorObject(ExecutionEngine *engine, const QString &msg, const QString &fileName, int lineNumber); +}; + +struct SyntaxErrorObject: ErrorObject { + SyntaxErrorObject(ExecutionEngine *engine, const Value &msg); + SyntaxErrorObject(ExecutionEngine *engine, const QString &msg); + SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *msg); + ~SyntaxErrorObject() { delete msg; } + static void destroy(Managed *that) { static_cast<SyntaxErrorObject *>(that)->~SyntaxErrorObject(); } + + DiagnosticMessage *message() { return msg; } + +private: + DiagnosticMessage *msg; +protected: + static const ManagedVTable static_vtbl; +}; + +struct TypeErrorObject: ErrorObject { + TypeErrorObject(ExecutionEngine *engine, const Value &message); + TypeErrorObject(ExecutionEngine *engine, const QString &msg); +}; + +struct URIErrorObject: ErrorObject { + URIErrorObject(ExecutionEngine *engine, const Value &message); +}; + +struct ErrorCtor: FunctionObject +{ + ErrorCtor(ExecutionContext *scope); + ErrorCtor(ExecutionContext *scope, String *name); + + static Value construct(Managed *, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct EvalErrorCtor: ErrorCtor +{ + EvalErrorCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct RangeErrorCtor: ErrorCtor +{ + RangeErrorCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct ReferenceErrorCtor: ErrorCtor +{ + ReferenceErrorCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct SyntaxErrorCtor: ErrorCtor +{ + SyntaxErrorCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct TypeErrorCtor: ErrorCtor +{ + TypeErrorCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct URIErrorCtor: ErrorCtor +{ + URIErrorCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + +protected: + static const ManagedVTable static_vtbl; +}; + + +struct ErrorPrototype: ErrorObject +{ + // ### shouldn't be undefined + ErrorPrototype(ExecutionEngine *engine): ErrorObject(engine, Value::undefinedValue()) {} + void init(ExecutionEngine *engine, const Value &ctor) { init(engine, ctor, this); } + + static void init(ExecutionEngine *engine, const Value &ctor, Object *obj); + static Value method_toString(SimpleCallContext *ctx); +}; + +struct EvalErrorPrototype: EvalErrorObject +{ + EvalErrorPrototype(ExecutionEngine *engine): EvalErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; } + void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } +}; + +struct RangeErrorPrototype: RangeErrorObject +{ + RangeErrorPrototype(ExecutionEngine *engine): RangeErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; } + void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } +}; + +struct ReferenceErrorPrototype: ReferenceErrorObject +{ + ReferenceErrorPrototype(ExecutionEngine *engine): ReferenceErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; } + void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } +}; + +struct SyntaxErrorPrototype: SyntaxErrorObject +{ + SyntaxErrorPrototype(ExecutionEngine *engine): SyntaxErrorObject(engine, 0) { vtbl = &static_vtbl; } + void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } +}; + +struct TypeErrorPrototype: TypeErrorObject +{ + TypeErrorPrototype(ExecutionEngine *engine): TypeErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; } + void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } +}; + +struct URIErrorPrototype: URIErrorObject +{ + URIErrorPrototype(ExecutionEngine *engine): URIErrorObject(engine, Value::undefinedValue()) { vtbl = &static_vtbl; } + void init(ExecutionEngine *engine, const Value &ctor) { ErrorPrototype::init(engine, ctor, this); } +}; + + +inline SyntaxErrorObject *ErrorObject::asSyntaxError() +{ + return subtype == SyntaxError ? static_cast<SyntaxErrorObject *>(this) : 0; +} + +} + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4/qv4exception.cpp b/src/qml/qml/v4/qv4exception.cpp new file mode 100644 index 0000000000..9f15c27ffc --- /dev/null +++ b/src/qml/qml/v4/qv4exception.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4exception_p.h" +#include "qv4errorobject_p.h" +#include "qv4debugging_p.h" +#include "qv4unwindhelper_p.h" + +#include <wtf/Platform.h> + +#if USE(LIBUNWIND_DEBUG) +#include <libunwind.h> +#include <execinfo.h> +#endif + +QT_BEGIN_NAMESPACE + +using namespace QV4; + + +void Exception::throwException(ExecutionContext *context, const Value &value) +{ + if (context->engine->debugger) + context->engine->debugger->aboutToThrow(value); + + UnwindHelper::prepareForUnwind(context); + +#if USE(LIBUNWIND_DEBUG) + printf("about to throw exception. walking stack first with libunwind:\n"); + unw_cursor_t cursor; unw_context_t uc; + unw_word_t ip, sp; + + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + while (unw_step(&cursor) > 0) { + unw_get_reg(&cursor, UNW_REG_IP, &ip); + unw_get_reg(&cursor, UNW_REG_SP, &sp); + printf("ip = %lx, sp = %lx ", (long) ip, (long) sp); + void * const addr = (void*)ip; + char **symbol = backtrace_symbols(&addr, 1); + printf("%s", symbol[0]); + free(symbol); + printf("\n"); + } + printf("stack walked. throwing exception now...\n"); +#endif + + throwInternal(context, value); +} + +Exception::Exception(ExecutionContext *throwingContext, const Value &exceptionValue) + : exception(exceptionValue) +{ + this->throwingContext = throwingContext->engine->current; + accepted = false; + if (ErrorObject *error = exceptionValue.asErrorObject()) + m_stackTrace = error->stackTrace; + else + m_stackTrace = throwingContext->engine->stackTrace(); +} + +Exception::~Exception() +{ + assert(accepted); +} + +void Exception::accept(ExecutionContext *catchingContext) +{ + assert(!accepted); + accepted = true; + partiallyUnwindContext(catchingContext); +} + +void Exception::partiallyUnwindContext(ExecutionContext *catchingContext) +{ + if (!throwingContext) + return; + ExecutionContext *context = throwingContext; + while (context != catchingContext) + context = context->engine->popContext(); + throwingContext = context; +} + +#if !defined(V4_CXX_ABI_EXCEPTION) +void Exception::throwInternal(ExecutionContext *throwingContext, const Value &exceptionValue) +{ + throw Exception(throwingContext, exceptionValue); +} +#endif + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4exception_gcc.cpp b/src/qml/qml/v4/qv4exception_gcc.cpp new file mode 100644 index 0000000000..0324a06e0b --- /dev/null +++ b/src/qml/qml/v4/qv4exception_gcc.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4exception_p.h" + +#include <unwind.h> +#include <cxxabi.h> +#include <bits/atomic_word.h> +#include <typeinfo> +#include <exception> + +/* + * This is a little bit hacky as it relies on the fact that exceptions are + * reference counted in libstdc++ and that affects the layout of the standardized + * cxa_exception, making it bigger. LLVM's libcxxabi stores the reference count + * differently, so this here is entirely GNU libstdc++ specific. + * + * Eliminating this dependency is doable but requires replacing the use of C++ exceptions + * with foreign exceptions (a different exception class) and then using __cxa_get_globals + * to get hold of the exception inside the catch (...). AFAICS that would be portable. + */ + +namespace { + +// 2.1.1 from http://mentorembedded.github.io/cxx-abi/abi-eh.html +struct cxa_exception { + std::type_info *typeInfo; + void (*exceptionDestructor)(void*); + std::unexpected_handler unexpectedHandler; + std::terminate_handler terminateHandler; + cxa_exception *nextException; + int handlerCount; +#ifdef __ARM_EABI_UNWINDER__ + cxa_exception *nextPropagatingException; + int propagationCount; +#else + int handlerSwitchValue; + const char *actionRecord; + const char *languageSpecificData; + void *catchTemp; + void *adjustedPtr; +#endif + _Unwind_Exception unwindHeader; +}; + +// This is what libstdc++ actually allocates +struct gcc_refcounted_compatible_exception { + _Atomic_word refCount; + cxa_exception x; +}; + +} + +static void exception_cleanup(_Unwind_Reason_Code, _Unwind_Exception *ex) +{ + gcc_refcounted_compatible_exception *exception = reinterpret_cast<gcc_refcounted_compatible_exception *>(ex + 1) - 1; + if (!--exception->refCount) { + if (exception->x.exceptionDestructor) + exception->x.exceptionDestructor(ex + 1); + abi::__cxa_free_exception(ex + 1); + } +} + +static void exception_destructor(void *ex) +{ + reinterpret_cast<QV4::Exception *>(ex)->~Exception(); +} + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +void Exception::throwInternal(ExecutionContext *throwingContext, const Value &exceptionValue) +{ + void *rawException = abi::__cxa_allocate_exception(sizeof(QV4::Exception)); + gcc_refcounted_compatible_exception *refCountedException = reinterpret_cast<gcc_refcounted_compatible_exception *>(rawException) - 1; + cxa_exception *exception = &refCountedException->x; + + (void)new (rawException) Exception(throwingContext, exceptionValue); + + refCountedException->refCount = 1; + exception->typeInfo = const_cast<std::type_info*>(&typeid(Exception)); + exception->exceptionDestructor = &exception_destructor; + exception->unexpectedHandler = std::unexpected; + exception->terminateHandler = std::terminate; + exception->unwindHeader.exception_cleanup = &exception_cleanup; +#ifdef __ARM_EABI_UNWINDER__ + exception->unwindHeader.exception_class[0] = 'G'; + exception->unwindHeader.exception_class[1] = 'N'; + exception->unwindHeader.exception_class[2] = 'U'; + exception->unwindHeader.exception_class[3] = 'C'; + exception->unwindHeader.exception_class[4] = 'C'; + exception->unwindHeader.exception_class[5] = '+'; + exception->unwindHeader.exception_class[6] = '+'; + exception->unwindHeader.exception_class[7] = 0; +#else + exception->unwindHeader.exception_class = 0x474e5543432b2b00; // GNUCC++0 +#endif + + _Unwind_RaiseException(&exception->unwindHeader); + abi::__cxa_begin_catch(rawException); + std::terminate(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4exception_p.h b/src/qml/qml/v4/qv4exception_p.h new file mode 100644 index 0000000000..8ba06f57f4 --- /dev/null +++ b/src/qml/qml/v4/qv4exception_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4EXCEPTION_GNU_P +#define QV4EXCEPTION_GNU_P + +#include <qglobal.h> +#include "qv4value_p.h" +#include "qv4engine_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Q_QML_EXPORT Exception { + static void throwException(ExecutionContext *throwingContext, const Value &exceptionValue); + + ~Exception(); + + void accept(ExecutionContext *catchingContext); + + void partiallyUnwindContext(ExecutionContext *catchingContext); + + Value value() const { return exception; } + + ExecutionEngine::StackTrace stackTrace() const { return m_stackTrace; } + +private: + void *operator new(size_t, void *p) { return p; } + + explicit Exception(ExecutionContext *throwingContext, const Value &exceptionValue); + + ExecutionContext *throwingContext; + bool accepted; + PersistentValue exception; + ExecutionEngine::StackTrace m_stackTrace; + static void throwInternal(ExecutionContext *throwingContext, const Value &exceptionValue); +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4EXCEPTION_GNU_P diff --git a/src/qml/qml/v4/qv4executableallocator.cpp b/src/qml/qml/v4/qv4executableallocator.cpp new file mode 100644 index 0000000000..a754663556 --- /dev/null +++ b/src/qml/qml/v4/qv4executableallocator.cpp @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4executableallocator_p.h" + +#include <assert.h> +#include <wtf/StdLibExtras.h> +#include <wtf/PageAllocation.h> + +using namespace QV4; + +void *ExecutableAllocator::Allocation::start() const +{ + return reinterpret_cast<void*>(addr); +} + +ExecutableAllocator::Allocation *ExecutableAllocator::Allocation::split(size_t dividingSize) +{ + Allocation *remainder = new Allocation; + if (next) + next->prev = remainder; + + remainder->next = next; + next = remainder; + + remainder->prev = this; + + remainder->size = size - dividingSize; + remainder->free = free; + remainder->addr = addr + dividingSize; + size = dividingSize; + + return remainder; +} + +bool ExecutableAllocator::Allocation::mergeNext(ExecutableAllocator *allocator) +{ + assert(free); + if (!next || !next->free) + return false; + + allocator->freeAllocations.remove(size, this); + allocator->freeAllocations.remove(next->size, next); + + size += next->size; + Allocation *newNext = next->next; + delete next; + next = newNext; + if (next) + next->prev = this; + + allocator->freeAllocations.insert(size, this); + return true; +} + +bool ExecutableAllocator::Allocation::mergePrevious(ExecutableAllocator *allocator) +{ + assert(free); + if (!prev || !prev->free) + return false; + + allocator->freeAllocations.remove(size, this); + allocator->freeAllocations.remove(prev->size, prev); + + prev->size += size; + if (next) + next->prev = prev; + prev->next = next; + + allocator->freeAllocations.insert(prev->size, prev); + + delete this; + return true; +} + +ExecutableAllocator::ChunkOfPages::~ChunkOfPages() +{ + delete unwindInfo; + Allocation *alloc = firstAllocation; + while (alloc) { + Allocation *next = alloc->next; + delete alloc; + alloc = next; + } + pages->deallocate(); + delete pages; +} + +bool ExecutableAllocator::ChunkOfPages::contains(Allocation *alloc) const +{ + Allocation *it = firstAllocation; + while (it) { + if (it == alloc) + return true; + it = it->next; + } + return false; +} + +ExecutableAllocator::~ExecutableAllocator() +{ + qDeleteAll(chunks); +} + +ExecutableAllocator::Allocation *ExecutableAllocator::allocate(size_t size) +{ + Allocation *allocation = 0; + + // Code is best aligned to 16-byte boundaries. + size = WTF::roundUpToMultipleOf(16, size); + + QMultiMap<size_t, Allocation*>::Iterator it = freeAllocations.lowerBound(size); + if (it != freeAllocations.end()) { + allocation = *it; + freeAllocations.erase(it); + } + + if (!allocation) { + ChunkOfPages *chunk = new ChunkOfPages; + size_t allocSize = WTF::roundUpToMultipleOf(WTF::pageSize(), size); + chunk->pages = new WTF::PageAllocation(WTF::PageAllocation::allocate(allocSize, OSAllocator::JSJITCodePages)); + chunks.insert(reinterpret_cast<quintptr>(chunk->pages->base()) - 1, chunk); + allocation = new Allocation; + allocation->addr = reinterpret_cast<quintptr>(chunk->pages->base()); + allocation->size = allocSize; + allocation->free = true; + chunk->firstAllocation = allocation; + } + + assert(allocation); + assert(allocation->free); + + allocation->free = false; + + if (allocation->size > size) { + Allocation *remainder = allocation->split(size); + remainder->free = true; + if (!remainder->mergeNext(this)) + freeAllocations.insert(remainder->size, remainder); + } + + return allocation; +} + +void ExecutableAllocator::free(Allocation *allocation) +{ + assert(allocation); + + allocation->free = true; + + QMap<quintptr, ChunkOfPages*>::Iterator it = chunks.lowerBound(allocation->addr); + if (it != chunks.begin()) + --it; + assert(it != chunks.end()); + ChunkOfPages *chunk = *it; + assert(chunk->contains(allocation)); + + bool merged = allocation->mergeNext(this); + merged |= allocation->mergePrevious(this); + if (!merged) + freeAllocations.insert(allocation->size, allocation); + + allocation = 0; + + if (!chunk->firstAllocation->next) { + freeAllocations.remove(chunk->firstAllocation->size, chunk->firstAllocation); + chunks.erase(it); + delete chunk; + return; + } +} + +ExecutableAllocator::ChunkOfPages *ExecutableAllocator::chunkForAllocation(Allocation *allocation) const +{ + QMap<quintptr, ChunkOfPages*>::ConstIterator it = chunks.lowerBound(allocation->addr); + if (it != chunks.begin()) + --it; + if (it == chunks.end()) + return 0; + return *it; +} + diff --git a/src/qml/qml/v4/qv4executableallocator_p.h b/src/qml/qml/v4/qv4executableallocator_p.h new file mode 100644 index 0000000000..2a304baf9c --- /dev/null +++ b/src/qml/qml/v4/qv4executableallocator_p.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4EXECUTABLEALLOCATOR_H +#define QV4EXECUTABLEALLOCATOR_H + +#include "qv4global_p.h" + +#include <QMultiMap> +#include <QHash> +#include <QVector> +#include <QByteArray> + +namespace WTF { +class PageAllocation; +}; + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +class Q_AUTOTEST_EXPORT ExecutableAllocator +{ +public: + struct ChunkOfPages; + struct Allocation; + + ~ExecutableAllocator(); + + Allocation *allocate(size_t size); + void free(Allocation *allocation); + + struct Allocation + { + Allocation() + : addr(0) + , size(0) + , free(true) + , next(0) + , prev(0) + {} + + void *start() const; + + private: + friend class ExecutableAllocator; + + Allocation *split(size_t dividingSize); + bool mergeNext(ExecutableAllocator *allocator); + bool mergePrevious(ExecutableAllocator *allocator); + + quintptr addr; + uint size : 31; // More than 2GB of function code? nah :) + uint free : 1; + Allocation *next; + Allocation *prev; + }; + + // for debugging / unit-testing + int freeAllocationCount() const { return freeAllocations.count(); } + int chunkCount() const { return chunks.count(); } + + // Used to store CIE+FDE on x86/x86-64. + struct PlatformUnwindInfo + { + virtual ~PlatformUnwindInfo() {} + }; + + struct ChunkOfPages + { + ChunkOfPages() + : pages(0) + , firstAllocation(0) + , unwindInfo(0) + {} + ~ChunkOfPages(); + + WTF::PageAllocation *pages; + Allocation *firstAllocation; + PlatformUnwindInfo *unwindInfo; + + bool contains(Allocation *alloc) const; + }; + + ChunkOfPages *chunkForAllocation(Allocation *allocation) const; + +private: + QMultiMap<size_t, Allocation*> freeAllocations; + QMap<quintptr, ChunkOfPages*> chunks; +}; + +} + +QT_END_NAMESPACE + +#endif // QV4EXECUTABLEALLOCATOR_H diff --git a/src/qml/qml/v4/qv4function.cpp b/src/qml/qml/v4/qv4function.cpp new file mode 100644 index 0000000000..bf633a9b41 --- /dev/null +++ b/src/qml/qml/v4/qv4function.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4function_p.h" +#include "qv4managed_p.h" +#include "qv4string_p.h" +#include "qv4value_p.h" + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +Function::~Function() +{ + delete[] codeData; +} + +void Function::mark() +{ + if (name) + name->mark(); + for (int i = 0; i < formals.size(); ++i) + formals.at(i)->mark(); + for (int i = 0; i < locals.size(); ++i) + locals.at(i)->mark(); + for (int i = 0; i < generatedValues.size(); ++i) + if (Managed *m = generatedValues.at(i).asManaged()) + m->mark(); + for (int i = 0; i < identifiers.size(); ++i) + identifiers.at(i)->mark(); +} + +namespace QV4 { +bool operator<(const LineNumberMapping &mapping, qptrdiff pc) +{ + return mapping.codeOffset < pc; +} +} + +int Function::lineNumberForProgramCounter(qptrdiff offset) const +{ + QVector<LineNumberMapping>::ConstIterator it = qLowerBound(lineNumberMappings.begin(), lineNumberMappings.end(), offset); + if (it != lineNumberMappings.constBegin() && lineNumberMappings.count() > 0) + --it; + if (it == lineNumberMappings.constEnd()) + return -1; + return it->lineNumber; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4function_p.h b/src/qml/qml/v4/qv4function_p.h new file mode 100644 index 0000000000..4dd0533d51 --- /dev/null +++ b/src/qml/qml/v4/qv4function_p.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4FUNCTION_H +#define QV4FUNCTION_H + +#include "qv4global_p.h" + +#include <QtCore/QVector> +#include <QtCore/QByteArray> +#include <QtCore/qurl.h> + +#include <config.h> +#include <assembler/MacroAssemblerCodeRef.h> +#include "qv4value_def_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct String; +struct Function; +struct Object; +struct FunctionObject; +struct ExecutionContext; +struct ExecutionEngine; +class MemoryManager; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; +struct InternalClass; +struct Lookup; + +struct LineNumberMapping +{ + quint32 codeOffset; + int lineNumber; +}; + +struct Function { + String *name; + + Value (*code)(ExecutionContext *, const uchar *); + const uchar *codeData; + JSC::MacroAssemblerCodeRef codeRef; + quint32 codeSize; + + QVector<String *> formals; + QVector<String *> locals; + QVector<Value> generatedValues; + QVector<String *> identifiers; + QVector<Function *> nestedFunctions; + Function *outer; + + Lookup *lookups; + + bool hasNestedFunctions; + bool hasDirectEval; + bool usesArgumentsObject; + bool isStrict; + bool isNamedExpression; + + QString sourceFile; + QVector<LineNumberMapping> lineNumberMappings; + + Function(String *name) + : name(name) + , code(0) + , codeData(0) + , codeSize(0) + , outer(0) + , lookups(0) + , hasNestedFunctions(0) + , hasDirectEval(false) + , usesArgumentsObject(false) + , isStrict(false) + , isNamedExpression(false) + {} + ~Function(); + + inline bool needsActivation() const { return hasNestedFunctions || hasDirectEval || usesArgumentsObject; } + + void mark(); + + int lineNumberForProgramCounter(qptrdiff offset) const; +}; + +} +Q_DECLARE_TYPEINFO(QV4::LineNumberMapping, Q_PRIMITIVE_TYPE); + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4/qv4functionobject.cpp b/src/qml/qml/v4/qv4functionobject.cpp new file mode 100644 index 0000000000..ffdd08ed0a --- /dev/null +++ b/src/qml/qml/v4/qv4functionobject.cpp @@ -0,0 +1,558 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4object_p.h" +#include "qv4jsir_p.h" +#include "qv4isel_p.h" +#include "qv4objectproto_p.h" +#include "qv4stringobject_p.h" +#include "qv4function_p.h" +#include "qv4mm_p.h" +#include "qv4exception_p.h" + +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsast_p.h> +#include <qv4jsir_p.h> +#include <qv4codegen_p.h> +#include "private/qlocale_tools_p.h" + +#include <QtCore/qmath.h> +#include <QtCore/QDebug> +#include <cassert> +#include <typeinfo> +#include <iostream> +#include "qv4alloca_p.h" + +using namespace QV4; + + +DEFINE_MANAGED_VTABLE(FunctionObject); + +FunctionObject::FunctionObject(ExecutionContext *scope, String *name) + : Object(scope->engine) + , scope(scope) + , name(name) + , formalParameterList(0) + , varList(0) + , formalParameterCount(0) + , varCount(0) + , function(0) +{ + vtbl = &static_vtbl; + prototype = scope->engine->functionPrototype; + + type = Type_FunctionObject; + needsActivation = true; + usesArgumentsObject = false; + strictMode = false; +#ifndef QT_NO_DEBUG + assert(scope->next != (ExecutionContext *)0x1); +#endif + + if (name) + defineReadonlyProperty(scope->engine->id_name, Value::fromString(name)); +} + +Value FunctionObject::newInstance() +{ + return construct(0, 0); +} + +bool FunctionObject::hasInstance(Managed *that, const Value &value) +{ + FunctionObject *f = static_cast<FunctionObject *>(that); + + Object *v = value.asObject(); + if (!v) + return false; + + ExecutionContext *ctx = f->engine()->current; + Object *o = f->get(ctx->engine->id_prototype).asObject(); + if (!o) + ctx->throwTypeError(); + + while (v) { + v = v->prototype; + + if (! v) + break; + else if (o == v) + return true; + } + + return false; +} + +Value FunctionObject::construct(Managed *that, Value *, int) +{ + FunctionObject *f = static_cast<FunctionObject *>(that); + ExecutionEngine *v4 = f->engine(); + + Object *obj = v4->newObject(); + Value proto = f->get(v4->id_prototype); + if (proto.isObject()) + obj->prototype = proto.objectValue(); + return Value::fromObject(obj); +} + +Value FunctionObject::call(Managed *, const Value &, Value *, int) +{ + return Value::undefinedValue(); +} + +void FunctionObject::markObjects(Managed *that) +{ + FunctionObject *o = static_cast<FunctionObject *>(that); + if (o->name) + o->name->mark(); + // these are marked in VM::Function: +// for (uint i = 0; i < formalParameterCount; ++i) +// formalParameterList[i]->mark(); +// for (uint i = 0; i < varCount; ++i) +// varList[i]->mark(); + o->scope->mark(); + if (o->function) + o->function->mark(); + + Object::markObjects(that); +} + + +DEFINE_MANAGED_VTABLE(FunctionCtor); + +FunctionCtor::FunctionCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Function"))) +{ + vtbl = &static_vtbl; +} + +// 15.3.2 +Value FunctionCtor::construct(Managed *that, Value *args, int argc) +{ + FunctionCtor *f = static_cast<FunctionCtor *>(that); + MemoryManager::GCBlocker gcBlocker(f->engine()->memoryManager); + + ExecutionContext *ctx = f->engine()->current; + QString arguments; + QString body; + if (argc > 0) { + for (uint i = 0; i < argc - 1; ++i) { + if (i) + arguments += QLatin1String(", "); + arguments += args[i].toString(ctx)->toQString(); + } + body = args[argc - 1].toString(ctx)->toQString(); + } + + QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1String("}"); + + QQmlJS::Engine ee, *engine = ⅇ + QQmlJS::Lexer lexer(engine); + lexer.setCode(function, 1, false); + QQmlJS::Parser parser(engine); + + const bool parsed = parser.parseExpression(); + + if (!parsed) + f->engine()->current->throwSyntaxError(0); + + using namespace QQmlJS::AST; + FunctionExpression *fe = QQmlJS::AST::cast<FunctionExpression *>(parser.rootNode()); + ExecutionEngine *v4 = f->engine(); + if (!fe) + v4->current->throwSyntaxError(0); + + QQmlJS::V4IR::Module module; + + QQmlJS::Codegen cg(v4->current, f->strictMode); + QQmlJS::V4IR::Function *irf = cg(QString(), function, fe, &module); + + QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(v4, &module)); + QV4::Function *vmf = isel->vmFunction(irf); + + return Value::fromObject(v4->newScriptFunction(v4->rootContext, vmf)); +} + +// 15.3.1: This is equivalent to new Function(...) +Value FunctionCtor::call(Managed *that, const Value &, Value *args, int argc) +{ + return construct(that, args, argc); +} + +FunctionPrototype::FunctionPrototype(ExecutionContext *ctx) + : FunctionObject(ctx) +{ +} + +void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + + defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(0)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 2); + defineDefaultProperty(ctx, QStringLiteral("call"), method_call, 1); + defineDefaultProperty(ctx, QStringLiteral("bind"), method_bind, 1); + +} + +Value FunctionPrototype::method_toString(SimpleCallContext *ctx) +{ + FunctionObject *fun = ctx->thisObject.asFunctionObject(); + if (!fun) + ctx->throwTypeError(); + + return Value::fromString(ctx, QStringLiteral("function() { [code] }")); +} + +Value FunctionPrototype::method_apply(SimpleCallContext *ctx) +{ + Value thisArg = ctx->argument(0); + + Value arg = ctx->argument(1); + QVector<Value> args; + + if (Object *arr = arg.asObject()) { + quint32 len = arr->get(ctx->engine->id_length).toUInt32(); + for (quint32 i = 0; i < len; ++i) { + Value a = arr->getIndexed(i); + args.append(a); + } + } else if (!(arg.isUndefined() || arg.isNull())) { + ctx->throwTypeError(); + return Value::undefinedValue(); + } + + FunctionObject *o = ctx->thisObject.asFunctionObject(); + if (!o) + ctx->throwTypeError(); + + return o->call(thisArg, args.data(), args.size()); +} + +Value FunctionPrototype::method_call(SimpleCallContext *ctx) +{ + Value thisArg = ctx->argument(0); + + QVector<Value> args(ctx->argumentCount ? ctx->argumentCount - 1 : 0); + if (ctx->argumentCount) + qCopy(ctx->arguments + 1, + ctx->arguments + ctx->argumentCount, args.begin()); + + FunctionObject *o = ctx->thisObject.asFunctionObject(); + if (!o) + ctx->throwTypeError(); + + return o->call(thisArg, args.data(), args.size()); +} + +Value FunctionPrototype::method_bind(SimpleCallContext *ctx) +{ + FunctionObject *target = ctx->thisObject.asFunctionObject(); + if (!target) + ctx->throwTypeError(); + + Value boundThis = ctx->argument(0); + QVector<Value> boundArgs; + for (uint i = 1; i < ctx->argumentCount; ++i) + boundArgs += ctx->argument(i); + + + BoundFunction *f = ctx->engine->newBoundFunction(ctx->engine->rootContext, target, boundThis, boundArgs); + return Value::fromObject(f); +} + + +static Value throwTypeError(SimpleCallContext *ctx) +{ + ctx->throwTypeError(); + return Value::undefinedValue(); +} + +DEFINE_MANAGED_VTABLE(ScriptFunction); + +ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function) + : FunctionObject(scope, function->name) +{ + vtbl = &static_vtbl; + this->function = function; + assert(function); + assert(function->code); + + // global function + if (!scope) + return; + + MemoryManager::GCBlocker gcBlocker(scope->engine->memoryManager); + + needsActivation = function->needsActivation(); + usesArgumentsObject = function->usesArgumentsObject; + strictMode = function->isStrict; + formalParameterCount = function->formals.size(); + formalParameterList = function->formals.constData(); + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(formalParameterCount)); + + varCount = function->locals.size(); + varList = function->locals.constData(); + + Object *proto = scope->engine->newObject(); + proto->defineDefaultProperty(scope->engine->id_constructor, Value::fromObject(this)); + Property *pd = insertMember(scope->engine->id_prototype, Attr_NotEnumerable|Attr_NotConfigurable); + pd->value = Value::fromObject(proto); + + if (scope->strictMode) { + FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, throwTypeError); + Property pd = Property::fromAccessor(thrower, thrower); + __defineOwnProperty__(scope, QStringLiteral("caller"), pd, Attr_Accessor|Attr_NotEnumerable|Attr_NotConfigurable); + __defineOwnProperty__(scope, QStringLiteral("arguments"), pd, Attr_Accessor|Attr_NotEnumerable|Attr_NotConfigurable); + } +} + +Value ScriptFunction::construct(Managed *that, Value *args, int argc) +{ + ScriptFunction *f = static_cast<ScriptFunction *>(that); + assert(f->function->code); + ExecutionEngine *v4 = f->engine(); + Object *obj = v4->newObject(); + Value proto = f->get(v4->id_prototype); + if (proto.isObject()) + obj->prototype = proto.objectValue(); + + ExecutionContext *context = v4->current; + quintptr stackSpace[stackContextSize/sizeof(quintptr)]; + ExecutionContext *ctx = v4->newCallContext(stackSpace, f, Value::fromObject(obj), args, argc); + + Value result; + try { + result = f->function->code(ctx, f->function->codeData); + } catch (Exception &ex) { + ex.partiallyUnwindContext(context); + throw; + } + ctx->engine->popContext(); + + if (result.isObject()) + return result; + return Value::fromObject(obj); +} + +Value ScriptFunction::call(Managed *that, const Value &thisObject, Value *args, int argc) +{ + ScriptFunction *f = static_cast<ScriptFunction *>(that); + assert(f->function->code); + quintptr stackSpace[stackContextSize/sizeof(quintptr)]; + ExecutionContext *context = f->engine()->current; + ExecutionContext *ctx = f->engine()->newCallContext(stackSpace, f, thisObject, args, argc); + + if (!f->strictMode && !thisObject.isObject()) { + if (thisObject.isUndefined() || thisObject.isNull()) { + ctx->thisObject = Value::fromObject(f->engine()->globalObject); + } else { + ctx->thisObject = Value::fromObject(thisObject.toObject(context)); + } + } + + Value result; + try { + result = f->function->code(ctx, f->function->codeData); + } catch (Exception &ex) { + ex.partiallyUnwindContext(context); + throw; + } + ctx->engine->popContext(); + return result; +} + + + +DEFINE_MANAGED_VTABLE(BuiltinFunctionOld); + +BuiltinFunctionOld::BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *)) + : FunctionObject(scope, name) + , code(code) +{ + vtbl = &static_vtbl; + isBuiltinFunction = true; +} + +Value BuiltinFunctionOld::construct(Managed *f, Value *, int) +{ + f->engine()->current->throwTypeError(); + return Value::undefinedValue(); +} + +Value BuiltinFunctionOld::call(Managed *that, const Value &thisObject, Value *args, int argc) +{ + BuiltinFunctionOld *f = static_cast<BuiltinFunctionOld *>(that); + ExecutionEngine *v4 = f->engine(); + ExecutionContext *context = v4->current; + + SimpleCallContext ctx; + ctx.initSimpleCallContext(f->scope->engine); + ctx.strictMode = f->scope->strictMode; // ### needed? scope or parent context? + ctx.thisObject = thisObject; + ctx.arguments = args; + ctx.argumentCount = argc; + v4->pushContext(&ctx); + + if (!f->strictMode && !thisObject.isObject()) { + // Built-in functions allow for the this object to be null or undefined. This overrides + // the behaviour of changing thisObject to the global object if null/undefined and allows + // the built-in functions for example to throw a type error if null is passed. + if (!thisObject.isUndefined() && !thisObject.isNull()) + ctx.thisObject = Value::fromObject(thisObject.toObject(context)); + } + + Value result = Value::undefinedValue(); + try { + result = f->code(&ctx); + } catch (Exception &ex) { + ex.partiallyUnwindContext(context); + throw; + } + + context->engine->popContext(); + return result; +} + +Value IndexedBuiltinFunction::call(Managed *that, const Value &thisObject, Value *args, int argc) +{ + IndexedBuiltinFunction *f = static_cast<IndexedBuiltinFunction *>(that); + ExecutionEngine *v4 = f->engine(); + ExecutionContext *context = v4->current; + + SimpleCallContext ctx; + ctx.initSimpleCallContext(f->scope->engine); + ctx.strictMode = f->scope->strictMode; // ### needed? scope or parent context? + ctx.thisObject = thisObject; + ctx.arguments = args; + ctx.argumentCount = argc; + v4->pushContext(&ctx); + + if (!f->strictMode && !thisObject.isObject()) { + // Built-in functions allow for the this object to be null or undefined. This overrides + // the behaviour of changing thisObject to the global object if null/undefined and allows + // the built-in functions for example to throw a type error if null is passed. + if (!thisObject.isUndefined() && !thisObject.isNull()) + ctx.thisObject = Value::fromObject(thisObject.toObject(context)); + } + + Value result = Value::undefinedValue(); + try { + result = f->code(&ctx, f->index); + } catch (Exception &ex) { + ex.partiallyUnwindContext(context); + throw; + } + + context->engine->popContext(); + return result; +} + +DEFINE_MANAGED_VTABLE(IndexedBuiltinFunction); + +DEFINE_MANAGED_VTABLE(BoundFunction); + +BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs) + : FunctionObject(scope, 0) + , target(target) + , boundThis(boundThis) + , boundArgs(boundArgs) +{ + vtbl = &static_vtbl; + int len = target->get(scope->engine->id_length).toUInt32(); + len -= boundArgs.size(); + if (len < 0) + len = 0; + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(len)); + + FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, throwTypeError); + Property pd = Property::fromAccessor(thrower, thrower); + *insertMember(scope->engine->id_arguments, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd; + *insertMember(scope->engine->id_caller, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd; +} + +void BoundFunction::destroy(Managed *that) +{ + static_cast<BoundFunction *>(that)->~BoundFunction(); +} + +Value BoundFunction::call(Managed *that, const Value &, Value *args, int argc) +{ + BoundFunction *f = static_cast<BoundFunction *>(that); + Value *newArgs = static_cast<Value *>(alloca(sizeof(Value)*(f->boundArgs.size() + argc))); + memcpy(newArgs, f->boundArgs.constData(), f->boundArgs.size()*sizeof(Value)); + memcpy(newArgs + f->boundArgs.size(), args, argc*sizeof(Value)); + + return f->target->call(f->boundThis, newArgs, f->boundArgs.size() + argc); +} + +Value BoundFunction::construct(Managed *that, Value *args, int argc) +{ + BoundFunction *f = static_cast<BoundFunction *>(that); + Value *newArgs = static_cast<Value *>(alloca(sizeof(Value)*(f->boundArgs.size() + argc))); + memcpy(newArgs, f->boundArgs.constData(), f->boundArgs.size()*sizeof(Value)); + memcpy(newArgs + f->boundArgs.size(), args, argc*sizeof(Value)); + + return f->target->construct(newArgs, f->boundArgs.size() + argc); +} + +bool BoundFunction::hasInstance(Managed *that, const Value &value) +{ + BoundFunction *f = static_cast<BoundFunction *>(that); + return FunctionObject::hasInstance(f->target, value); +} + +void BoundFunction::markObjects(Managed *that) +{ + BoundFunction *o = static_cast<BoundFunction *>(that); + o->target->mark(); + if (Managed *m = o->boundThis.asManaged()) + m->mark(); + for (int i = 0; i < o->boundArgs.size(); ++i) + if (Managed *m = o->boundArgs.at(i).asManaged()) + m->mark(); + FunctionObject::markObjects(that); +} diff --git a/src/qml/qml/v4/qv4functionobject_p.h b/src/qml/qml/v4/qv4functionobject_p.h new file mode 100644 index 0000000000..39e07a2b8d --- /dev/null +++ b/src/qml/qml/v4/qv4functionobject_p.h @@ -0,0 +1,223 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4FUNCTIONOBJECT_H +#define QV4FUNCTIONOBJECT_H + +#include "qv4global_p.h" +#include "qv4runtime_p.h" +#include "qv4engine_p.h" +#include "qv4context_p.h" +#include "qv4object_p.h" +#include "qv4string_p.h" +#include "qv4codegen_p.h" +#include "qv4isel_p.h" +#include "qv4managed_p.h" +#include "qv4property_p.h" +#include "qv4objectiterator_p.h" + +#include <QtCore/QString> +#include <QtCore/QHash> +#include <QtCore/QScopedPointer> +#include <cstdio> +#include <cassert> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Function; +struct Object; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct ErrorObject; +struct ArgumentsObject; +struct ExecutionContext; +struct ExecutionEngine; +class MemoryManager; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; +struct InternalClass; +struct Lookup; + +struct Q_QML_EXPORT FunctionObject: Object { + // Used with Managed::subType + enum FunctionType { + RegularFunction = 0, + WrappedQtMethod = 1 + }; + + ExecutionContext *scope; + String *name; + String * const *formalParameterList; + String * const *varList; + unsigned int formalParameterCount; + unsigned int varCount; + Function *function; + + FunctionObject(ExecutionContext *scope, String *name = 0); + + Value newInstance(); + + static Value construct(Managed *that, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + inline Value construct(Value *args, int argc) { + return vtbl->construct(this, args, argc); + } + inline Value call(const Value &thisObject, Value *args, int argc) { + return vtbl->call(this, thisObject, args, argc); + } + +protected: + static const ManagedVTable static_vtbl; + static void markObjects(Managed *that); + static bool hasInstance(Managed *that, const Value &value); +}; + +struct FunctionCtor: FunctionObject +{ + FunctionCtor(ExecutionContext *scope); + + static Value construct(Managed *that, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct FunctionPrototype: FunctionObject +{ + FunctionPrototype(ExecutionContext *ctx); + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_toString(SimpleCallContext *ctx); + static Value method_apply(SimpleCallContext *ctx); + static Value method_call(SimpleCallContext *ctx); + static Value method_bind(SimpleCallContext *ctx); +}; + +struct BuiltinFunctionOld: FunctionObject { + Value (*code)(SimpleCallContext *); + + BuiltinFunctionOld(ExecutionContext *scope, String *name, Value (*code)(SimpleCallContext *)); + + static Value construct(Managed *, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct IndexedBuiltinFunction: FunctionObject +{ + Q_MANAGED + + Value (*code)(SimpleCallContext *ctx, uint index); + uint index; + + IndexedBuiltinFunction(ExecutionContext *scope, uint index, Value (*code)(SimpleCallContext *ctx, uint index)) + : FunctionObject(scope, /*name*/0) + , code(code) + , index(index) + { + vtbl = &static_vtbl; + isBuiltinFunction = true; + } + + static Value construct(Managed *m, Value *, int) + { + m->engine()->current->throwTypeError(); + return Value::undefinedValue(); + } + + static Value call(Managed *that, const Value &thisObject, Value *args, int argc); +}; + + +struct ScriptFunction: FunctionObject { + ScriptFunction(ExecutionContext *scope, Function *function); + + static Value construct(Managed *, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct BoundFunction: FunctionObject { + FunctionObject *target; + Value boundThis; + QVector<Value> boundArgs; + + BoundFunction(ExecutionContext *scope, FunctionObject *target, Value boundThis, const QVector<Value> &boundArgs); + ~BoundFunction() {} + + + static Value construct(Managed *, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + + static const ManagedVTable static_vtbl; + static void destroy(Managed *); + static void markObjects(Managed *that); + static bool hasInstance(Managed *that, const Value &value); +}; + +} + +QT_END_NAMESPACE + +#endif // QMLJS_OBJECTS_H diff --git a/src/qml/qml/v4/qv4global_p.h b/src/qml/qml/v4/qv4global_p.h new file mode 100644 index 0000000000..8e47f3c88a --- /dev/null +++ b/src/qml/qml/v4/qv4global_p.h @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4GLOBAL_H +#define QV4GLOBAL_H + +#include <QtCore/qglobal.h> + +#include <qtqmlglobal.h> + +#if defined(Q_CC_MSVC) +namespace std { + +inline bool isinf(double d) { return !_finite(d) && !_isnan(d); } +inline bool isnan(double d) { return !!_isnan(d); } +inline bool isfinite(double d) { return _finite(d); } +inline bool signbit(double d) { return _copysign(1.0, d) < 0; } + +} // namespace std + +inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); } +#endif + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +class MemoryManager; +struct String; +struct Object; +struct ObjectPrototype; +struct ObjectIterator; +struct ExecutionContext; +struct ScriptFunction; +struct InternalClass; +struct Property; + +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct ErrorObject; +struct ArgumentsObject; +struct Managed; +struct Lookup; +struct ExecutionEngine; +struct QObjectWrapper; + + +enum PropertyFlag { + Attr_Data = 0, + Attr_Accessor = 0x1, + Attr_NotWritable = 0x2, + Attr_NotEnumerable = 0x4, + Attr_NotConfigurable = 0x8, + Attr_ReadOnly = Attr_NotWritable|Attr_NotEnumerable|Attr_NotConfigurable, + Attr_Invalid = 0xff +}; + +Q_DECLARE_FLAGS(PropertyFlags, PropertyFlag); +Q_DECLARE_OPERATORS_FOR_FLAGS(PropertyFlags); + +struct PropertyAttributes +{ + union { + uchar m_all; + struct { + uchar m_flags : 4; + uchar m_mask : 4; + }; + struct { + uchar m_type : 1; + uchar m_writable : 1; + uchar m_enumerable : 1; + uchar m_configurable : 1; + uchar type_set : 1; + uchar writable_set : 1; + uchar enumerable_set : 1; + uchar configurable_set : 1; + }; + }; + + enum Type { + Data = 0, + Accessor = 1, + Generic = 2 + }; + + PropertyAttributes() : m_all(0) {} + PropertyAttributes(PropertyFlag f) : m_all(0) { + if (f != Attr_Invalid) { + setType(f & Attr_Accessor ? Accessor : Data); + if (!(f & Attr_Accessor)) + setWritable(!(f & Attr_NotWritable)); + setEnumerable(!(f & Attr_NotEnumerable)); + setConfigurable(!(f & Attr_NotConfigurable)); + } + } + PropertyAttributes(PropertyFlags f) : m_all(0) { + if (f != Attr_Invalid) { + setType(f & Attr_Accessor ? Accessor : Data); + if (!(f & Attr_Accessor)) + setWritable(!(f & Attr_NotWritable)); + setEnumerable(!(f & Attr_NotEnumerable)); + setConfigurable(!(f & Attr_NotConfigurable)); + } + } + PropertyAttributes(const PropertyAttributes &other) : m_all(other.m_all) {} + PropertyAttributes & operator=(const PropertyAttributes &other) { m_all = other.m_all; return *this; } + + void setType(Type t) { m_type = t; type_set = true; } + Type type() const { return type_set ? (Type)m_type : Generic; } + + bool isData() const { return type() == PropertyAttributes::Data || writable_set; } + bool isAccessor() const { return type() == PropertyAttributes::Accessor; } + bool isGeneric() const { return type() == PropertyAttributes::Generic && !writable_set; } + + bool hasType() const { return type_set; } + bool hasWritable() const { return writable_set; } + bool hasConfigurable() const { return configurable_set; } + bool hasEnumerable() const { return enumerable_set; } + + void setWritable(bool b) { m_writable = b; writable_set = true; } + void setConfigurable(bool b) { m_configurable = b; configurable_set = true; } + void setEnumerable(bool b) { m_enumerable = b; enumerable_set = true; } + + void resolve() { m_mask = 0xf; if (m_type == Accessor) { m_writable = false; writable_set = false; } } + + bool isWritable() const { return m_type != Data || m_writable; } + bool isEnumerable() const { return m_enumerable; } + bool isConfigurable() const { return m_configurable; } + + void clearType() { m_type = Data; type_set = false; } + void clearWritable() { m_writable = false; writable_set = false; } + void clearEnumerable() { m_enumerable = false; enumerable_set = false; } + void clearConfigurable() { m_configurable = false; configurable_set = false; } + + void clear() { m_all = 0; } + bool isEmpty() const { return !m_all; } + + uint flags() const { return m_flags; } + + bool operator==(PropertyAttributes other) { + return m_all == other.m_all; + } + bool operator!=(PropertyAttributes other) { + return m_all != other.m_all; + } +}; + +} + +Q_DECLARE_TYPEINFO(QV4::PropertyAttributes, Q_PRIMITIVE_TYPE); + +QT_END_NAMESPACE + +#endif // QV4GLOBAL_H diff --git a/src/qml/qml/v4/qv4globalobject.cpp b/src/qml/qml/v4/qv4globalobject.cpp new file mode 100644 index 0000000000..6b279416a3 --- /dev/null +++ b/src/qml/qml/v4/qv4globalobject.cpp @@ -0,0 +1,652 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4globalobject_p.h" +#include "qv4mm_p.h" +#include "qv4value_p.h" +#include "qv4context_p.h" +#include "qv4function_p.h" +#include "qv4debugging_p.h" +#include "qv4script_p.h" +#include "qv4exception_p.h" + +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsast_p.h> +#include <qv4jsir_p.h> +#include <qv4codegen_p.h> +#include "private/qlocale_tools_p.h" + +#include <QtCore/QDebug> +#include <QtCore/QString> +#include <iostream> +#include "qv4alloca_p.h" + +#include <wtf/MathExtras.h> + +using namespace QV4; + + +static inline char toHex(char c) +{ + static const char hexnumbers[] = "0123456789ABCDEF"; + return hexnumbers[c & 0xf]; +} + +static int fromHex(QChar ch) +{ + ushort c = ch.unicode(); + if ((c >= '0') && (c <= '9')) + return c - '0'; + if ((c >= 'A') && (c <= 'F')) + return c - 'A' + 10; + if ((c >= 'a') && (c <= 'f')) + return c - 'a' + 10; + return -1; +} + +static QString escape(const QString &input) +{ + QString output; + output.reserve(input.size() * 3); + const int length = input.length(); + for (int i = 0; i < length; ++i) { + ushort uc = input.at(i).unicode(); + if (uc < 0x100) { + if ( (uc > 0x60 && uc < 0x7B) + || (uc > 0x3F && uc < 0x5B) + || (uc > 0x2C && uc < 0x3A) + || (uc == 0x2A) + || (uc == 0x2B) + || (uc == 0x5F)) { + output.append(QChar(uc)); + } else { + output.append('%'); + output.append(QChar(toHex(uc >> 4))); + output.append(QChar(toHex(uc))); + } + } else { + output.append('%'); + output.append('u'); + output.append(QChar(toHex(uc >> 12))); + output.append(QChar(toHex(uc >> 8))); + output.append(QChar(toHex(uc >> 4))); + output.append(QChar(toHex(uc))); + } + } + return output; +} + +static QString unescape(const QString &input) +{ + QString result; + result.reserve(input.length()); + int i = 0; + const int length = input.length(); + while (i < length) { + QChar c = input.at(i++); + if ((c == '%') && (i + 1 < length)) { + QChar a = input.at(i); + if ((a == 'u') && (i + 4 < length)) { + int d3 = fromHex(input.at(i+1)); + int d2 = fromHex(input.at(i+2)); + int d1 = fromHex(input.at(i+3)); + int d0 = fromHex(input.at(i+4)); + if ((d3 != -1) && (d2 != -1) && (d1 != -1) && (d0 != -1)) { + ushort uc = ushort((d3 << 12) | (d2 << 8) | (d1 << 4) | d0); + result.append(QChar(uc)); + i += 5; + } else { + result.append(c); + } + } else { + int d1 = fromHex(a); + int d0 = fromHex(input.at(i+1)); + if ((d1 != -1) && (d0 != -1)) { + c = (d1 << 4) | d0; + i += 2; + } + result.append(c); + } + } else { + result.append(c); + } + } + return result; +} + +static const char uriReserved[] = ";/?:@&=+$,#"; +static const char uriUnescaped[] = "-_.!~*'()"; +static const char uriUnescapedReserved[] = "-_.!~*'();/?:@&=+$,#"; + +static void addEscapeSequence(QString &output, uchar ch) +{ + output.append(QLatin1Char('%')); + output.append(QLatin1Char(toHex(ch >> 4))); + output.append(QLatin1Char(toHex(ch & 0xf))); +} + +static QString encode(const QString &input, const char *unescapedSet, bool *ok) +{ + *ok = true; + QString output; + const int length = input.length(); + int i = 0; + while (i < length) { + const QChar c = input.at(i); + bool escape = true; + if ((c.unicode() >= 'a' && c.unicode() <= 'z') || + (c.unicode() >= 'A' && c.unicode() <= 'Z') || + (c.unicode() >= '0' && c.unicode() <= '9')) { + escape = false; + } else { + const char *r = unescapedSet; + while (*r) { + if (*r == c.unicode()) { + escape = false; + break; + } + ++r; + } + } + if (escape) { + uint uc = c.unicode(); + if ((uc >= 0xDC00) && (uc <= 0xDFFF)) { + *ok = false; + break; + } + if (!((uc < 0xD800) || (uc > 0xDBFF))) { + ++i; + if (i == length) { + *ok = false; + break; + } + const uint uc2 = input.at(i).unicode(); + if ((uc2 < 0xDC00) || (uc2 > 0xDFFF)) { + *ok = false; + break; + } + uc = ((uc - 0xD800) * 0x400) + (uc2 - 0xDC00) + 0x10000; + } + if (uc < 0x80) { + addEscapeSequence(output, (uchar)uc); + } else { + if (uc < 0x0800) { + addEscapeSequence(output, 0xc0 | ((uchar) (uc >> 6))); + } else { + + if (QChar::requiresSurrogates(uc)) { + addEscapeSequence(output, 0xf0 | ((uchar) (uc >> 18))); + addEscapeSequence(output, 0x80 | (((uchar) (uc >> 12)) & 0x3f)); + } else { + addEscapeSequence(output, 0xe0 | (((uchar) (uc >> 12)) & 0x3f)); + } + addEscapeSequence(output, 0x80 | (((uchar) (uc >> 6)) & 0x3f)); + } + addEscapeSequence(output, 0x80 | ((uchar) (uc&0x3f))); + } + } else { + output.append(c); + } + ++i; + } + if (i != length) + *ok = false; + return output; +} + +enum DecodeMode { + DecodeAll, + DecodeNonReserved +}; + +static QString decode(const QString &input, DecodeMode decodeMode, bool *ok) +{ + *ok = true; + QString output; + output.reserve(input.length()); + const int length = input.length(); + int i = 0; + const QChar percent = QLatin1Char('%'); + while (i < length) { + const QChar ch = input.at(i); + if (ch == percent) { + int start = i; + if (i + 2 >= length) + goto error; + + int d1 = fromHex(input.at(i+1)); + int d0 = fromHex(input.at(i+2)); + if ((d1 == -1) || (d0 == -1)) + goto error; + + int b = (d1 << 4) | d0; + i += 2; + if (b & 0x80) { + int uc; + int min_uc; + int need; + if ((b & 0xe0) == 0xc0) { + uc = b & 0x1f; + need = 1; + min_uc = 0x80; + } else if ((b & 0xf0) == 0xe0) { + uc = b & 0x0f; + need = 2; + min_uc = 0x800; + } else if ((b & 0xf8) == 0xf0) { + uc = b & 0x07; + need = 3; + min_uc = 0x10000; + } else { + goto error; + } + + if (i + (3 * need) >= length) + goto error; + + for (int j = 0; j < need; ++j) { + ++i; + if (input.at(i) != percent) + goto error; + + d1 = fromHex(input.at(i+1)); + d0 = fromHex(input.at(i+2)); + if ((d1 == -1) || (d0 == -1)) + goto error; + + b = (d1 << 4) | d0; + if ((b & 0xC0) != 0x80) + goto error; + + i += 2; + uc = (uc << 6) + (b & 0x3f); + } + if (uc < min_uc) + goto error; + + if (uc < 0x10000) { + output.append(QChar(uc)); + } else { + if (uc > 0x10FFFF) + goto error; + + ushort l = ushort(((uc - 0x10000) & 0x3FF) + 0xDC00); + ushort h = ushort((((uc - 0x10000) >> 10) & 0x3FF) + 0xD800); + output.append(QChar(h)); + output.append(QChar(l)); + } + } else { + if (decodeMode == DecodeNonReserved && b <= 0x40) { + const char *r = uriReserved; + while (*r) { + if (*r == b) + break; + ++r; + } + if (*r) + output.append(input.mid(start, i - start + 1)); + else + output.append(QChar(b)); + } else { + output.append(QChar(b)); + } + } + } else { + output.append(ch); + } + ++i; + } + if (i != length) + *ok = false; + return output; + error: + *ok = false; + return QString(); +} + +DEFINE_MANAGED_VTABLE(EvalFunction); + +EvalFunction::EvalFunction(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->id_eval) +{ + vtbl = &static_vtbl; + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); +} + +Value EvalFunction::evalCall(Value /*thisObject*/, Value *args, int argc, bool directCall) +{ + if (argc < 1) + return Value::undefinedValue(); + + ExecutionContext *parentContext = engine()->current; + ExecutionEngine *engine = parentContext->engine; + ExecutionContext *ctx = parentContext; + + if (!directCall) { + // the context for eval should be the global scope, so we fake a root + // context + ctx = engine->pushGlobalContext(); + } + + if (!args[0].isString()) + return args[0]; + + const QString code = args[0].stringValue()->toQString(); + bool inheritContext = !ctx->strictMode; + + Script script(ctx, code, QString("eval code")); + script.strictMode = (directCall && parentContext->strictMode); + script.inheritContext = inheritContext; + script.parse(); + + Function *function = script.function(); + if (!function) + return Value::undefinedValue(); + + strictMode = function->isStrict || (ctx->strictMode); + + usesArgumentsObject = function->usesArgumentsObject; + needsActivation = function->needsActivation(); + + if (strictMode) { + FunctionObject *e = engine->newScriptFunction(ctx, function); + return e->call(ctx->thisObject, 0, 0); + } + + ExecutionContext::EvalCode evalCode; + evalCode.function = function; + evalCode.next = ctx->currentEvalCode; + ctx->currentEvalCode = &evalCode; + + // set the correct strict mode flag on the context + bool cstrict = ctx->strictMode; + ctx->strictMode = strictMode; + + Value result = Value::undefinedValue(); + try { + result = function->code(ctx, function->codeData); + } catch (Exception &ex) { + ctx->strictMode = cstrict; + ctx->currentEvalCode = evalCode.next; + if (strictMode) + ex.partiallyUnwindContext(parentContext); + throw; + } + + ctx->strictMode = cstrict; + ctx->currentEvalCode = evalCode.next; + + while (engine->current != parentContext) + engine->popContext(); + + return result; +} + + +Value EvalFunction::call(Managed *that, const Value &thisObject, Value *args, int argc) +{ + // indirect call + return static_cast<EvalFunction *>(that)->evalCall(thisObject, args, argc, false); +} + + +static inline int toInt(const QChar &qc, int R) +{ + ushort c = qc.unicode(); + int v = -1; + if (c >= '0' && c <= '9') + v = c - '0'; + else if (c >= 'A' && c <= 'Z') + v = c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + v = c - 'a' + 10; + if (v >= 0 && v < R) + return v; + else + return -1; +} + +// parseInt [15.1.2.2] +Value GlobalFunctions::method_parseInt(SimpleCallContext *context) +{ + Value string = context->argument(0); + Value radix = context->argument(1); + int R = radix.isUndefined() ? 0 : radix.toInt32(); + + // [15.1.2.2] step by step: + String *inputString = string.toString(context); // 1 + QString trimmed = inputString->toQString().trimmed(); // 2 + const QChar *pos = trimmed.constData(); + const QChar *end = pos + trimmed.length(); + + int sign = 1; // 3 + if (pos != end) { + if (*pos == QLatin1Char('-')) + sign = -1; // 4 + if (*pos == QLatin1Char('-') || *pos == QLatin1Char('+')) + ++pos; // 5 + } + bool stripPrefix = true; // 7 + if (R) { // 8 + if (R < 2 || R > 36) + return Value::fromDouble(std::numeric_limits<double>::quiet_NaN()); // 8a + if (R != 16) + stripPrefix = false; // 8b + } else { // 9 + R = 10; // 9a + } + if (stripPrefix) { // 10 + if ((end - pos >= 2) + && (pos[0] == QLatin1Char('0')) + && (pos[1] == QLatin1Char('x') || pos[1] == QLatin1Char('X'))) { // 10a + pos += 2; + R = 16; + } + } + // 11: Z is progressively built below + // 13: this is handled by the toInt function + if (pos == end) // 12 + return Value::fromDouble(std::numeric_limits<double>::quiet_NaN()); + bool overflow = false; + qint64 v_overflow; + unsigned overflow_digit_count = 0; + int d = toInt(*pos++, R); + if (d == -1) + return Value::fromDouble(std::numeric_limits<double>::quiet_NaN()); + qint64 v = d; + while (pos != end) { + d = toInt(*pos++, R); + if (d == -1) + break; + if (overflow) { + if (overflow_digit_count == 0) { + v_overflow = v; + v = 0; + } + ++overflow_digit_count; + v = v * R + d; + } else { + qint64 vNew = v * R + d; + if (vNew < v) { + overflow = true; + --pos; + } else { + v = vNew; + } + } + } + + if (overflow) { + double result = (double) v_overflow * pow(R, overflow_digit_count); + result += v; + return Value::fromDouble(sign * result); + } else { + return Value::fromDouble(sign * (double) v); // 15 + } +} + +// parseFloat [15.1.2.3] +Value GlobalFunctions::method_parseFloat(SimpleCallContext *context) +{ + Value string = context->argument(0); + + // [15.1.2.3] step by step: + String *inputString = string.toString(context); // 1 + QString trimmed = inputString->toQString().trimmed(); // 2 + + // 4: + if (trimmed.startsWith(QLatin1String("Infinity")) + || trimmed.startsWith(QLatin1String("+Infinity"))) + return Value::fromDouble(Q_INFINITY); + if (trimmed.startsWith("-Infinity")) + return Value::fromDouble(-Q_INFINITY); + QByteArray ba = trimmed.toLatin1(); + bool ok; + const char *begin = ba.constData(); + const char *end = 0; + double d = qstrtod(begin, &end, &ok); + if (end - begin == 0) + return Value::fromDouble(std::numeric_limits<double>::quiet_NaN()); // 3 + else + return Value::fromDouble(d); +} + +/// isNaN [15.1.2.4] +Value GlobalFunctions::method_isNaN(SimpleCallContext *context) +{ + const Value &v = context->argument(0); + if (v.integerCompatible()) + return Value::fromBoolean(false); + + double d = v.toNumber(); + return Value::fromBoolean(std::isnan(d)); +} + +/// isFinite [15.1.2.5] +Value GlobalFunctions::method_isFinite(SimpleCallContext *context) +{ + const Value &v = context->argument(0); + if (v.integerCompatible()) + return Value::fromBoolean(true); + + double d = v.toNumber(); + return Value::fromBoolean(std::isfinite(d)); +} + +/// decodeURI [15.1.3.1] +Value GlobalFunctions::method_decodeURI(SimpleCallContext *context) +{ + if (context->argumentCount == 0) + return Value::undefinedValue(); + + QString uriString = context->arguments[0].toString(context)->toQString(); + bool ok; + QString out = decode(uriString, DecodeNonReserved, &ok); + if (!ok) + context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(context, out); +} + +/// decodeURIComponent [15.1.3.2] +Value GlobalFunctions::method_decodeURIComponent(SimpleCallContext *context) +{ + if (context->argumentCount == 0) + return Value::undefinedValue(); + + QString uriString = context->arguments[0].toString(context)->toQString(); + bool ok; + QString out = decode(uriString, DecodeAll, &ok); + if (!ok) + context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(context, out); +} + +/// encodeURI [15.1.3.3] +Value GlobalFunctions::method_encodeURI(SimpleCallContext *context) +{ + if (context->argumentCount == 0) + return Value::undefinedValue(); + + QString uriString = context->arguments[0].toString(context)->toQString(); + bool ok; + QString out = encode(uriString, uriUnescapedReserved, &ok); + if (!ok) + context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(context, out); +} + +/// encodeURIComponent [15.1.3.4] +Value GlobalFunctions::method_encodeURIComponent(SimpleCallContext *context) +{ + if (context->argumentCount == 0) + return Value::undefinedValue(); + + QString uriString = context->arguments[0].toString(context)->toQString(); + bool ok; + QString out = encode(uriString, uriUnescaped, &ok); + if (!ok) + context->throwURIError(Value::fromString(context, QStringLiteral("malformed URI sequence"))); + + return Value::fromString(context, out); +} + +Value GlobalFunctions::method_escape(SimpleCallContext *context) +{ + if (!context->argumentCount) + return Value::fromString(context, QStringLiteral("undefined")); + + QString str = context->argument(0).toString(context)->toQString(); + return Value::fromString(context, escape(str)); +} + +Value GlobalFunctions::method_unescape(SimpleCallContext *context) +{ + if (!context->argumentCount) + return Value::fromString(context, QStringLiteral("undefined")); + + QString str = context->argument(0).toString(context)->toQString(); + return Value::fromString(context, unescape(str)); +} diff --git a/src/qml/qml/v4/qv4globalobject_p.h b/src/qml/qml/v4/qv4globalobject_p.h new file mode 100644 index 0000000000..11d034b5b4 --- /dev/null +++ b/src/qml/qml/v4/qv4globalobject_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4GLOBALOBJECT_H +#define QV4GLOBALOBJECT_H + +#include "qv4global_p.h" +#include "qv4functionobject_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Q_QML_EXPORT EvalFunction : FunctionObject +{ + EvalFunction(ExecutionContext *scope); + + Value evalCall(Value thisObject, Value *args, int argc, bool directCall); + + using Managed::construct; + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct GlobalFunctions +{ + static Value method_parseInt(SimpleCallContext *context); + static Value method_parseFloat(SimpleCallContext *context); + static Value method_isNaN(SimpleCallContext *context); + static Value method_isFinite(SimpleCallContext *context); + static Value method_decodeURI(SimpleCallContext *context); + static Value method_decodeURIComponent(SimpleCallContext *context); + static Value method_encodeURI(SimpleCallContext *context); + static Value method_encodeURIComponent(SimpleCallContext *context); + static Value method_escape(SimpleCallContext *context); + static Value method_unescape(SimpleCallContext *context); +}; + +} + +QT_END_NAMESPACE + +#endif // QMLJS_OBJECTS_H diff --git a/src/qml/qml/v4/qv4identifier.cpp b/src/qml/qml/v4/qv4identifier.cpp new file mode 100644 index 0000000000..5d8077bfdc --- /dev/null +++ b/src/qml/qml/v4/qv4identifier.cpp @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qv4identifier_p.h" +#include "qv4identifiertable_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +static const uchar prime_deltas[] = { + 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, + 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 +}; + +static inline int primeForNumBits(int numBits) +{ + return (1 << numBits) + prime_deltas[numBits]; +} + + +IdentifierHashData::IdentifierHashData(int numBits) + : numBits(numBits) + , size(0) +{ + refCount.store(1); + alloc = primeForNumBits(numBits); + entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); + memset(entries, 0, alloc*sizeof(IdentifierHashEntry)); +} + +IdentifierHashBase::IdentifierHashBase(ExecutionEngine *engine) +{ + d = new IdentifierHashData(3); + d->identifierTable = engine->identifierTable; +} + + +IdentifierHashEntry *IdentifierHashBase::addEntry(const Identifier *identifier) +{ + // fill up to max 50% + bool grow = (d->alloc <= d->size*2); + + if (grow) { + ++d->numBits; + int newAlloc = primeForNumBits(d->numBits); + IdentifierHashEntry *newEntries = (IdentifierHashEntry *)malloc(newAlloc * sizeof(IdentifierHashEntry)); + memset(newEntries, 0, newAlloc*sizeof(IdentifierHashEntry)); + for (uint i = 0; i < d->alloc; ++i) { + const IdentifierHashEntry &e = d->entries[i]; + if (!e.identifier) + continue; + uint idx = e.identifier->hashValue % newAlloc; + while (newEntries[idx].identifier) { + ++idx; + idx %= newAlloc; + } + newEntries[idx] = e; + } + free(d->entries); + d->entries = newEntries; + d->alloc = newAlloc; + } + + uint idx = identifier->hashValue % d->alloc; + while (d->entries[idx].identifier) { + Q_ASSERT(d->entries[idx].identifier != identifier); + ++idx; + idx %= d->alloc; + } + d->entries[idx].identifier = identifier; + ++d->size; + return d->entries + idx; +} + +const IdentifierHashEntry *IdentifierHashBase::lookup(const Identifier *identifier) const +{ + if (!d) + return 0; + assert(d->entries); + + uint idx = identifier->hashValue % d->alloc; + while (1) { + if (!d->entries[idx].identifier) + return 0; + if (d->entries[idx].identifier == identifier) + return d->entries + idx; + ++idx; + idx %= d->alloc; + } +} + +const IdentifierHashEntry *IdentifierHashBase::lookup(const QString &str) const +{ + if (!d) + return 0; + assert(d->entries); + + uint hash = String::createHashValue(str.constData(), str.length()); + uint idx = hash % d->alloc; + while (1) { + if (!d->entries[idx].identifier) + return 0; + if (d->entries[idx].identifier->string == str) + return d->entries + idx; + ++idx; + idx %= d->alloc; + } +} + +const IdentifierHashEntry *IdentifierHashBase::lookup(String *str) const +{ + if (!d) + return 0; + if (str->identifier) + return lookup(str->identifier); + return lookup(str->toQString()); +} + +const Identifier *IdentifierHashBase::toIdentifier(const QString &str) const +{ + Q_ASSERT(d); + return d->identifierTable->identifier(str); +} + +const Identifier *IdentifierHashBase::toIdentifier(String *str) const +{ + Q_ASSERT(d); + return d->identifierTable->identifier(str); +} + + +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4identifier_p.h b/src/qml/qml/v4/qv4identifier_p.h new file mode 100644 index 0000000000..7c69e1d8c4 --- /dev/null +++ b/src/qml/qml/v4/qv4identifier_p.h @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4IDENTIFIER_H +#define QV4IDENTIFIER_H + +#include <qstring.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct String; +struct IdentifierTable; +struct ExecutionEngine; + +struct Identifier +{ + QString string; + uint hashValue; +}; + + +struct IdentifierHashEntry { + const Identifier *identifier; + union { + int value; + void *pointer; + }; + int get(int *) const { return this ? value : -1; } + bool get(bool *) const { return this != 0; } + void *get(void **) const { return this ? pointer : 0; } +}; + +struct IdentifierHashData +{ + IdentifierHashData(int numBits); + ~IdentifierHashData() { + free(entries); + } + + QBasicAtomicInt refCount; + int alloc; + int size; + int numBits; + IdentifierTable *identifierTable; + IdentifierHashEntry *entries; +}; + +struct IdentifierHashBase +{ + + IdentifierHashData *d; + + IdentifierHashBase() : d(0) {} + IdentifierHashBase(ExecutionEngine *engine); + inline IdentifierHashBase(const IdentifierHashBase &other); + inline ~IdentifierHashBase(); + inline IdentifierHashBase &operator=(const IdentifierHashBase &other); + + bool isEmpty() const { return !d; } + // ### + void reserve(int) {} + + inline int count() const; + bool contains(const Identifier *i) const; + bool contains(const QString &str) const; + bool contains(String *str) const; + +protected: + IdentifierHashEntry *addEntry(const Identifier *i); + const IdentifierHashEntry *lookup(const Identifier *identifier) const; + const IdentifierHashEntry *lookup(const QString &str) const; + const IdentifierHashEntry *lookup(String *str) const; + const Identifier *toIdentifier(const QString &str) const; + const Identifier *toIdentifier(String *str) const; +}; + + +template<typename T> +struct IdentifierHash : public IdentifierHashBase +{ + IdentifierHash() + : IdentifierHashBase() {} + IdentifierHash(ExecutionEngine *engine) + : IdentifierHashBase(engine) {} + inline IdentifierHash(const IdentifierHash<T> &other) + : IdentifierHashBase(other) {} + inline ~IdentifierHash() {} + inline IdentifierHash &operator=(const IdentifierHash<T> &other) { + IdentifierHashBase::operator =(other); + return *this; + } + + void add(const QString &str, const T &value); + + inline T value(const QString &str) const; + inline T value(String *str) const; + QString findId(T value) const; +}; + +inline IdentifierHashBase::IdentifierHashBase(const IdentifierHashBase &other) +{ + d = other.d; + if (d) + d->refCount.ref(); +} + +inline IdentifierHashBase::~IdentifierHashBase() +{ + if (d && !d->refCount.deref()) + delete d; +} + +IdentifierHashBase &IdentifierHashBase::operator=(const IdentifierHashBase &other) +{ + if (other.d) + other.d->refCount.ref(); + if (d && !d->refCount.deref()) + delete d; + d = other.d; + return *this; +} + +inline int IdentifierHashBase::count() const +{ + return d ? d->size : 0; +} + +inline bool IdentifierHashBase::contains(const Identifier *i) const +{ + return lookup(i) != 0; +} + +inline bool IdentifierHashBase::contains(const QString &str) const +{ + return lookup(str) != 0; +} + +inline bool IdentifierHashBase::contains(String *str) const +{ + return lookup(str) != 0; +} + +template<typename T> +void IdentifierHash<T>::add(const QString &str, const T &value) +{ + IdentifierHashEntry *e = addEntry(toIdentifier(str)); + e->value = value; +} + +template<typename T> +inline T IdentifierHash<T>::value(const QString &str) const +{ + return lookup(str)->get((T*)0); +} + +template<typename T> +inline T IdentifierHash<T>::value(String *str) const +{ + return lookup(str)->get((T*)0); +} + + +template<typename T> +QString IdentifierHash<T>::findId(T value) const +{ + IdentifierHashEntry *e = d->entries; + IdentifierHashEntry *end = e + d->alloc; + while (e < end) { + if (e->identifier && e->get((T*)0) == value) + return e->identifier->string; + ++e; + } + return QString(); +} + + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4/qv4identifiertable.cpp b/src/qml/qml/v4/qv4identifiertable.cpp new file mode 100644 index 0000000000..5de2f893ef --- /dev/null +++ b/src/qml/qml/v4/qv4identifiertable.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qv4identifiertable_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +static const uchar prime_deltas[] = { + 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, + 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 +}; + +static inline int primeForNumBits(int numBits) +{ + return (1 << numBits) + prime_deltas[numBits]; +} + + +IdentifierTable::IdentifierTable(ExecutionEngine *engine) + : engine(engine) + , size(0) + , numBits(8) +{ + alloc = primeForNumBits(numBits); + entries = (String **)malloc(alloc*sizeof(String *)); + memset(entries, 0, alloc*sizeof(String *)); +} + +IdentifierTable::~IdentifierTable() +{ + free(entries); +} + +void IdentifierTable::addEntry(String *str) +{ + uint hash = str->hashValue(); + + if (str->subtype == String::StringType_ArrayIndex) + return; + + str->identifier = new Identifier; + str->identifier->string = str->toQString(); + str->identifier->hashValue = hash; + + bool grow = (alloc <= size*2); + + if (grow) { + ++numBits; + int newAlloc = primeForNumBits(numBits); + String **newEntries = (String **)malloc(newAlloc*sizeof(String *)); + memset(newEntries, 0, newAlloc*sizeof(String *)); + for (uint i = 0; i < alloc; ++i) { + String *e = entries[i]; + if (!e) + continue; + uint idx = e->stringHash % newAlloc; + while (newEntries[idx]) { + ++idx; + idx %= newAlloc; + } + newEntries[idx] = e; + } + free(entries); + entries = newEntries; + alloc = newAlloc; + } + + uint idx = hash % alloc; + while (entries[idx]) { + ++idx; + idx %= alloc; + } + entries[idx] = str; + ++size; +} + + + +String *IdentifierTable::insertString(const QString &s) +{ + uint hash = String::createHashValue(s.constData(), s.length()); + uint idx = hash % alloc; + while (String *e = entries[idx]) { + if (e->stringHash == hash && e->toQString() == s) + return e; + ++idx; + idx %= alloc; + } + + String *str = engine->newString(s); + addEntry(str); + return str; +} + + +Identifier *IdentifierTable::identifier(String *str) +{ + if (str->identifier) + return str->identifier; + uint hash = str->hashValue(); + if (str->subtype == String::StringType_ArrayIndex) + return 0; + + uint idx = hash % alloc; + while (String *e = entries[idx]) { + if (e->stringHash == hash && e->isEqualTo(str)) { + str->identifier = e->identifier; + return e->identifier; + } + ++idx; + idx %= alloc; + } + + addEntry(str); + return str->identifier; +} + +Identifier *IdentifierTable::identifier(const QString &s) +{ + return insertString(s)->identifier; +} + +Identifier *IdentifierTable::identifier(const char *s, int len) +{ + uint hash = String::createHashValue(s, len); + if (hash == UINT_MAX) + return identifier(QString::fromUtf8(s, len)); + + QLatin1String latin(s, len); + uint idx = hash % alloc; + while (String *e = entries[idx]) { + if (e->stringHash == hash && e->toQString() == latin) + return e->identifier; + ++idx; + idx %= alloc; + } + + String *str = engine->newString(QString::fromLatin1(s, len)); + addEntry(str); + return str->identifier; +} + +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4identifiertable_p.h b/src/qml/qml/v4/qv4identifiertable_p.h new file mode 100644 index 0000000000..0f9a5921f9 --- /dev/null +++ b/src/qml/qml/v4/qv4identifiertable_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4IDENTIFIERTABLE_H +#define QV4IDENTIFIERTABLE_H + +#include "qv4identifier_p.h" +#include "qv4string_p.h" +#include "qv4engine_p.h" +#include <limits.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct IdentifierTable +{ + ExecutionEngine *engine; + + int alloc; + int size; + int numBits; + String **entries; + + void addEntry(String *str); + +public: + + IdentifierTable(ExecutionEngine *engine); + ~IdentifierTable(); + + String *insertString(const QString &s); + + Identifier *identifier(String *str); + Identifier *identifier(const QString &s); + Identifier *identifier(const char *s, int len); + + void mark() { + for (int i = 0; i < alloc; ++i) + if (entries[i]) + entries[i]->mark(); + } +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4/qv4include.cpp b/src/qml/qml/v4/qv4include.cpp new file mode 100644 index 0000000000..4fd7bb14c7 --- /dev/null +++ b/src/qml/qml/v4/qv4include.cpp @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4include_p.h" + +#include <QtQml/qjsengine.h> +#include <QtNetwork/qnetworkrequest.h> +#include <QtNetwork/qnetworkreply.h> +#include <QtCore/qfile.h> +#include <QtQml/qqmlfile.h> + +#include <private/qqmlengine_p.h> +#include <private/qv4engine_p.h> +#include <private/qv4functionobject_p.h> +#include <private/qv4script_p.h> +#include <private/qv4context_p.h> +#include <private/qv4exception_p.h> +#include <private/qqmlcontextwrapper_p.h> + +QT_BEGIN_NAMESPACE + +QV4Include::QV4Include(const QUrl &url, QV8Engine *engine, QQmlContextData *context, + const QV4::Value &qmlglobal, const QV4::Value &callback) + : v4(QV8Engine::getV4(engine)), m_network(0), m_reply(0), m_url(url), m_redirectCount(0), m_context(context) +{ + m_qmlglobal = qmlglobal; + if (callback.asFunctionObject()) + m_callbackFunction = callback; + + m_resultObject = resultValue(v4); + + m_network = engine->networkAccessManager(); + + QNetworkRequest request; + request.setUrl(url); + + m_reply = m_network->get(request); + QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); +} + +QV4Include::~QV4Include() +{ + delete m_reply; m_reply = 0; +} + +QV4::Value QV4Include::resultValue(QV4::ExecutionEngine *v4, Status status) +{ + + // XXX It seems inefficient to create this object from scratch each time. + QV4::Object *o = v4->newObject(); + o->put(v4->newString("OK"), QV4::Value::fromInt32(Ok)); + o->put(v4->newString("LOADING"), QV4::Value::fromInt32(Loading)); + o->put(v4->newString("NETWORK_ERROR"), QV4::Value::fromInt32(NetworkError)); + o->put(v4->newString("EXCEPTION"), QV4::Value::fromInt32(Exception)); + + o->put(v4->newString("status"), QV4::Value::fromInt32(status)); + + return QV4::Value::fromObject(o); +} + +void QV4Include::callback(const QV4::Value &callback, const QV4::Value &status) +{ + QV4::FunctionObject *f = callback.asFunctionObject(); + if (!f) + return; + + QV4::Value args[] = { status }; + QV4::ExecutionContext *ctx = f->engine()->current; + try { + f->call(QV4::Value::fromObject(f->engine()->globalObject), args, 1); + } catch (QV4::Exception &e) { + e.accept(ctx); + } +} + +QV4::Value QV4Include::result() +{ + return m_resultObject.value(); +} + +#define INCLUDE_MAXIMUM_REDIRECT_RECURSION 15 +void QV4Include::finished() +{ + m_redirectCount++; + + if (m_redirectCount < INCLUDE_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + m_url = m_url.resolved(redirect.toUrl()); + delete m_reply; + + QNetworkRequest request; + request.setUrl(m_url); + + m_reply = m_network->get(request); + QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + return; + } + } + + if (m_reply->error() == QNetworkReply::NoError) { + QByteArray data = m_reply->readAll(); + + QString code = QString::fromUtf8(data); + QQmlScript::Parser::extractPragmas(code); + + QV4::Script script(v4, m_qmlglobal.value().asObject(), code, m_url.toString()); + + QV4::ExecutionContext *ctx = v4->current; + QV4::Object *o = m_resultObject.value().asObject(); + try { + script.parse(); + script.run(); + o->put(v4->newString("status"), QV4::Value::fromInt32(Ok)); + } catch (QV4::Exception &e) { + e.accept(ctx); + o->put(v4->newString("status"), QV4::Value::fromInt32(Exception)); + o->put(v4->newString("exception"), e.value()); + } + } else { + m_resultObject.value().asObject()->put(v4->newString("status"), QV4::Value::fromInt32(NetworkError)); + } + + callback(m_callbackFunction.value(), m_resultObject.value()); + + disconnect(); + deleteLater(); +} + +/* + Documented in qv8engine.cpp +*/ +QV4::Value QV4Include::include(QV4::SimpleCallContext *ctx) +{ + if (!ctx->argumentCount) + return QV4::Value::undefinedValue(); + + QV4::ExecutionEngine *v4 = ctx->engine; + QV8Engine *engine = v4->v8Engine; + QQmlContextData *context = QV4::QmlContextWrapper::callingContext(v4); + + if (!context || !context->isJSContext) + V4THROW_ERROR("Qt.include(): Can only be called from JavaScript files"); + + QUrl url(ctx->engine->resolvedUrl(ctx->arguments[0].toQString())); + + QV4::Value callbackFunction = QV4::Value::undefinedValue(); + if (ctx->argumentCount >= 2 && ctx->arguments[1].asFunctionObject()) + callbackFunction = ctx->arguments[1]; + + QString localFile = QQmlFile::urlToLocalFileOrQrc(url); + + QV4::Value result = QV4::Value::undefinedValue(); + + if (localFile.isEmpty()) { + + QV4Include *i = new QV4Include(url, engine, context, + QV4::Value::fromObject(v4->qmlContextObject()), + callbackFunction); + result = i->result(); + + } else { + + QFile f(localFile); + + if (f.open(QIODevice::ReadOnly)) { + QByteArray data = f.readAll(); + QString code = QString::fromUtf8(data); + QQmlScript::Parser::extractPragmas(code); + + QV4::Object *qmlglobal = v4->qmlContextObject(); + QV4::Script script(v4, qmlglobal, code, url.toString()); + + QV4::ExecutionContext *ctx = v4->current; + try { + script.parse(); + script.run(); + result = resultValue(v4, Ok); + } catch (QV4::Exception &e) { + e.accept(ctx); + result = resultValue(v4, Exception); + result.asObject()->put(v4->newString("exception"), e.value()); + } + } else { + result = resultValue(v4, NetworkError); + } + + callback(callbackFunction, result); + } + + return result; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4program_p.h b/src/qml/qml/v4/qv4include_p.h index fb23e863af..d6bbcd1a60 100644 --- a/src/qml/qml/v4/qv4program_p.h +++ b/src/qml/qml/v4/qv4include_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef QV4PROGRAM_P_H -#define QV4PROGRAM_P_H +#ifndef QV4INCLUDE_P_H +#define QV4INCLUDE_P_H // // W A R N I N G @@ -53,76 +53,61 @@ // We mean it. // -#include "qv4instruction_p.h" +#include <QtCore/qobject.h> +#include <QtCore/qurl.h> -#ifdef Q_CC_MSVC -// nonstandard extension used : zero-sized array in struct/union. -# pragma warning( disable : 4200 ) -#endif +#include <private/qqmlcontext_p.h> + +#include <private/qv4value_p.h> +#include <private/qv4context_p.h> QT_BEGIN_NAMESPACE -struct QV4Program { - quint32 bindings; - quint32 dataLength; - quint32 signalTableOffset; - quint32 exceptionDataOffset; - quint16 subscriptions; - quint16 instructionCount; - - struct BindingReference { - quint32 binding; - quint32 blockMask; - }; - - struct BindingReferenceList { - quint32 count; - BindingReference bindings[]; +class QQmlEngine; +class QNetworkAccessManager; +class QNetworkReply; +class QV8Engine; +class QV4Include : public QObject +{ + Q_OBJECT +public: + enum Status { + Ok = 0, + Loading = 1, + NetworkError = 2, + Exception = 3 }; - inline const char *data() const; - inline const char *instructions() const; - inline BindingReferenceList *signalTable(int signalIndex) const; -}; + static QV4::Value include(QV4::SimpleCallContext *ctx); -enum QQmlRegisterType { - UndefinedType, - NullType, - QObjectStarType, - NumberType, - FloatType, - IntType, - BoolType, - SpecialNumericType, - - PODValueType, - - FirstCleanupType, - QStringType = FirstCleanupType, - QUrlType, - QVariantType, - QColorType, - V8HandleType, - QJSValueType -}; +private slots: + void finished(); + +private: + QV4Include(const QUrl &url, QV8Engine *engine, QQmlContextData *context, + const QV4::Value &qmlglobal, const QV4::Value &callback); + ~QV4Include(); -const char *QV4Program::data() const -{ - return ((const char *)this) + sizeof(QV4Program); -} + QV4::Value result(); -const char *QV4Program::instructions() const -{ - return (const char *)(data() + dataLength); -} + static QV4::Value resultValue(QV4::ExecutionEngine *v4, Status status = Loading); + static void callback(const QV4::Value &callback, const QV4::Value &status); -QV4Program::BindingReferenceList *QV4Program::signalTable(int signalIndex) const -{ - quint32 *signalTable = (quint32 *)(data() + signalTableOffset); - return (BindingReferenceList *)(signalTable + signalTable[signalIndex]); -} + QV4::ExecutionEngine *v4; + QNetworkAccessManager *m_network; + QPointer<QNetworkReply> m_reply; + + QUrl m_url; + int m_redirectCount; + + QV4::PersistentValue m_callbackFunction; + QV4::PersistentValue m_resultObject; + + QQmlGuardedContextData m_context; + QV4::PersistentValue m_qmlglobal; +}; QT_END_NAMESPACE -#endif // QV4PROGRAM_P_H +#endif diff --git a/src/qml/qml/v4/qv4instruction.cpp b/src/qml/qml/v4/qv4instruction.cpp deleted file mode 100644 index 103a43a3a3..0000000000 --- a/src/qml/qml/v4/qv4instruction.cpp +++ /dev/null @@ -1,532 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4instruction_p.h" -#include "qv4bindings_p.h" - -#include <QtCore/qdebug.h> -#include <private/qqmlglobal_p.h> - -// Define this to do a test dump of all the instructions at startup. This is -// helpful to test that each instruction's Instr::dump() case uses the correct -// number of tabs etc and otherwise looks correct. -// #define DEBUG_INSTR_DUMP - -QT_BEGIN_NAMESPACE - -namespace QQmlJS { - -#ifdef DEBUG_INSTR_DUMP -static struct DumpInstrAtStartup { - DumpInstrAtStartup() { - Bytecode bc; -#define DUMP_INSTR_AT_STARTUP(I, FMT) { V4InstrData<V4Instr::I> i; bc.append(i); } - FOR_EACH_V4_INSTR(DUMP_INSTR_AT_STARTUP); -#undef DUMP_INSTR_AT_STARTUP - const char *start = bc.constData(); - const char *end = start + bc.size(); - bc.dump(start, end); - } -} dump_instr_at_startup; -#endif - -int V4Instr::size(Type type) -{ -#define V4_RETURN_INSTR_SIZE(I, FMT) case I: return QML_V4_INSTR_SIZE(I, FMT); - switch (type) { - FOR_EACH_V4_INSTR(V4_RETURN_INSTR_SIZE) - } -#undef V4_RETURN_INSTR_SIZE - return 0; -} - -void Bytecode::dump(const V4Instr *i, int address) const -{ - QByteArray leading; - if (address != -1) { - leading = QByteArray::number(address); - leading.prepend(QByteArray(8 - leading.count(), ' ')); - leading.append('\t'); - } - -#define INSTR_DUMP qWarning().nospace() << leading.constData() - - switch (instructionType(i)) { - case V4Instr::Noop: - INSTR_DUMP << '\t' << "Noop"; - break; - case V4Instr::BindingId: - INSTR_DUMP << i->id.line << ':' << i->id.column << ':'; - break; - case V4Instr::SubscribeId: - INSTR_DUMP << '\t' << "SubscribeId" << "\t\t" << "Id_Offset(" << i->subscribeop.index << ") -> Subscribe_Slot(" << i->subscribeop.offset << ')'; - break; - case V4Instr::FetchAndSubscribe: - INSTR_DUMP << '\t' << "FetchAndSubscribe" << '\t' << "Object_Reg(" << i->fetchAndSubscribe.reg << ") Fast_Accessor(" << i->fetchAndSubscribe.property.accessors << ") -> Output_Reg(" << i->fetchAndSubscribe.reg << ") Subscription_Slot(" << i->fetchAndSubscribe.subscription << ')'; - break; - case V4Instr::LoadId: - INSTR_DUMP << '\t' << "LoadId" << "\t\t\t" << "Id_Offset(" << i->load.index << ") -> Output_Reg(" << i->load.reg << ')'; - break; - case V4Instr::LoadScope: - INSTR_DUMP << '\t' << "LoadScope" << "\t\t" << "-> Output_Reg(" << i->load.reg << ')'; - break; - case V4Instr::LoadRoot: - INSTR_DUMP << '\t' << "LoadRoot" << "\t\t" << "-> Output_Reg(" << i->load.reg << ')'; - break; - case V4Instr::LoadSingletonObject: - INSTR_DUMP << '\t' << "LoadSingletonObject" << "\t\t" << ") -> Output_Reg(" << i->load.reg << ')'; - break; - case V4Instr::LoadAttached: - INSTR_DUMP << '\t' << "LoadAttached" << "\t\t" << "Object_Reg(" << i->attached.reg << ") Attached_Index(" << i->attached.id << ") -> Output_Reg(" << i->attached.output << ')'; - break; - case V4Instr::UnaryNot: - INSTR_DUMP << '\t' << "UnaryNot" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::UnaryMinusNumber: - INSTR_DUMP << '\t' << "UnaryMinusNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::UnaryMinusInt: - INSTR_DUMP << '\t' << "UnaryMinusInt" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::UnaryPlusNumber: - INSTR_DUMP << '\t' << "UnaryPlusNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::UnaryPlusInt: - INSTR_DUMP << '\t' << "UnaryPlusInt" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertBoolToInt: - INSTR_DUMP << '\t' << "ConvertBoolToInt" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertBoolToJSValue: - INSTR_DUMP << '\t' << "ConvertBoolToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertBoolToNumber: - INSTR_DUMP << '\t' << "ConvertBoolToNumber" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertBoolToString: - INSTR_DUMP << '\t' << "ConvertBoolToString" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertBoolToVariant: - INSTR_DUMP << '\t' << "ConvertBoolToVariant" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertBoolToVar: - INSTR_DUMP << '\t' << "ConvertBoolToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertIntToBool: - INSTR_DUMP << '\t' << "ConvertIntToBool" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertIntToJSValue: - INSTR_DUMP << '\t' << "ConvertIntToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertIntToNumber: - INSTR_DUMP << '\t' << "ConvertIntToNumber" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertIntToString: - INSTR_DUMP << '\t' << "ConvertIntToString" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertIntToVariant: - INSTR_DUMP << '\t' << "ConvertIntToVariant" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertIntToVar: - INSTR_DUMP << '\t' << "ConvertIntToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertJSValueToVar: - INSTR_DUMP << '\t' << "ConvertJSValueToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertNumberToBool: - INSTR_DUMP << '\t' << "ConvertNumberToBool" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertNumberToInt: - INSTR_DUMP << '\t' << "ConvertNumberToInt" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertNumberToJSValue: - INSTR_DUMP << '\t' << "ConvertNumberToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertNumberToString: - INSTR_DUMP << '\t' << "ConvertNumberToString" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertNumberToVariant: - INSTR_DUMP << '\t' << "ConvertNumberToVariant" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertNumberToVar: - INSTR_DUMP << '\t' << "ConvertNumberToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertStringToBool: - INSTR_DUMP << '\t' << "ConvertStringToBool" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertStringToInt: - INSTR_DUMP << '\t' << "ConvertStringToInt" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertStringToJSValue: - INSTR_DUMP << '\t' << "ConvertStringToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertStringToNumber: - INSTR_DUMP << '\t' << "ConvertStringToNumber" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertStringToUrl: - INSTR_DUMP << '\t' << "ConvertStringToUrl" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertStringToColor: - INSTR_DUMP << '\t' << "ConvertStringToColor" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertStringToVariant: - INSTR_DUMP << '\t' << "ConvertStringToVariant" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertStringToVar: - INSTR_DUMP << '\t' << "ConvertStringToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertUrlToBool: - INSTR_DUMP << '\t' << "ConvertUrlToBool" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertUrlToJSValue: - INSTR_DUMP << '\t' << "ConvertUrlToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertUrlToString: - INSTR_DUMP << '\t' << "ConvertUrlToString" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertUrlToVariant: - INSTR_DUMP << '\t' << "ConvertUrlToVariant" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertUrlToVar: - INSTR_DUMP << '\t' << "ConvertUrlToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertColorToBool: - INSTR_DUMP << '\t' << "ConvertColorToBool" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertColorToJSValue: - INSTR_DUMP << '\t' << "ConvertColorToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertColorToString: - INSTR_DUMP << '\t' << "ConvertColorToString" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertColorToVariant: - INSTR_DUMP << '\t' << "ConvertColorToVariant" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertColorToVar: - INSTR_DUMP << '\t' << "ConvertColorToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertObjectToBool: - INSTR_DUMP << '\t' << "ConvertObjectToBool" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertObjectToJSValue: - INSTR_DUMP << '\t' << "ConvertObjectToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertObjectToVariant: - INSTR_DUMP << '\t' << "ConvertObjectToVariant" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertObjectToVar: - INSTR_DUMP << '\t' << "ConvertObjectToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertVarToJSValue: - INSTR_DUMP << '\t' << "ConvertVarToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertNullToJSValue: - INSTR_DUMP << '\t' << "ConvertNullToJSValue" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertNullToObject: - INSTR_DUMP << '\t' << "ConvertNullToObject" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertNullToVariant: - INSTR_DUMP << '\t' << "ConvertNullToVariant" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ConvertNullToVar: - INSTR_DUMP << '\t' << "ConvertNullToVar" << '\t' << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::ResolveUrl: - INSTR_DUMP << '\t' << "ResolveUrl" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::MathSinNumber: - INSTR_DUMP << '\t' << "MathSinNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::MathCosNumber: - INSTR_DUMP << '\t' << "MathCosNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::MathAbsNumber: - INSTR_DUMP << '\t' << "MathAbsNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::MathRoundNumber: - INSTR_DUMP << '\t' << "MathRoundNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::MathFloorNumber: - INSTR_DUMP << '\t' << "MathFloorNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::MathCeilNumber: - INSTR_DUMP << '\t' << "MathCeilNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::MathPINumber: - INSTR_DUMP << '\t' << "MathPINumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ')'; - break; - case V4Instr::LoadNull: - INSTR_DUMP << '\t' << "LoadNull" << "\t\t" << "Constant(null) -> Output_Reg(" << i->null_value.reg << ')'; - break; - case V4Instr::LoadNumber: - INSTR_DUMP << '\t' << "LoadNumber" << "\t\t" << "Constant(" << i->number_value.value << ") -> Output_Reg(" << i->number_value.reg << ')'; - break; - case V4Instr::LoadInt: - INSTR_DUMP << '\t' << "LoadInt" << "\t\t\t" << "Constant(" << i->int_value.value << ") -> Output_Reg(" << i->int_value.reg << ')'; - break; - case V4Instr::LoadBool: - INSTR_DUMP << '\t' << "LoadBool" << "\t\t" << "Constant(" << i->bool_value.value << ") -> Output_Reg(" << i->bool_value.reg << ')'; - break; - case V4Instr::LoadString: - INSTR_DUMP << '\t' << "LoadString" << "\t\t" << "String_DataIndex(" << i->string_value.offset << ") String_Length(" << i->string_value.length << ") -> Output_Register(" << i->string_value.reg << ')'; - break; - case V4Instr::EnableV4Test: - INSTR_DUMP << '\t' << "EnableV4Test" << "\t\t" << "String_DataIndex(" << i->string_value.offset << ") String_Length(" << i->string_value.length << ')'; - break; - case V4Instr::TestV4Store: - INSTR_DUMP << '\t' << "TestV4Store" << "\t\t" << "Input_Reg(" << i->storetest.reg << ") Reg_Type(" << i->storetest.regType << ')'; - break; - case V4Instr::BitAndInt: - INSTR_DUMP << '\t' << "BitAndInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::BitOrInt: - INSTR_DUMP << '\t' << "BitOrInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::BitXorInt: - INSTR_DUMP << '\t' << "BitXorInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::AddNumber: - INSTR_DUMP << '\t' << "AddNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::AddString: - INSTR_DUMP << '\t' << "AddString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::SubNumber: - INSTR_DUMP << '\t' << "SubNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::MulNumber: - INSTR_DUMP << '\t' << "MulNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::DivNumber: - INSTR_DUMP << '\t' << "DivNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::ModNumber: - INSTR_DUMP << '\t' << "ModNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::LShiftInt: - INSTR_DUMP << '\t' << "LShiftInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::RShiftInt: - INSTR_DUMP << '\t' << "RShiftInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::URShiftInt: - INSTR_DUMP << '\t' << "URShiftInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::GtNumber: - INSTR_DUMP << '\t' << "GtNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::LtNumber: - INSTR_DUMP << '\t' << "LtNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::GeNumber: - INSTR_DUMP << '\t' << "GeNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::LeNumber: - INSTR_DUMP << '\t' << "LeNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::EqualNumber: - INSTR_DUMP << '\t' << "EqualNumber" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::NotEqualNumber: - INSTR_DUMP << '\t' << "NotEqualNumber" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::StrictEqualNumber: - INSTR_DUMP << '\t' << "StrictEqualNumber" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::StrictNotEqualNumber: - INSTR_DUMP << '\t' << "StrictNotEqualNumber" << '\t' << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::GtString: - INSTR_DUMP << '\t' << "GtString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::LtString: - INSTR_DUMP << '\t' << "LtString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::GeString: - INSTR_DUMP << '\t' << "GeString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::LeString: - INSTR_DUMP << '\t' << "LeString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::EqualString: - INSTR_DUMP << '\t' << "EqualString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::NotEqualString: - INSTR_DUMP << '\t' << "NotEqualString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::StrictEqualString: - INSTR_DUMP << '\t' << "StrictEqualString" << '\t' << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::StrictNotEqualString: - INSTR_DUMP << '\t' << "StrictNotEqualString" << '\t' << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::EqualObject: - INSTR_DUMP << '\t' << "EqualObject" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::NotEqualObject: - INSTR_DUMP << '\t' << "NotEqualObject" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::StrictEqualObject: - INSTR_DUMP << '\t' << "StrictEqualObject" << '\t' << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::StrictNotEqualObject: - INSTR_DUMP << '\t' << "StrictNotEqualObject" << '\t' << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::MathMaxNumber: - INSTR_DUMP << '\t' << "MathMaxNumber" << '\t' << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::MathMinNumber: - INSTR_DUMP << '\t' << "MathMinNumber" << '\t' << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ')'; - break; - case V4Instr::NewString: - INSTR_DUMP << '\t' << "NewString" << "\t\t" << "Register(" << i->construct.reg << ')'; - break; - case V4Instr::NewUrl: - INSTR_DUMP << '\t' << "NewUrl" << "\t\t\t" << "Register(" << i->construct.reg << ')'; - break; - case V4Instr::CleanupRegister: - INSTR_DUMP << '\t' << "CleanupRegister" << "\t\t" << "Register(" << i->cleanup.reg << ')'; - break; - case V4Instr::Fetch: - INSTR_DUMP << '\t' << "Fetch" << "\t\t\t" << "Object_Reg(" << i->fetch.reg << ") Property_Index(" << i->fetch.index << ") -> Output_Reg(" << i->fetch.reg << ')'; - break; - case V4Instr::Store: - INSTR_DUMP << '\t' << "Store" << "\t\t\t" << "Input_Reg(" << i->store.reg << ") -> Object_Reg(" << i->store.output << ") Property_Index(" << i->store.index << ')'; - break; - case V4Instr::Copy: - INSTR_DUMP << '\t' << "Copy" << "\t\t\t" << "Input_Reg(" << i->copy.src << ") -> Output_Reg(" << i->copy.reg << ')'; - break; - case V4Instr::Jump: - if (i->jump.reg != -1) { - INSTR_DUMP << '\t' << "Jump" << "\t\t\t" << "Address(" << (address + size() + i->jump.count) << ") [if false == Input_Reg(" << i->jump.reg << ")]"; - } else { - INSTR_DUMP << '\t' << "Jump" << "\t\t\t" << "Address(" << (address + size() + i->jump.count) << ')'; - } - break; - case V4Instr::BranchFalse: - INSTR_DUMP << '\t' << "BranchFalse" << "\t\t" << "Address(" << (address + size() + i->branchop.offset) << ") [if false == Input_Reg(" << i->branchop.reg << ")]"; - break; - case V4Instr::BranchTrue: - INSTR_DUMP << '\t' << "BranchTrue" << "\t\t" << "Address(" << (address + size() + i->branchop.offset) << ") [if true == Input_Reg(" << i->branchop.reg << ")]"; - break; - case V4Instr::Branch: - INSTR_DUMP << '\t' << "Branch" << "\t\t\t" << "Address(" << (address + size() + i->branchop.offset) << ')'; - break; - case V4Instr::Block: - INSTR_DUMP << '\t' << "Block" << "\t\t\t" << "Mask(" << QByteArray::number(i->blockop.block, 16).constData() << ')'; - break; - case V4Instr::Throw: - INSTR_DUMP << '\t' << "Throw" << "\t\t\t" << "InputReg(" << i->throwop.message << ')'; - break; - default: - INSTR_DUMP << '\t' << "Unknown"; - break; - } -} - -void Bytecode::dump(const char *start, const char *end) const -{ - const char *code = start; - while (code < end) { - const V4Instr *instr = reinterpret_cast<const V4Instr *>(code); - dump(instr, code - start); - code += V4Instr::size(instructionType(instr)); - } -} - -Bytecode::Bytecode() -{ -#ifdef QML_THREADED_INTERPRETER - decodeInstr = QV4Bindings::getDecodeInstrTable(); -#endif -} - -V4Instr::Type Bytecode::instructionType(const V4Instr *instr) const -{ -#ifdef QML_THREADED_INTERPRETER - void *code = instr->common.code; - -# define CHECK_V4_INSTR_CODE(I, FMT) \ - if (decodeInstr[static_cast<int>(V4Instr::I)] == code) \ - return V4Instr::I; - - FOR_EACH_V4_INSTR(CHECK_V4_INSTR_CODE) - Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid instruction address"); - return static_cast<V4Instr::Type>(0); -# undef CHECK_V4_INSTR_CODE -#else - return static_cast<V4Instr::Type>(instr->common.type); -#endif - -} - -void Bytecode::append(V4Instr::Type type, V4Instr &instr) -{ -#ifdef QML_THREADED_INTERPRETER - instr.common.code = decodeInstr[static_cast<int>(type)]; -#else - instr.common.type = type; -#endif - d.append(reinterpret_cast<const char *>(&instr), V4Instr::size(type)); -} - -int Bytecode::remove(int offset) -{ - const V4Instr *instr = reinterpret_cast<const V4Instr *>(d.begin() + offset); - const int instrSize = V4Instr::size(instructionType(instr)); - d.remove(offset, instrSize); - return instrSize; -} - -const V4Instr &Bytecode::operator[](int offset) const -{ - return *(reinterpret_cast<const V4Instr *>(d.begin() + offset)); -} - -V4Instr &Bytecode::operator[](int offset) -{ - return *(reinterpret_cast<V4Instr *>(d.begin() + offset)); -} - -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4instruction_p.h b/src/qml/qml/v4/qv4instruction_p.h deleted file mode 100644 index 9797abf69d..0000000000 --- a/src/qml/qml/v4/qv4instruction_p.h +++ /dev/null @@ -1,483 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4INSTRUCTION_P_H -#define QV4INSTRUCTION_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qglobal.h> -#include <QtCore/qbytearray.h> -#include <QtCore/qvector.h> -#include <QtCore/qvarlengtharray.h> - -#include <private/qqmlpropertycache_p.h> - -QT_BEGIN_NAMESPACE - -#define FOR_EACH_V4_INSTR(F) \ - F(Noop, common) \ - F(BindingId, id) \ - F(SubscribeId, subscribeop) \ - F(FetchAndSubscribe, fetchAndSubscribe) \ - F(LoadId, load) \ - F(LoadScope, load) \ - F(LoadRoot, load) \ - F(LoadSingletonObject, load) \ - F(LoadAttached, attached) \ - F(UnaryNot, unaryop) \ - F(UnaryMinusNumber, unaryop) \ - F(UnaryMinusInt, unaryop) \ - F(UnaryPlusNumber, unaryop) \ - F(UnaryPlusInt, unaryop) \ - F(ConvertBoolToInt, unaryop) \ - F(ConvertBoolToJSValue, unaryop) \ - F(ConvertBoolToNumber, unaryop) \ - F(ConvertBoolToString, unaryop) \ - F(ConvertBoolToVariant, unaryop) \ - F(ConvertBoolToVar, unaryop) \ - F(ConvertIntToBool, unaryop) \ - F(ConvertIntToJSValue, unaryop) \ - F(ConvertIntToNumber, unaryop) \ - F(ConvertIntToString, unaryop) \ - F(ConvertIntToVariant, unaryop) \ - F(ConvertIntToVar, unaryop) \ - F(ConvertJSValueToVar, unaryop) \ - F(ConvertNumberToBool, unaryop) \ - F(ConvertNumberToInt, unaryop) \ - F(ConvertNumberToJSValue, unaryop) \ - F(ConvertNumberToString, unaryop) \ - F(ConvertNumberToVariant, unaryop) \ - F(ConvertNumberToVar, unaryop) \ - F(ConvertStringToBool, unaryop) \ - F(ConvertStringToInt, unaryop) \ - F(ConvertStringToJSValue, unaryop) \ - F(ConvertStringToNumber, unaryop) \ - F(ConvertStringToUrl, unaryop) \ - F(ConvertStringToColor, unaryop) \ - F(ConvertStringToVariant, unaryop) \ - F(ConvertStringToVar, unaryop) \ - F(ConvertUrlToBool, unaryop) \ - F(ConvertUrlToJSValue, unaryop) \ - F(ConvertUrlToString, unaryop) \ - F(ConvertUrlToVariant, unaryop) \ - F(ConvertUrlToVar, unaryop) \ - F(ConvertColorToBool, unaryop) \ - F(ConvertColorToJSValue, unaryop) \ - F(ConvertColorToString, unaryop) \ - F(ConvertColorToVariant, unaryop) \ - F(ConvertColorToVar, unaryop) \ - F(ConvertObjectToBool, unaryop) \ - F(ConvertObjectToJSValue, unaryop) \ - F(ConvertObjectToVariant, unaryop) \ - F(ConvertObjectToVar, unaryop) \ - F(ConvertVarToJSValue, unaryop) \ - F(ConvertNullToJSValue, unaryop) \ - F(ConvertNullToObject, unaryop) \ - F(ConvertNullToVariant, unaryop) \ - F(ConvertNullToVar, unaryop) \ - F(ResolveUrl, unaryop) \ - F(MathSinNumber, unaryop) \ - F(MathCosNumber, unaryop) \ - F(MathAbsNumber, unaryop) \ - F(MathRoundNumber, unaryop) \ - F(MathFloorNumber, unaryop) \ - F(MathCeilNumber, unaryop) \ - F(MathPINumber, unaryop) \ - F(LoadNull, null_value) \ - F(LoadNumber, number_value) \ - F(LoadInt, int_value) \ - F(LoadBool, bool_value) \ - F(LoadString, string_value) \ - F(EnableV4Test, string_value) \ - F(TestV4Store, storetest) \ - F(BitAndInt, binaryop) \ - F(BitOrInt, binaryop) \ - F(BitXorInt, binaryop) \ - F(AddNumber, binaryop) \ - F(AddString, binaryop) \ - F(SubNumber, binaryop) \ - F(MulNumber, binaryop) \ - F(DivNumber, binaryop) \ - F(ModNumber, binaryop) \ - F(LShiftInt, binaryop) \ - F(RShiftInt, binaryop) \ - F(URShiftInt, binaryop) \ - F(GtNumber, binaryop) \ - F(LtNumber, binaryop) \ - F(GeNumber, binaryop) \ - F(LeNumber, binaryop) \ - F(EqualNumber, binaryop) \ - F(NotEqualNumber, binaryop) \ - F(StrictEqualNumber, binaryop) \ - F(StrictNotEqualNumber, binaryop) \ - F(GtString, binaryop) \ - F(LtString, binaryop) \ - F(GeString, binaryop) \ - F(LeString, binaryop) \ - F(EqualString, binaryop) \ - F(NotEqualString, binaryop) \ - F(StrictEqualString, binaryop) \ - F(StrictNotEqualString, binaryop) \ - F(EqualObject, binaryop) \ - F(NotEqualObject, binaryop) \ - F(StrictEqualObject, binaryop) \ - F(StrictNotEqualObject, binaryop) \ - F(MathMaxNumber, binaryop) \ - F(MathMinNumber, binaryop) \ - F(NewString, construct) \ - F(NewUrl, construct) \ - F(CleanupRegister, cleanup) \ - F(Copy, copy) \ - F(Fetch, fetch) \ - F(Store, store) \ - F(Jump, jump) \ - F(BranchTrue, branchop) \ - F(BranchFalse, branchop) \ - F(Branch, branchop) \ - F(Block, blockop) \ - F(Throw, throwop) - -#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) -# define QML_THREADED_INTERPRETER -#endif - -#ifdef Q_ALIGNOF -# define QML_V4_INSTR_ALIGN_MASK (Q_ALIGNOF(V4Instr) - 1) -#else -# define QML_V4_INSTR_ALIGN_MASK (sizeof(void *) - 1) -#endif - -#define QML_V4_INSTR_ENUM(I, FMT) I, -#define QML_V4_INSTR_ADDR(I, FMT) &&op_##I, -#define QML_V4_INSTR_SIZE(I, FMT) ((sizeof(V4Instr::instr_##FMT) + QML_V4_INSTR_ALIGN_MASK) & ~QML_V4_INSTR_ALIGN_MASK) - -#ifdef QML_THREADED_INTERPRETER -# define QML_V4_BEGIN_INSTR(I,FMT) op_##I: -# define QML_V4_END_INSTR(I,FMT) code += QML_V4_INSTR_SIZE(I, FMT); instr = (const V4Instr *) code; goto *instr->common.code; -# define QML_V4_INSTR_HEADER void *code; -#else -# define QML_V4_BEGIN_INSTR(I,FMT) case V4Instr::I: -# define QML_V4_END_INSTR(I,FMT) code += QML_V4_INSTR_SIZE(I, FMT); instr = (const V4Instr *) code; break; -# define QML_V4_INSTR_HEADER quint8 type; -#endif - -class QObject; -class QQmlNotifier; - -namespace QQmlJS { - -union Q_AUTOTEST_EXPORT V4Instr { - enum Type { - FOR_EACH_V4_INSTR(QML_V4_INSTR_ENUM) - }; - - static int size(Type type); - - struct instr_common { - QML_V4_INSTR_HEADER - }; - - struct instr_id { - QML_V4_INSTR_HEADER - quint16 column; - quint16 line; - }; - - struct instr_init { - QML_V4_INSTR_HEADER - quint16 subscriptions; - quint16 identifiers; - }; - - struct instr_subscribeop { - QML_V4_INSTR_HEADER - qint8 reg; - quint16 offset; - quint32 index; - }; - - struct instr_load { - QML_V4_INSTR_HEADER - qint8 reg; - quint32 index; - }; - - struct instr_attached { - QML_V4_INSTR_HEADER - qint8 output; - qint8 reg; - quint8 exceptionId; - quint32 id; - }; - - struct instr_store { - QML_V4_INSTR_HEADER - qint8 output; - qint8 reg; - quint8 exceptionId; - quint8 valueType; - quint32 index; - }; - - struct instr_storetest { - QML_V4_INSTR_HEADER - qint8 reg; - qint32 regType; - }; - - struct instr_fetchAndSubscribe { - QML_V4_INSTR_HEADER - qint8 reg; - quint8 exceptionId; - quint8 valueType; - quint16 subscription; - QQmlPropertyRawData property; - }; - - struct instr_fetch{ - QML_V4_INSTR_HEADER - qint8 reg; - quint8 exceptionId; - quint8 valueType; - quint32 index; - quint16 subOffset; - quint32 subIndex; - }; - - struct instr_copy { - QML_V4_INSTR_HEADER - qint8 reg; - qint8 src; - }; - - struct instr_construct { - QML_V4_INSTR_HEADER - qint8 reg; - }; - - struct instr_null_value { - QML_V4_INSTR_HEADER - qint8 reg; - }; - - struct instr_number_value { - QML_V4_INSTR_HEADER - qint8 reg; - double value; // XXX Makes the instruction 12 bytes - }; - - struct instr_int_value { - QML_V4_INSTR_HEADER - qint8 reg; - int value; - }; - - struct instr_bool_value { - QML_V4_INSTR_HEADER - qint8 reg; - bool value; - }; - - struct instr_string_value { - QML_V4_INSTR_HEADER - qint8 reg; - quint16 length; - quint32 offset; - }; - - struct instr_binaryop { - QML_V4_INSTR_HEADER - qint8 output; - qint8 left; - qint8 right; - }; - - struct instr_unaryop { - QML_V4_INSTR_HEADER - qint8 output; - qint8 src; - }; - - struct instr_jump { - QML_V4_INSTR_HEADER - qint8 reg; - quint32 count; - }; - - struct instr_find { - QML_V4_INSTR_HEADER - qint8 reg; - qint8 src; - quint8 exceptionId; - quint16 name; - quint16 subscribeIndex; - }; - - struct instr_cleanup { - QML_V4_INSTR_HEADER - qint8 reg; - }; - - struct instr_initstring { - QML_V4_INSTR_HEADER - quint16 offset; - quint32 dataIdx; - }; - - struct instr_branchop { - QML_V4_INSTR_HEADER - quint8 reg; - qint16 offset; - }; - - struct instr_blockop { - QML_V4_INSTR_HEADER - quint32 block; - }; - - struct instr_throwop { - QML_V4_INSTR_HEADER - quint8 exceptionId; - quint32 message; - }; - - instr_common common; - instr_id id; - instr_init init; - instr_subscribeop subscribeop; - instr_load load; - instr_attached attached; - instr_store store; - instr_storetest storetest; - instr_fetchAndSubscribe fetchAndSubscribe; - instr_fetch fetch; - instr_copy copy; - instr_construct construct; - instr_null_value null_value; - instr_number_value number_value; - instr_int_value int_value; - instr_bool_value bool_value; - instr_string_value string_value; - instr_binaryop binaryop; - instr_unaryop unaryop; - instr_jump jump; - instr_find find; - instr_cleanup cleanup; - instr_initstring initstring; - instr_branchop branchop; - instr_blockop blockop; - instr_throwop throwop; -}; - -template<int N> -struct V4InstrMeta { -}; - -#define QML_V4_INSTR_META_TEMPLATE(I, FMT) \ - template<> struct V4InstrMeta<(int)V4Instr::I> { \ - enum { Size = QML_V4_INSTR_SIZE(I, FMT) }; \ - typedef V4Instr::instr_##FMT DataType; \ - static const DataType &data(const V4Instr &instr) { return instr.FMT; } \ - static void setData(V4Instr &instr, const DataType &v) { instr.FMT = v; } \ - }; -FOR_EACH_V4_INSTR(QML_V4_INSTR_META_TEMPLATE); -#undef QML_V4_INSTR_META_TEMPLATE - -template<int Instr> -class Q_AUTOTEST_EXPORT V4InstrData : public V4InstrMeta<Instr>::DataType -{ -}; - -class Q_AUTOTEST_EXPORT Bytecode -{ - Q_DISABLE_COPY(Bytecode) - -public: - Bytecode(); - - const char *constData() const { return d.constData(); } - int size() const { return d.size(); } - int count() const { return d.count(); } - void clear() { d.clear(); } - bool isEmpty() const { return d.isEmpty(); } - V4Instr::Type instructionType(const V4Instr *instr) const; - - template <int Instr> - void append(const V4InstrData<Instr> &data) - { - V4Instr genericInstr; - V4InstrMeta<Instr>::setData(genericInstr, data); - return append(static_cast<V4Instr::Type>(Instr), genericInstr); - } - void append(V4Instr::Type type, V4Instr &instr); - - int remove(int index); - - const V4Instr &operator[](int offset) const; - V4Instr &operator[](int offset); - - void dump(const char *start, const char *end) const; - -private: - void dump(const V4Instr *instr, int = -1) const; - - QVarLengthArray<char, 4 * 1024> d; -#ifdef QML_THREADED_INTERPRETER - void **decodeInstr; -#endif -}; - -} - -QT_END_NAMESPACE - -#endif // QV4INSTRUCTION_P_H - diff --git a/src/qml/qml/v4/qv4internalclass.cpp b/src/qml/qml/v4/qv4internalclass.cpp new file mode 100644 index 0000000000..f4edc99545 --- /dev/null +++ b/src/qml/qml/v4/qv4internalclass.cpp @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qv4internalclass_p.h> +#include <qv4string_p.h> +#include <qv4engine_p.h> +#include <qv4identifier_p.h> +#include "qv4object_p.h" +#include "qv4identifiertable_p.h" + +QT_BEGIN_NAMESPACE + +uint QV4::qHash(const QV4::InternalClassTransition &t, uint) +{ + return t.id->hashValue ^ t.flags; +} + +using namespace QV4; + +static const uchar prime_deltas[] = { + 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, + 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 +}; + +static inline int primeForNumBits(int numBits) +{ + return (1 << numBits) + prime_deltas[numBits]; +} + +PropertyHashData::PropertyHashData(int numBits) + : numBits(numBits) + , size(0) +{ + refCount.store(1); + alloc = primeForNumBits(numBits); + entries = (PropertyHash::Entry *)malloc(alloc*sizeof(PropertyHash::Entry)); + memset(entries, 0, alloc*sizeof(PropertyHash::Entry)); +} + +void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize) +{ + // fill up to max 50% + bool grow = (d->alloc <= d->size*2); + + if (classSize < d->size || grow) { + PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits); + for (uint i = 0; i < d->alloc; ++i) { + const Entry &e = d->entries[i]; + if (!e.identifier || e.index >= classSize) + continue; + uint idx = e.identifier->hashValue % dd->alloc; + while (dd->entries[idx].identifier) { + ++idx; + idx %= dd->alloc; + } + dd->entries[idx] = e; + } + dd->size = classSize; + assert(d->refCount.load() > 1); + d->refCount.deref(); + d = dd; + } + + uint idx = entry.identifier->hashValue % d->alloc; + while (d->entries[idx].identifier) { + ++idx; + idx %= d->alloc; + } + d->entries[idx] = entry; + ++d->size; +} + +uint PropertyHash::lookup(const Identifier *identifier) const +{ + assert(d->entries); + + uint idx = identifier->hashValue % d->alloc; + while (1) { + if (d->entries[idx].identifier == identifier) + return d->entries[idx].index; + if (!d->entries[idx].identifier) + return UINT_MAX; + ++idx; + idx %= d->alloc; + } +} + + +InternalClass::InternalClass(const QV4::InternalClass &other) + : engine(other.engine) + , propertyTable(other.propertyTable) + , nameMap(other.nameMap) + , propertyData(other.propertyData) + , transitions() + , m_sealed(0) + , m_frozen(0) + , size(other.size) +{ +} + +// ### Should we build this up from the empty class to avoid duplication? +InternalClass *InternalClass::changeMember(String *string, PropertyAttributes data, uint *index) +{ +// qDebug() << "InternalClass::changeMember()" << string->toQString() << hex << (uint)data.m_all; + data.resolve(); + uint idx = find(string); + if (index) + *index = idx; + + assert(idx != UINT_MAX); + + if (data == propertyData[idx]) + return this; + + + Transition t = { string->identifier, (int)data.flags() }; + QHash<Transition, InternalClass *>::const_iterator tit = transitions.constFind(t); + if (tit != transitions.constEnd()) + return tit.value(); + + // create a new class and add it to the tree + InternalClass *newClass = engine->newClass(*this); + newClass->propertyData[idx] = data; + return newClass; + +} + +InternalClass *InternalClass::addMember(String *string, PropertyAttributes data, uint *index) +{ +// qDebug() << "InternalClass::addMember()" << string->toQString() << size << hex << (uint)data.m_all << data.type(); + data.resolve(); + engine->identifierTable->identifier(string); + + if (propertyTable.lookup(string->identifier) < size) + return changeMember(string, data, index); + + Transition t = { string->identifier, (int)data.flags() }; + QHash<Transition, InternalClass *>::const_iterator tit = transitions.constFind(t); + + if (index) + *index = size; + if (tit != transitions.constEnd()) + return tit.value(); + + // create a new class and add it to the tree + InternalClass *newClass = engine->newClass(*this); + PropertyHash::Entry e = { string->identifier, size }; + newClass->propertyTable.addEntry(e, size); + + // The incoming string can come from anywhere, so make sure to + // store a string in the nameMap that's guaranteed to get + // marked properly during GC. + String *name = engine->newIdentifier(string->toQString()); + newClass->nameMap.append(name); + + newClass->propertyData.append(data); + ++newClass->size; + transitions.insert(t, newClass); + return newClass; +} + +void InternalClass::removeMember(Object *object, Identifier *id) +{ + int propIdx = propertyTable.lookup(id); + assert(propIdx < size); + + Transition t = { id, -1 }; + QHash<Transition, InternalClass *>::const_iterator tit = transitions.constFind(t); + + if (tit != transitions.constEnd()) { + object->internalClass = tit.value(); + return; + } + + // create a new class and add it to the tree + object->internalClass = engine->emptyClass; + for (int i = 0; i < nameMap.size(); ++i) { + if (i == propIdx) + continue; + object->internalClass = object->internalClass->addMember(nameMap.at(i), propertyData.at(i)); + } + + transitions.insert(t, object->internalClass); +} + +uint InternalClass::find(String *string) +{ + engine->identifierTable->identifier(string); + const Identifier *id = string->identifier; + + uint index = propertyTable.lookup(id); + if (index < size) + return index; + + return UINT_MAX; +} + +InternalClass *InternalClass::sealed() +{ + if (m_sealed) + return m_sealed; + + m_sealed = engine->emptyClass; + for (int i = 0; i < nameMap.size(); ++i) { + PropertyAttributes attrs = propertyData.at(i); + attrs.setConfigurable(false); + m_sealed = m_sealed->addMember(nameMap.at(i), attrs); + } + + m_sealed->m_sealed = m_sealed; + return m_sealed; +} + +InternalClass *InternalClass::frozen() +{ + if (m_frozen) + return m_frozen; + + m_frozen = engine->emptyClass; + for (int i = 0; i < nameMap.size(); ++i) { + PropertyAttributes attrs = propertyData.at(i); + attrs.setWritable(false); + attrs.setConfigurable(false); + m_frozen = m_frozen->addMember(nameMap.at(i), attrs); + } + + m_frozen->m_frozen = m_frozen; + m_frozen->m_sealed = m_frozen; + return m_frozen; +} + +void InternalClass::destroy() +{ + if (!engine) + return; + engine = 0; + + // Free the memory of the hashes/vectors by calling clear(), which + // re-assigns them to the shared null instance. Therefore Internalclass + // doesn't need a destructor to be called. + propertyTable.~PropertyHash(); + nameMap.clear(); + propertyData.clear(); + + if (m_sealed) + m_sealed->destroy(); + + if (m_frozen) + m_frozen->destroy(); + + for (QHash<Transition, InternalClass *>::ConstIterator it = transitions.begin(), end = transitions.end(); + it != end; ++it) + it.value()->destroy(); + + transitions.clear(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4internalclass_p.h b/src/qml/qml/v4/qv4internalclass_p.h new file mode 100644 index 0000000000..fc6c5352b1 --- /dev/null +++ b/src/qml/qml/v4/qv4internalclass_p.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4INTERNALCLASS_H +#define QV4INTERNALCLASS_H + +#include <QHash> +#include <QVector> +#include "qv4global_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct String; +struct ExecutionEngine; +struct Object; +struct Identifier; + +struct PropertyHashData; +struct PropertyHash +{ + struct Entry { + const Identifier *identifier; + uint index; + }; + + PropertyHashData *d; + + inline PropertyHash(); + inline PropertyHash(const PropertyHash &other); + inline ~PropertyHash(); + + void addEntry(const Entry &entry, int classSize); + uint lookup(const Identifier *identifier) const; + +private: + PropertyHash &operator=(const PropertyHash &other); +}; + +struct PropertyHashData +{ + PropertyHashData(int numBits); + ~PropertyHashData() { + free(entries); + } + + QBasicAtomicInt refCount; + int alloc; + int size; + int numBits; + PropertyHash::Entry *entries; +}; + +inline PropertyHash::PropertyHash() +{ + d = new PropertyHashData(3); +} + +inline PropertyHash::PropertyHash(const PropertyHash &other) +{ + d = other.d; + d->refCount.ref(); +} + +inline PropertyHash::~PropertyHash() +{ + if (!d->refCount.deref()) + delete d; +} + +struct InternalClassTransition +{ + Identifier *id; + int flags; + + bool operator==(const InternalClassTransition &other) const + { return id == other.id && flags == other.flags; } +}; +uint qHash(const QV4::InternalClassTransition &t, uint = 0); + +struct InternalClass { + ExecutionEngine *engine; + PropertyHash propertyTable; // id to valueIndex + QVector<String *> nameMap; + + QVector<PropertyAttributes> propertyData; + + typedef InternalClassTransition Transition; + QHash<Transition, InternalClass *> transitions; // id to next class, positive means add, negative delete + + InternalClass *m_sealed; + InternalClass *m_frozen; + + uint size; + + InternalClass *addMember(String *string, PropertyAttributes data, uint *index = 0); + InternalClass *changeMember(String *string, PropertyAttributes data, uint *index = 0); + void removeMember(Object *object, Identifier *id); + uint find(String *s); + + InternalClass *sealed(); + InternalClass *frozen(); + + void destroy(); + +private: + friend struct ExecutionEngine; + InternalClass(ExecutionEngine *engine) : engine(engine), m_sealed(0), m_frozen(0), size(0) {} + InternalClass(const InternalClass &other); +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4/qv4ir.cpp b/src/qml/qml/v4/qv4ir.cpp deleted file mode 100644 index 3159cf0d5f..0000000000 --- a/src/qml/qml/v4/qv4ir.cpp +++ /dev/null @@ -1,919 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4ir_p.h" - -#include <QtCore/qtextstream.h> -#include <QtCore/qdebug.h> -#include <math.h> - -QT_BEGIN_NAMESPACE - -namespace QQmlJS { -namespace IR { - -const char *typeName(Type t) -{ - switch (t) { - case InvalidType: return "invalid"; - case UndefinedType: return "undefined"; - case NullType: return "null"; - case VoidType: return "void"; - case StringType: return "string"; - case UrlType: return "QUrl"; - case ColorType: return "QColor"; - case SGAnchorLineType: return "SGAnchorLine"; - case AttachType: return "AttachType"; - case ObjectType: return "object"; - case VariantType: return "variant"; - case VarType: return "var"; - case JSValueType: return "QJSValue"; - case BoolType: return "bool"; - case IntType: return "int"; - case FloatType: return "float"; - case NumberType: return "number"; - default: return "invalid"; - } -} - -inline bool isNumberType(IR::Type ty) -{ - return ty >= IR::FirstNumberType; -} - -inline bool isStringType(IR::Type ty) -{ - return ty == IR::StringType || ty == IR::UrlType || ty == IR::ColorType; -} - -IR::Type maxType(IR::Type left, IR::Type right) -{ - if (isStringType(left) && isStringType(right)) { - // String promotions (url to string) are more specific than - // identity conversions (AKA left == right). That's because - // we want to ensure we convert urls to strings in binary - // expressions. - return IR::StringType; - } else if (left == right) - return left; - else if (isNumberType(left) && isNumberType(right)) { - IR::Type ty = qMax(left, right); - return ty == FloatType ? NumberType : ty; // promote floats - } else if ((isNumberType(left) && isStringType(right)) || - (isNumberType(right) && isStringType(left))) - return IR::StringType; - else - return IR::InvalidType; -} - -bool isRealType(IR::Type type) -{ - return type == IR::NumberType || type == IR::FloatType; -} - -const char *opname(AluOp op) -{ - switch (op) { - case OpInvalid: return "?"; - - case OpIfTrue: return "(bool)"; - case OpNot: return "!"; - case OpUMinus: return "-"; - case OpUPlus: return "+"; - case OpCompl: return "~"; - - case OpBitAnd: return "&"; - case OpBitOr: return "|"; - case OpBitXor: return "^"; - - case OpAdd: return "+"; - case OpSub: return "-"; - case OpMul: return "*"; - case OpDiv: return "/"; - case OpMod: return "%"; - - case OpLShift: return "<<"; - case OpRShift: return ">>"; - case OpURShift: return ">>>"; - - case OpGt: return ">"; - case OpLt: return "<"; - case OpGe: return ">="; - case OpLe: return "<="; - case OpEqual: return "=="; - case OpNotEqual: return "!="; - case OpStrictEqual: return "==="; - case OpStrictNotEqual: return "!=="; - - case OpAnd: return "&&"; - case OpOr: return "||"; - - default: return "?"; - - } // switch -} - -AluOp binaryOperator(int op) -{ - switch (static_cast<QSOperator::Op>(op)) { - case QSOperator::Add: return OpAdd; - case QSOperator::And: return OpAnd; - case QSOperator::BitAnd: return OpBitAnd; - case QSOperator::BitOr: return OpBitOr; - case QSOperator::BitXor: return OpBitXor; - case QSOperator::Div: return OpDiv; - case QSOperator::Equal: return OpEqual; - case QSOperator::Ge: return OpGe; - case QSOperator::Gt: return OpGt; - case QSOperator::Le: return OpLe; - case QSOperator::LShift: return OpLShift; - case QSOperator::Lt: return OpLt; - case QSOperator::Mod: return OpMod; - case QSOperator::Mul: return OpMul; - case QSOperator::NotEqual: return OpNotEqual; - case QSOperator::Or: return OpOr; - case QSOperator::RShift: return OpRShift; - case QSOperator::StrictEqual: return OpStrictEqual; - case QSOperator::StrictNotEqual: return OpStrictNotEqual; - case QSOperator::Sub: return OpSub; - case QSOperator::URShift: return OpURShift; - default: return OpInvalid; - } -} - -void Const::dump(QTextStream &out) -{ - out << value; -} - -void String::dump(QTextStream &out) -{ - out << '"' << escape(value) << '"'; -} - -QString String::escape(const QStringRef &s) -{ - QString r; - for (int i = 0; i < s.length(); ++i) { - const QChar ch = s.at(i); - if (ch == QLatin1Char('\n')) - r += QLatin1String("\\n"); - else if (ch == QLatin1Char('\r')) - r += QLatin1String("\\r"); - else if (ch == QLatin1Char('\\')) - r += QLatin1String("\\\\"); - else if (ch == QLatin1Char('"')) - r += QLatin1String("\\\""); - else if (ch == QLatin1Char('\'')) - r += QLatin1String("\\'"); - else - r += ch; - } - return r; -} - -void Name::init(Name *base, Type type, const QString *id, Symbol symbol, quint16 line, quint16 column) -{ - this->type = type; - this->base = base; - this->id = id; - this->symbol = symbol; - this->ptr = 0; - this->property = 0; - this->storage = MemberStorage; - this->builtin = NoBuiltinSymbol; - this->line = line; - this->column = column; - - if (id->length() == 8 && *id == QLatin1String("Math.sin")) { - builtin = MathSinBultinFunction; - } else if (id->length() == 8 && *id == QLatin1String("Math.cos")) { - builtin = MathCosBultinFunction; - } else if (id->length() == 8 && *id == QLatin1String("Math.abs")) { - builtin = MathAbsBuiltinFunction; - } else if (id->length() == 10 && *id == QLatin1String("Math.round")) { - builtin = MathRoundBultinFunction; - } else if (id->length() == 10 && *id == QLatin1String("Math.floor")) { - builtin = MathFloorBultinFunction; - } else if (id->length() == 9 && *id == QLatin1String("Math.ceil")) { - builtin = MathCeilBuiltinFunction; - } else if (id->length() == 8 && *id == QLatin1String("Math.max")) { - builtin = MathMaxBuiltinFunction; - } else if (id->length() == 8 && *id == QLatin1String("Math.min")) { - builtin = MathMinBuiltinFunction; - } else if (id->length() == 7 && *id == QLatin1String("Math.PI")) { - builtin = MathPIBuiltinConstant; - this->type = NumberType; - } -} - -void Name::dump(QTextStream &out) -{ - if (base) { - base->dump(out); - out << '.'; - } - - out << *id; -} - -void Temp::dump(QTextStream &out) -{ - out << 't' << index; -} - -void Unop::dump(QTextStream &out) -{ - out << opname(op); - expr->dump(out); -} - -Type Unop::typeForOp(AluOp op, Expr *expr) -{ - switch (op) { - case OpIfTrue: return BoolType; - case OpNot: return BoolType; - - case OpUMinus: - case OpUPlus: - case OpCompl: - return maxType(expr->type, NumberType); - - default: - break; - } - - return InvalidType; -} - -void Binop::dump(QTextStream &out) -{ - left->dump(out); - out << ' ' << opname(op) << ' '; - right->dump(out); -} - -Type Binop::typeForOp(AluOp op, Expr *left, Expr *right) -{ - if (! (left && right)) - return InvalidType; - - switch (op) { - case OpInvalid: - return InvalidType; - - // unary operators - case OpIfTrue: - case OpNot: - case OpUMinus: - case OpUPlus: - case OpCompl: - return InvalidType; - - // bit fields - case OpBitAnd: - case OpBitOr: - case OpBitXor: - return IntType; - - case OpAdd: - if (left->type == StringType) - return StringType; - return NumberType; - - case OpSub: - case OpMul: - case OpDiv: - case OpMod: - return NumberType; - - case OpLShift: - case OpRShift: - case OpURShift: - return IntType; - - case OpAnd: - case OpOr: - return BoolType; - - case OpGt: - case OpLt: - case OpGe: - case OpLe: - case OpEqual: - case OpNotEqual: - case OpStrictEqual: - case OpStrictNotEqual: - return BoolType; - } // switch - - return InvalidType; -} - -void Call::dump(QTextStream &out) -{ - base->dump(out); - out << '('; - for (ExprList *it = args; it; it = it->next) { - if (it != args) - out << ", "; - it->expr->dump(out); - } - out << ')'; -} - -Type Call::typeForFunction(Expr *base) -{ - if (! base) - return InvalidType; - - if (Name *name = base->asName()) { - switch (name->builtin) { - case MathSinBultinFunction: - case MathCosBultinFunction: - case MathAbsBuiltinFunction: //### type could also be Int if input was Int - case MathMaxBuiltinFunction: - case MathMinBuiltinFunction: - return NumberType; - - case MathRoundBultinFunction: - case MathFloorBultinFunction: - case MathCeilBuiltinFunction: - return IntType; - - case NoBuiltinSymbol: - case MathPIBuiltinConstant: - break; - } - } // switch - - return InvalidType; -} - -void Exp::dump(QTextStream &out, Mode) -{ - out << "(void) "; - expr->dump(out); - out << ';'; -} - -void Move::dump(QTextStream &out, Mode) -{ - target->dump(out); - out << " = "; - if (source->type != target->type) - out << typeName(source->type) << "_to_" << typeName(target->type) << '('; - source->dump(out); - if (source->type != target->type) - out << ')'; - out << ';'; -} - -void Jump::dump(QTextStream &out, Mode mode) -{ - Q_UNUSED(mode); - out << "goto " << 'L' << target << ';'; -} - -void CJump::dump(QTextStream &out, Mode mode) -{ - Q_UNUSED(mode); - out << "if ("; - cond->dump(out); - out << ") goto " << 'L' << iftrue << "; else goto " << 'L' << iffalse << ';'; -} - -void Ret::dump(QTextStream &out, Mode) -{ - out << "return"; - if (expr) { - out << ' '; - expr->dump(out); - } - out << ';'; -} - -Function::~Function() -{ - qDeleteAll(basicBlocks); -} - -QString *Function::newString(const QString &text) -{ - return pool->NewString(text); -} - -BasicBlock *Function::newBasicBlock() -{ - const int index = basicBlocks.size(); - return i(new BasicBlock(this, index)); -} - -void Function::dump(QTextStream &out) -{ - out << "function () {" << endl; - foreach (BasicBlock *bb, basicBlocks) { - bb->dump(out); - } - out << '}' << endl; -} - -Temp *BasicBlock::TEMP(Type type, int index) -{ - Temp *e = function->pool->New<Temp>(); - e->init(type, index); - return e; -} - -Temp *BasicBlock::TEMP(Type type) -{ - return TEMP(type, function->tempCount++); -} - -Expr *BasicBlock::CONST(Type type, double value) -{ - Const *e = function->pool->New<Const>(); - e->init(type, value); - return e; -} - -Expr *BasicBlock::STRING(const QStringRef &value) -{ - String *e = function->pool->New<String>(); - e->init(value); - return e; -} - -Name *BasicBlock::NAME(const QString &id, quint16 line, quint16 column) -{ - return NAME(0, id, line, column); -} - -Name *BasicBlock::NAME(Name *base, const QString &id, quint16 line, quint16 column) -{ - Name *e = function->pool->New<Name>(); - e->init(base, InvalidType, - function->newString(id), - Name::Unbound, line, column); - return e; -} - -Name *BasicBlock::SYMBOL(Type type, const QString &id, const QQmlMetaObject &meta, QQmlPropertyData *property, Name::Storage storage, - quint16 line, quint16 column) -{ - Name *name = SYMBOL(/*base = */ 0, type, id, meta, property, line, column); - name->storage = storage; - return name; -} - -Name *BasicBlock::SYMBOL(Name *base, Type type, const QString &id, const QQmlMetaObject &meta, QQmlPropertyData *property, Name::Storage storage, - quint16 line, quint16 column) -{ - Name *name = function->pool->New<Name>(); - name->init(base, type, function->newString(id), - Name::Property, line, column); - name->meta = meta; - name->property = property; - name->storage = storage; - return name; -} - -Name *BasicBlock::SYMBOL(Name *base, Type type, const QString &id, const QQmlMetaObject &meta, QQmlPropertyData *property, - quint16 line, quint16 column) -{ - Name *name = function->pool->New<Name>(); - name->init(base, type, function->newString(id), - Name::Property, line, column); - name->meta = meta; - name->property = property; - return name; -} - -Name *BasicBlock::ID_OBJECT(const QString &id, const QQmlScript::Object *object, quint16 line, quint16 column) -{ - Name *name = function->pool->New<Name>(); - name->init(/*base = */ 0, IR::ObjectType, - function->newString(id), - Name::IdObject, line, column); - name->idObject = object; - name->property = 0; - name->storage = Name::IdStorage; - return name; -} - -Name *BasicBlock::ATTACH_TYPE(const QString &id, const QQmlType *attachType, Name::Storage storage, - quint16 line, quint16 column) -{ - Name *name = function->pool->New<Name>(); - name->init(/*base = */ 0, IR::AttachType, - function->newString(id), - Name::AttachType, line, column); - name->declarativeType = attachType; - name->storage = storage; - return name; -} - -Name *BasicBlock::SINGLETON_OBJECT(const QString &id, const QQmlMetaObject &meta, Name::Storage storage, - quint16 line, quint16 column) -{ - Name *name = function->pool->New<Name>(); - name->init(/*base = */ 0, IR::ObjectType, - function->newString(id), - Name::SingletonObject, line, column); - name->meta = meta; - name->storage = storage; - return name; -} - -Expr *BasicBlock::UNOP(AluOp op, Expr *expr) -{ - Unop *e = function->pool->New<Unop>(); - e->init(op, expr); - return e; -} - -Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) -{ - if (left && right) { - if (Const *c1 = left->asConst()) { - if (Const *c2 = right->asConst()) { - const IR::Type ty = Binop::typeForOp(op, left, right); - - switch (op) { - case OpAdd: return CONST(ty, c1->value + c2->value); - case OpAnd: return CONST(ty, c1->value ? c2->value : 0); - case OpBitAnd: return CONST(ty, int(c1->value) & int(c2->value)); - case OpBitOr: return CONST(ty, int(c1->value) | int(c2->value)); - case OpBitXor: return CONST(ty, int(c1->value) ^ int(c2->value)); - case OpDiv: return CONST(ty, c1->value / c2->value); - case OpEqual: return CONST(ty, c1->value == c2->value); - case OpGe: return CONST(ty, c1->value >= c2->value); - case OpGt: return CONST(ty, c1->value > c2->value); - case OpLe: return CONST(ty, c1->value <= c2->value); - case OpLShift: return CONST(ty, int(c1->value) << int(c2->value)); - case OpLt: return CONST(ty, c1->value < c2->value); - case OpMod: return CONST(ty, ::fmod(c1->value, c2->value)); - case OpMul: return CONST(ty, c1->value * c2->value); - case OpNotEqual: return CONST(ty, c1->value != c2->value); - case OpOr: return CONST(ty, c1->value ? c1->value : c2->value); - case OpRShift: return CONST(ty, int(c1->value) >> int(c2->value)); - case OpStrictEqual: return CONST(ty, c1->value == c2->value); - case OpStrictNotEqual: return CONST(ty, c1->value != c2->value); - case OpSub: return CONST(ty, c1->value - c2->value); - case OpURShift: return CONST(ty, unsigned(c1->value) >> int(c2->value)); - - case OpIfTrue: // unary ops - case OpNot: - case OpUMinus: - case OpUPlus: - case OpCompl: - case OpInvalid: - break; - } - } - } else if (op == OpAdd) { - if (String *s1 = left->asString()) { - if (String *s2 = right->asString()) { - return STRING(function->newString(s1->value.toString() + s2->value)); - } - } - } - } - - Binop *e = function->pool->New<Binop>(); - e->init(op, left, right); - return e; -} - -Expr *BasicBlock::CALL(Expr *base, ExprList *args) -{ - Call *e = function->pool->New<Call>(); - e->init(base, args); - return e; -} - -Stmt *BasicBlock::EXP(Expr *expr) -{ - Exp *s = function->pool->New<Exp>(); - s->init(expr); - statements.append(s); - return s; -} - -Stmt *BasicBlock::MOVE(Expr *target, Expr *source, bool isMoveForReturn) -{ - Move *s = function->pool->New<Move>(); - s->init(target, source, isMoveForReturn); - statements.append(s); - return s; -} - -Stmt *BasicBlock::JUMP(BasicBlock *target) -{ - if (isTerminated()) - return 0; - - Jump *s = function->pool->New<Jump>(); - s->init(target); - statements.append(s); - return s; -} - -Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) -{ - if (isTerminated()) - return 0; - - CJump *s = function->pool->New<CJump>(); - s->init(cond, iftrue, iffalse); - statements.append(s); - return s; -} - -Stmt *BasicBlock::RET(Expr *expr, Type type, quint16 line, quint16 column) -{ - if (isTerminated()) - return 0; - - Ret *s = function->pool->New<Ret>(); - s->init(expr, type, line, column); - statements.append(s); - return s; -} - -void BasicBlock::dump(QTextStream &out) -{ - out << 'L' << this << ':' << endl; - foreach (Stmt *s, statements) { - out << '\t'; - s->dump(out); - out << endl; - } -} - -#ifdef DEBUG_IR_STRUCTURE - -static const char *symbolname(Name::Symbol s) -{ - switch (s) { - case Name::Unbound: - return "Unbound"; - case Name::IdObject: - return "IdObject"; - case Name::AttachType: - return "AttachType"; - case Name::SingletonObject: - return "SingletonObject"; - case Name::Object: - return "Object"; - case Name::Property: - return "Property"; - case Name::Slot: - return "Slot"; - default: - Q_ASSERT(!"Unreachable"); - return "Unknown"; - } -} - -static const char *storagename(Name::Storage s) -{ - switch (s) { - case Name::MemberStorage: - return "MemberStorage"; - case Name::IdStorage: - return "IdStorage"; - case Name::RootStorage: - return "RootStorage"; - case Name::ScopeStorage: - return "ScopeStorage"; - default: - Q_ASSERT(!"Unreachable"); - return "UnknownStorage"; - } -} - -IRDump::IRDump() -: indentSize(0) -{ -} - -void IRDump::inc() -{ - indentSize++; - indentData = QByteArray(indentSize * 4, ' '); -} - -void IRDump::dec() -{ - indentSize--; - indentData = QByteArray(indentSize * 4, ' '); -} - -void IRDump::dec(); - -void IRDump::expression(QQmlJS::IR::Expr *e) -{ - inc(); - - e->accept(this); - - dec(); -} - -void IRDump::basicblock(QQmlJS::IR::BasicBlock *b) -{ - inc(); - - qWarning().nospace() << indent() << "BasicBlock " << b << " {"; - for (int ii = 0; ii < b->statements.count(); ++ii) { - statement(b->statements.at(ii)); - if (ii != (b->statements.count() - 1)) - qWarning(); - } - qWarning().nospace() << indent() << '}'; - - dec(); -} - -void IRDump::statement(QQmlJS::IR::Stmt *s) -{ - inc(); - - s->accept(this); - - dec(); -} - -void IRDump::function(QQmlJS::IR::Function *f) -{ - inc(); - - qWarning().nospace() << indent() << "Function {"; - for (int ii = 0; ii < f->basicBlocks.count(); ++ii) { - basicblock(f->basicBlocks.at(ii)); - } - qWarning().nospace() << indent() << '}'; - - dec(); -} - -const char *IRDump::indent() -{ - return indentData.constData(); -} - -void IRDump::visitConst(QQmlJS::IR::Const *e) -{ - qWarning().nospace() << indent() << "Const:Expr { type: " << typeName(e->type) << ", value: " << e->value << '}'; -} - -void IRDump::visitString(QQmlJS::IR::String *e) -{ - qWarning().nospace() << indent() << "String:Expr { type: " << typeName(e->type) << ", value: " << e->value << '}'; -} - -static void namedumprecur(QQmlJS::IR::Name *e, const char *indent) -{ - if (e->base) namedumprecur(e->base, indent); - qWarning().nospace() << indent << " { type: " << typeName(e->type) << ", symbol: " << symbolname(e->symbol) << ", storage: " << storagename(e->storage) << ", id: " << e->id << '}'; -} - -void IRDump::visitName(QQmlJS::IR::Name *e) -{ - qWarning().nospace() << indent() << "Name:Expr {"; - namedumprecur(e, indent()); - qWarning().nospace() << indent() << '}'; -} - -void IRDump::visitTemp(QQmlJS::IR::Temp *e) -{ - qWarning().nospace() << indent() << "Temp:Expr { type: " << typeName(e->type) << ", index: " << e->index << " }"; -} - -void IRDump::visitUnop(QQmlJS::IR::Unop *e) -{ - qWarning().nospace() << indent() << "Unop:Expr { "; - qWarning().nospace() << indent() << " type: " << typeName(e->type) << ", op: " << opname(e->op); - qWarning().nospace() << indent() << " expr: {"; - expression(e->expr); - qWarning().nospace() << indent() << " }"; - qWarning().nospace() << indent() << '}'; -} - -void IRDump::visitBinop(QQmlJS::IR::Binop *e) -{ - qWarning().nospace() << indent() << "Binop:Expr { "; - qWarning().nospace() << indent() << " type: " << typeName(e->type) << ", op: " << opname(e->op); - qWarning().nospace() << indent() << " left: {"; - inc(); - expression(e->left); - dec(); - qWarning().nospace() << indent() << " },"; - qWarning().nospace() << indent() << " right: {"; - inc(); - expression(e->right); - dec(); - qWarning().nospace() << indent() << " }"; - qWarning().nospace() << indent() << '}'; -} - -void IRDump::visitCall(QQmlJS::IR::Call *e) -{ - Q_UNUSED(e); - qWarning().nospace() << indent() << "Exp::Call { }"; -} - -void IRDump::visitExp(QQmlJS::IR::Exp *s) -{ - qWarning().nospace() << indent() << "Exp:Stmt {"; - expression(s->expr); - qWarning().nospace() << indent() << '}'; -} - -void IRDump::visitMove(QQmlJS::IR::Move *s) -{ - qWarning().nospace() << indent() << "Move:Stmt {"; - qWarning().nospace() << indent() << " isMoveForReturn: " << s->isMoveForReturn; - qWarning().nospace() << indent() << " target: {"; - inc(); - expression(s->target); - dec(); - qWarning().nospace() << indent() << " },"; - qWarning().nospace() << indent() << " source: {"; - inc(); - expression(s->source); - dec(); - qWarning().nospace() << indent() << " }"; - qWarning().nospace() << indent() << '}'; -} - -void IRDump::visitJump(QQmlJS::IR::Jump *s) -{ - qWarning().nospace() << indent() << "Jump:Stmt { BasicBlock(" << s->target << ") }"; -} - -void IRDump::visitCJump(QQmlJS::IR::CJump *s) -{ - qWarning().nospace() << indent() << "CJump:Stmt {"; - qWarning().nospace() << indent() << " cond: {"; - inc(); - expression(s->cond); - dec(); - qWarning().nospace() << indent() << " }"; - qWarning().nospace() << indent() << " iftrue: BasicBlock(" << s->iftrue << ')'; - qWarning().nospace() << indent() << " iffalse: BasicBlock(" << s->iffalse << ')'; - qWarning().nospace() << indent() << '}'; -} - -void IRDump::visitRet(QQmlJS::IR::Ret *s) -{ - qWarning().nospace() << indent() << "Ret:Stmt {"; - qWarning().nospace() << indent() << " type: " << typeName(s->type); - expression(s->expr); - qWarning().nospace() << indent() << '}'; -} -#endif - -} // end of namespace IR -} // end of namespace QQmlJS - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4ir_p.h b/src/qml/qml/v4/qv4ir_p.h deleted file mode 100644 index 701f76d9e4..0000000000 --- a/src/qml/qml/v4/qv4ir_p.h +++ /dev/null @@ -1,615 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4IR_P_H -#define QV4IR_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qqmljsast_p.h> -#include <private/qqmljsengine_p.h> -#include <private/qqmlscript_p.h> -#include <private/qqmlimport_p.h> -#include <private/qqmlengine_p.h> -#include <private/qv4compiler_p.h> - -#include <private/qqmlpool_p.h> -#include <QtCore/qvarlengtharray.h> - -// #define DEBUG_IR_STRUCTURE - -#ifdef CONST -# undef CONST -#endif - -QT_BEGIN_NAMESPACE - -class QTextStream; -class QQmlType; - -namespace QQmlJS { - -namespace IR { - -struct BasicBlock; -struct Function; - -struct Stmt; -struct Expr; - -// expressions -struct Const; -struct String; -struct Name; -struct Temp; -struct Unop; -struct Binop; -struct Call; - -// statements -struct Exp; -struct Move; -struct Jump; -struct CJump; -struct Ret; - -enum AluOp { - OpInvalid = 0, - - OpIfTrue, - OpNot, - OpUMinus, - OpUPlus, - OpCompl, - - OpBitAnd, - OpBitOr, - OpBitXor, - - OpAdd, - OpSub, - OpMul, - OpDiv, - OpMod, - - OpLShift, - OpRShift, - OpURShift, - - OpGt, - OpLt, - OpGe, - OpLe, - OpEqual, - OpNotEqual, - OpStrictEqual, - OpStrictNotEqual, - - OpAnd, - OpOr -}; -AluOp binaryOperator(int op); - -enum Type { - InvalidType, - UndefinedType, - NullType, - VoidType, - StringType, - UrlType, - ColorType, - SGAnchorLineType, - AttachType, - ObjectType, - VariantType, - VarType, - JSValueType, - - FirstNumberType, - BoolType = FirstNumberType, - IntType, - FloatType, - NumberType -}; -Type maxType(IR::Type left, IR::Type right); -bool isRealType(IR::Type type); -const char *typeName(IR::Type t); - -struct ExprVisitor { - virtual ~ExprVisitor() {} - virtual void visitConst(Const *) {} - virtual void visitString(String *) {} - virtual void visitName(Name *) {} - virtual void visitTemp(Temp *) {} - virtual void visitUnop(Unop *) {} - virtual void visitBinop(Binop *) {} - virtual void visitCall(Call *) {} -}; - -struct StmtVisitor { - virtual ~StmtVisitor() {} - virtual void visitExp(Exp *) {} - virtual void visitMove(Move *) {} - virtual void visitJump(Jump *) {} - virtual void visitCJump(CJump *) {} - virtual void visitRet(Ret *) {} -}; - -struct Expr: QQmlPool::POD { - Type type; - - Expr(): type(InvalidType) {} - virtual ~Expr() {} - virtual void accept(ExprVisitor *) = 0; - virtual Const *asConst() { return 0; } - virtual String *asString() { return 0; } - virtual Name *asName() { return 0; } - virtual Temp *asTemp() { return 0; } - virtual Unop *asUnop() { return 0; } - virtual Binop *asBinop() { return 0; } - virtual Call *asCall() { return 0; } - virtual void dump(QTextStream &out) = 0; -}; - -struct ExprList: QQmlPool::POD { - Expr *expr; - ExprList *next; - - void init(Expr *expr, ExprList *next = 0) - { - this->expr = expr; - this->next = next; - } -}; - -struct Const: Expr { - double value; - - void init(Type type, double value) - { - this->type = type; - this->value = value; - } - - virtual void accept(ExprVisitor *v) { v->visitConst(this); } - virtual Const *asConst() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct String: Expr { - QStringRef value; - - void init(const QStringRef &value) - { - this->type = StringType; - this->value = value; - } - - virtual void accept(ExprVisitor *v) { v->visitString(this); } - virtual String *asString() { return this; } - - virtual void dump(QTextStream &out); - static QString escape(const QStringRef &s); -}; - -enum BuiltinSymbol { - NoBuiltinSymbol, - MathSinBultinFunction, - MathCosBultinFunction, - MathRoundBultinFunction, - MathFloorBultinFunction, - MathCeilBuiltinFunction, - MathAbsBuiltinFunction, - MathMaxBuiltinFunction, - MathMinBuiltinFunction, - - MathPIBuiltinConstant -}; - -struct Name: Expr { - enum Symbol { - Unbound, - IdObject, // This is a load of a id object. Storage will always be IdStorage - AttachType, // This is a load of an attached object - SingletonObject, // This is a load of a singleton object - Object, // XXX what is this for? - Property, // This is a load of a regular property - Slot // XXX what is this for? - }; - - enum Storage { - MemberStorage, // This is a property of a previously fetched object - IdStorage, // This is a load of a id object. Symbol will always be IdObject - RootStorage, // This is a property of the root object - ScopeStorage // This is a property of the scope object - }; - - Name *base; - const QString *id; - Symbol symbol; - union { - void *ptr; - const QQmlType *declarativeType; - const QQmlScript::Object *idObject; - }; - - QQmlMetaObject meta; - QQmlPropertyData *property; - Storage storage; - BuiltinSymbol builtin; - quint16 line; - quint16 column; - - void init(Name *base, Type type, const QString *id, Symbol symbol, quint16 line, quint16 column); - - inline bool is(Symbol s) const { return s == symbol; } - inline bool isNot(Symbol s) const { return s != symbol; } - - virtual void accept(ExprVisitor *v) { v->visitName(this); } - virtual Name *asName() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Temp: Expr { - int index; - - void init(Type type, int index) - { - this->type = type; - this->index = index; - } - - virtual void accept(ExprVisitor *v) { v->visitTemp(this); } - virtual Temp *asTemp() { return this; } - - virtual void dump(QTextStream &out); -}; - -struct Unop: Expr { - AluOp op; - Expr *expr; - - void init(AluOp op, Expr *expr) - { - this->type = this->typeForOp(op, expr); - this->op = op; - this->expr = expr; - } - - virtual void accept(ExprVisitor *v) { v->visitUnop(this); } - virtual Unop *asUnop() { return this; } - - virtual void dump(QTextStream &out); - -private: - static Type typeForOp(AluOp op, Expr *expr); -}; - -struct Binop: Expr { - AluOp op; - Expr *left; - Expr *right; - - void init(AluOp op, Expr *left, Expr *right) - { - this->type = typeForOp(op, left, right); - this->op = op; - this->left = left; - this->right = right; - } - - virtual void accept(ExprVisitor *v) { v->visitBinop(this); } - virtual Binop *asBinop() { return this; } - - virtual void dump(QTextStream &out); - - static Type typeForOp(AluOp op, Expr *left, Expr *right); -}; - -struct Call: Expr { - Expr *base; - ExprList *args; - - void init(Expr *base, ExprList *args) - { - this->type = typeForFunction(base); - this->base = base; - this->args = args; - } - - Expr *onlyArgument() const { - if (args && ! args->next) - return args->expr; - return 0; - } - - virtual void accept(ExprVisitor *v) { v->visitCall(this); } - virtual Call *asCall() { return this; } - - virtual void dump(QTextStream &out); - -private: - static Type typeForFunction(Expr *base); -}; - -struct Stmt: QQmlPool::POD { - enum Mode { - HIR, - MIR - }; - - virtual ~Stmt() {} - virtual Stmt *asTerminator() { return 0; } - - virtual void accept(StmtVisitor *) = 0; - virtual Exp *asExp() { return 0; } - virtual Move *asMove() { return 0; } - virtual Jump *asJump() { return 0; } - virtual CJump *asCJump() { return 0; } - virtual Ret *asRet() { return 0; } - virtual void dump(QTextStream &out, Mode mode = HIR) = 0; -}; - -struct Exp: Stmt { - Expr *expr; - - void init(Expr *expr) - { - this->expr = expr; - } - - virtual void accept(StmtVisitor *v) { v->visitExp(this); } - virtual Exp *asExp() { return this; } - - virtual void dump(QTextStream &out, Mode); -}; - -struct Move: Stmt { - Expr *target; - Expr *source; - bool isMoveForReturn; - - void init(Expr *target, Expr *source, bool isMoveForReturn) - { - this->target = target; - this->source = source; - this->isMoveForReturn = isMoveForReturn; - } - - virtual void accept(StmtVisitor *v) { v->visitMove(this); } - virtual Move *asMove() { return this; } - - virtual void dump(QTextStream &out, Mode); -}; - -struct Jump: Stmt { - BasicBlock *target; - - void init(BasicBlock *target) - { - this->target = target; - } - - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitJump(this); } - virtual Jump *asJump() { return this; } - - virtual void dump(QTextStream &out, Mode mode); -}; - -struct CJump: Stmt { - Expr *cond; - BasicBlock *iftrue; - BasicBlock *iffalse; - - void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) - { - this->cond = cond; - this->iftrue = iftrue; - this->iffalse = iffalse; - } - - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitCJump(this); } - virtual CJump *asCJump() { return this; } - - virtual void dump(QTextStream &out, Mode mode); -}; - -struct Ret: Stmt { - Expr *expr; - Type type; - quint16 line; - quint16 column; - - void init(Expr *expr, Type type, quint16 line, quint16 column) - { - this->expr = expr; - this->type = type; - this->line = line; - this->column = column; - } - - virtual Stmt *asTerminator() { return this; } - - virtual void accept(StmtVisitor *v) { v->visitRet(this); } - virtual Ret *asRet() { return this; } - - virtual void dump(QTextStream &out, Mode); -}; - -struct Function { - QQmlPool *pool; - QVarLengthArray<BasicBlock *, 8> basicBlocks; - int tempCount; - - Function(QQmlPool *pool) - : pool(pool), tempCount(0) {} - - virtual ~Function(); - - BasicBlock *newBasicBlock(); - QString *newString(const QString &text); - - inline BasicBlock *i(BasicBlock *block) { basicBlocks.append(block); return block; } - - virtual void dump(QTextStream &out); -}; - -struct BasicBlock { - Function *function; - int index; - int offset; - QVarLengthArray<Stmt *, 32> statements; - - BasicBlock(Function *function, int index): function(function), index(index), offset(-1) {} - ~BasicBlock() {} - - template <typename Instr> inline Instr i(Instr i) { statements.append(i); return i; } - - inline bool isEmpty() const { - return statements.isEmpty(); - } - - inline Stmt *terminator() const { - if (! statements.isEmpty() && statements.at(statements.size() - 1)->asTerminator() != 0) - return statements.at(statements.size() - 1); - return 0; - } - - inline bool isTerminated() const { - if (terminator() != 0) - return true; - return false; - } - - Temp *TEMP(Type type, int index); - Temp *TEMP(Type type); - - Expr *CONST(Type type, double value); - Expr *STRING(const QStringRef &value); - - Name *NAME(const QString &id, quint16 line, quint16 column); - Name *NAME(Name *base, const QString &id, quint16 line, quint16 column); - Name *SYMBOL(Type type, const QString &id, const QQmlMetaObject &meta, QQmlPropertyData *property, Name::Storage storage, quint16 line, quint16 column); - Name *SYMBOL(Name *base, Type type, const QString &id, const QQmlMetaObject &meta, QQmlPropertyData *property, quint16 line, quint16 column); - Name *SYMBOL(Name *base, Type type, const QString &id, const QQmlMetaObject &meta, QQmlPropertyData *property, Name::Storage storage, quint16 line, quint16 column); - Name *ID_OBJECT(const QString &id, const QQmlScript::Object *object, quint16 line, quint16 column); - Name *ATTACH_TYPE(const QString &id, const QQmlType *attachType, Name::Storage storage, quint16 line, quint16 column); - Name *SINGLETON_OBJECT(const QString &id, const QQmlMetaObject &meta, Name::Storage storage, quint16 line, quint16 column); - - Expr *UNOP(AluOp op, Expr *expr); - Expr *BINOP(AluOp op, Expr *left, Expr *right); - Expr *CALL(Expr *base, ExprList *args); - - Stmt *EXP(Expr *expr); - Stmt *MOVE(Expr *target, Expr *source, bool = false); - - Stmt *JUMP(BasicBlock *target); - Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); - Stmt *RET(Expr *expr, Type type, quint16 line, quint16 column); - - void dump(QTextStream &out); -}; - -#ifdef DEBUG_IR_STRUCTURE -struct IRDump : public ExprVisitor, - public StmtVisitor -{ -public: - IRDump(); - - void expression(QQmlJS::IR::Expr *); - void basicblock(QQmlJS::IR::BasicBlock *); - void statement(QQmlJS::IR::Stmt *); - void function(QQmlJS::IR::Function *); -protected: - - const char *indent(); - - // - // expressions - // - virtual void visitConst(QQmlJS::IR::Const *e); - virtual void visitString(QQmlJS::IR::String *e); - virtual void visitName(QQmlJS::IR::Name *e); - virtual void visitTemp(QQmlJS::IR::Temp *e); - virtual void visitUnop(QQmlJS::IR::Unop *e); - virtual void visitBinop(QQmlJS::IR::Binop *e); - virtual void visitCall(QQmlJS::IR::Call *e); - - // - // statements - // - virtual void visitExp(QQmlJS::IR::Exp *s); - virtual void visitMove(QQmlJS::IR::Move *s); - virtual void visitJump(QQmlJS::IR::Jump *s); - virtual void visitCJump(QQmlJS::IR::CJump *s); - virtual void visitRet(QQmlJS::IR::Ret *s); - -private: - int indentSize; - QByteArray indentData; - void inc(); - void dec(); -}; -#endif - -} // end of namespace IR - -} // end of namespace QQmlJS - -QT_END_NAMESPACE - -#endif // QV4IR_P_H diff --git a/src/qml/qml/v4/qv4irbuilder.cpp b/src/qml/qml/v4/qv4irbuilder.cpp deleted file mode 100644 index d217b5e2f5..0000000000 --- a/src/qml/qml/v4/qv4irbuilder.cpp +++ /dev/null @@ -1,1394 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4irbuilder_p.h" -#include "qv4compiler_p_p.h" - -#include <private/qqmlglobal_p.h> -#include <private/qqmlmetatype_p.h> -#include <private/qqmltypenamecache_p.h> - -DEFINE_BOOL_CONFIG_OPTION(qmlVerboseCompiler, QML_VERBOSE_COMPILER) - -QT_BEGIN_NAMESPACE - -using namespace QQmlJS; - -static IR::Type irTypeFromVariantType(int t, QQmlEnginePrivate *engine) -{ - switch (t) { - case QMetaType::Bool: - return IR::BoolType; - - case QMetaType::Int: - return IR::IntType; - - case QMetaType::Float: - return IR::FloatType; - - case QMetaType::Double: - return IR::NumberType; - - case QMetaType::QString: - return IR::StringType; - - case QMetaType::QUrl: - return IR::UrlType; - - case QMetaType::QColor: - return IR::ColorType; - - default: - if (t == QQmlMetaType::QQuickAnchorLineMetaTypeId()) { - return IR::SGAnchorLineType; - } else if (!engine->metaObjectForType(t).isNull()) { - return IR::ObjectType; - } else if (t == qMetaTypeId<QJSValue>()) { - return IR::JSValueType; - } - - return IR::InvalidType; - } -} - -QV4IRBuilder::QV4IRBuilder(const QV4Compiler::Expression *expr, - QQmlEnginePrivate *engine) -: m_expression(expr), m_engine(engine), _function(0), _block(0), _discard(false), - _invalidatable(false) -{ -} - -bool QV4IRBuilder::operator()(QQmlJS::IR::Function *function, - QQmlJS::AST::Node *ast, bool *invalidatable) -{ - bool discarded = false; - - IR::BasicBlock *block = function->newBasicBlock(); - - qSwap(_discard, discarded); - qSwap(_function, function); - qSwap(_block, block); - - ExprResult r; - AST::SourceLocation location; - if (AST::ExpressionNode *asExpr = ast->expressionCast()) { - r = expression(asExpr); - location = asExpr->firstSourceLocation(); - } else if (AST::Statement *asStmt = ast->statementCast()) { - r = statement(asStmt); - location = asStmt->firstSourceLocation(); - } - - //_block->MOVE(_block->TEMP(IR::InvalidType), r.code); - if (r.code) { - IR::Type targetType = IR::InvalidType; - - // This is the only operation where variant is supported: - QQmlPropertyData *data = &m_expression->property->core; - if (data->propType == QMetaType::QVariant) { - targetType = (data->isVarProperty() ? IR::VarType : IR::VariantType); - } else { - targetType = irTypeFromVariantType(data->propType, m_engine); - } - - if (targetType != r.type()) { - IR::Expr *x = _block->TEMP(targetType); - _block->MOVE(x, r, true); - r.code = x; - } - _block->RET(r.code, targetType, location.startLine, location.startColumn); - } - - qSwap(_block, block); - qSwap(_function, function); - qSwap(_discard, discarded); - - *invalidatable = _invalidatable; - return !discarded; -} - -bool QV4IRBuilder::buildName(QList<QStringRef> &name, - AST::Node *node, - QList<AST::ExpressionNode *> *nodes) -{ - if (node->kind == AST::Node::Kind_IdentifierExpression) { - name << static_cast<AST::IdentifierExpression*>(node)->name; - if (nodes) *nodes << static_cast<AST::IdentifierExpression*>(node); - } else if (node->kind == AST::Node::Kind_FieldMemberExpression) { - AST::FieldMemberExpression *expr = - static_cast<AST::FieldMemberExpression *>(node); - - if (!buildName(name, expr->base, nodes)) - return false; - - name << expr->name; - if (nodes) *nodes << expr; - } else { - return false; - } - - return true; -} - -void QV4IRBuilder::discard() -{ - _discard = true; -} - -QV4IRBuilder::ExprResult -QV4IRBuilder::expression(AST::ExpressionNode *ast) -{ - ExprResult r; - if (ast) { - qSwap(_expr, r); - accept(ast); - qSwap(_expr, r); - - if (r.is(IR::InvalidType)) - discard(); - else { - Q_ASSERT(r.hint == r.format); - } - } - - return r; -} - -void QV4IRBuilder::condition(AST::ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) -{ - if (! ast) - return; - ExprResult r(iftrue, iffalse); - qSwap(_expr, r); - accept(ast); - qSwap(_expr, r); - - if (r.format != ExprResult::cx) { - if (! r.code) - discard(); - - Q_ASSERT(r.hint == ExprResult::cx); - Q_ASSERT(r.format == ExprResult::ex); - - if (r.type() != IR::BoolType) { - IR::Temp *t = _block->TEMP(IR::BoolType); - _block->MOVE(t, r); - r = t; - } - - _block->CJUMP(_block->UNOP(IR::OpIfTrue, r), iftrue, iffalse); - } -} - -QV4IRBuilder::ExprResult -QV4IRBuilder::statement(AST::Statement *ast) -{ - ExprResult r; - if (ast) { - qSwap(_expr, r); - accept(ast); - qSwap(_expr, r); - - if (r.is(IR::InvalidType)) - discard(); - else { - Q_ASSERT(r.hint == r.format); - } - } - - return r; -} - -void QV4IRBuilder::sourceElement(AST::SourceElement *ast) -{ - accept(ast); -} - -void QV4IRBuilder::implicitCvt(ExprResult &expr, IR::Type type) -{ - if (expr.type() == type) - return; // nothing to do - - IR::Expr *x = _block->TEMP(type); - _block->MOVE(x, expr.code); - expr.code = x; -} - -// QML -bool QV4IRBuilder::visit(AST::UiProgram *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::UiImportList *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::UiImport *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::UiPublicMember *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::UiSourceElement *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::UiObjectDefinition *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::UiObjectInitializer *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::UiObjectBinding *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::UiScriptBinding *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::UiArrayBinding *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::UiObjectMemberList *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::UiArrayMemberList *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::UiQualifiedId *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - - -// JS -bool QV4IRBuilder::visit(AST::Program *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::SourceElements *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::FunctionSourceElement *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::StatementSourceElement *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -// object literals -bool QV4IRBuilder::visit(AST::PropertyAssignmentList *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::PropertyNameAndValue *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::PropertyGetterSetter *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::IdentifierPropertyName *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::StringLiteralPropertyName *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::NumericLiteralPropertyName *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - - -// array literals -bool QV4IRBuilder::visit(AST::ElementList *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -bool QV4IRBuilder::visit(AST::Elision *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - - -// function calls -bool QV4IRBuilder::visit(AST::ArgumentList *) -{ - Q_ASSERT(!"unreachable"); - return false; -} - -// expressions -bool QV4IRBuilder::visit(AST::ObjectLiteral *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::ArrayLiteral *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::ThisExpression *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::IdentifierExpression *ast) -{ - const quint16 line = ast->identifierToken.startLine; - const quint16 column = ast->identifierToken.startColumn; - - const QString name = ast->name.toString(); - - if (name.at(0) == QLatin1Char('u') && name.length() == 9 && name == QLatin1String("undefined")) { - _expr.code = _block->CONST(IR::UndefinedType, 0); // ### undefined value - } else if (m_engine->v8engine()->illegalNames().contains(name) ) { - if (qmlVerboseCompiler()) qWarning() << "*** illegal symbol:" << name; - return false; - } else if (const QQmlScript::Object *obj = m_expression->ids->value(name)) { - IR::Name *code = _block->ID_OBJECT(name, obj, line, column); - if (obj == m_expression->component) - code->storage = IR::Name::RootStorage; - _expr.code = code; - } else { - - QQmlTypeNameCache::Result r = m_expression->importCache->query(name); - if (r.isValid()) { - if (r.type) { - if (r.type->isSingleton()) { - // Note: we don't need to check singletonType->qobjectCallback here, since - // we did that check in registerSingletonType() in qqmlmetatype.cpp. - // We cannot create the QObject Singleton Type Instance here, - // as we might be running in a loader thread. - // Thus, V4 can only handle bindings which use Singleton Types which - // were registered with the templated registration function. - _expr.code = _block->SINGLETON_OBJECT(name, r.type->singletonInstanceInfo()->instanceMetaObject, IR::Name::MemberStorage, line, column); - } else { - _expr.code = _block->ATTACH_TYPE(name, r.type, IR::Name::ScopeStorage, line, column); - } - } - } else { - bool found = false; - - if (m_expression->context != m_expression->component) { - // RootStorage is more efficient than ScopeStorage, so prefer that if they are the same - QQmlPropertyCache *cache = m_expression->context->synthCache; - if (!cache) cache = m_expression->context->metatype; - - QQmlPropertyData *data = cache->property(name, 0, 0); - - if (data && data->hasRevision()) { - if (qmlVerboseCompiler()) - qWarning() << "*** versioned symbol:" << name; - discard(); - return false; - } - - if (data && !data->isFunction()) { - IR::Type irType = irTypeFromVariantType(data->propType, m_engine); - _expr.code = _block->SYMBOL(irType, name, QQmlMetaObject(cache), data, IR::Name::ScopeStorage, line, column); - found = true; - } - } - - if (!found) { - QQmlPropertyCache *cache = m_expression->component->synthCache; - if (!cache) cache = m_expression->component->metatype; - - QQmlPropertyData *data = cache->property(name, 0, 0); - - if (data && data->hasRevision()) { - if (qmlVerboseCompiler()) - qWarning() << "*** versioned symbol:" << name; - discard(); - return false; - } - - if (data && !data->isFunction()) { - IR::Type irType = irTypeFromVariantType(data->propType, m_engine); - _expr.code = _block->SYMBOL(irType, name, QQmlMetaObject(cache), data, IR::Name::RootStorage, line, column); - found = true; - } - } - - if (!found && qmlVerboseCompiler()) - qWarning() << "*** unknown symbol:" << name; - } - } - - if (_expr.code && _expr.hint == ExprResult::cx) { - _expr.format = ExprResult::cx; - - if (_expr.type() != IR::BoolType) { - IR::Temp *t = _block->TEMP(IR::BoolType); - _block->MOVE(t, _expr); - _expr.code = t; - } - - _block->CJUMP(_expr.code, _expr.iftrue, _expr.iffalse); - _expr.code = 0; - } - - return false; -} - -bool QV4IRBuilder::visit(AST::NullExpression *) -{ - // ### TODO: cx format - _expr.code = _block->CONST(IR::NullType, 0); - return false; -} - -bool QV4IRBuilder::visit(AST::TrueLiteral *) -{ - // ### TODO: cx format - _expr.code = _block->CONST(IR::BoolType, 1); - return false; -} - -bool QV4IRBuilder::visit(AST::FalseLiteral *) -{ - // ### TODO: cx format - _expr.code = _block->CONST(IR::BoolType, 0); - return false; -} - -bool QV4IRBuilder::visit(AST::StringLiteral *ast) -{ - // ### TODO: cx format - _expr.code = _block->STRING(ast->value); - return false; -} - -bool QV4IRBuilder::visit(AST::NumericLiteral *ast) -{ - if (_expr.hint == ExprResult::cx) { - _expr.format = ExprResult::cx; - _block->JUMP(ast->value ? _expr.iftrue : _expr.iffalse); - } else { - _expr.code = _block->CONST(IR::NumberType, ast->value); - } - return false; -} - -bool QV4IRBuilder::visit(AST::RegExpLiteral *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::NestedExpression *) -{ - return true; // the value of the nested expression -} - -bool QV4IRBuilder::visit(AST::ArrayMemberExpression *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::FieldMemberExpression *ast) -{ - if (IR::Expr *left = expression(ast->base)) { - if (IR::Name *baseName = left->asName()) { - const quint32 line = ast->identifierToken.startLine; - const quint32 column = ast->identifierToken.startColumn; - - QString name = ast->name.toString(); - - switch(baseName->symbol) { - case IR::Name::Unbound: - break; - - case IR::Name::AttachType: - if (name.at(0).isUpper()) { - QByteArray utf8Name = name.toUtf8(); - const char *enumName = utf8Name.constData(); - - const QMetaObject *meta = baseName->declarativeType->metaObject(); - bool found = false; - for (int ii = 0; !found && ii < meta->enumeratorCount(); ++ii) { - QMetaEnum e = meta->enumerator(ii); - for (int jj = 0; !found && jj < e.keyCount(); ++jj) { - if (0 == strcmp(e.key(jj), enumName)) { - found = true; - _expr.code = _block->CONST(IR::IntType, e.value(jj)); - } - } - } - - if (!found && qmlVerboseCompiler()) - qWarning() << "*** unresolved enum:" - << (*baseName->id + QLatin1Char('.') + ast->name.toString()); - } else if(const QMetaObject *attachedMeta = baseName->declarativeType->attachedPropertiesType()) { - QQmlPropertyCache *cache = m_engine->cache(attachedMeta); - QQmlPropertyData *data = cache->property(name, 0, 0); - - if (!data || data->isFunction()) - return false; // Don't support methods (or non-existing properties ;) - - if (!data->isFinal()) - _invalidatable = true; - - IR::Type irType = irTypeFromVariantType(data->propType, m_engine); - _expr.code = _block->SYMBOL(baseName, irType, name, attachedMeta, data, line, column); - } - break; - - case IR::Name::SingletonObject: { - if (name.at(0).isUpper()) { - QByteArray utf8Name = name.toUtf8(); - const char *enumName = utf8Name.constData(); - - const QQmlPropertyCache *cache = baseName->meta.propertyCache(m_engine); - if (!cache) { - //Happens in some cases where they make properties with uppercase names - qFatal("QV4: Unable to resolve enum: '%s'", - QString(*baseName->id + QLatin1Char('.') + ast->name.toString()).toLatin1().constData()); - } - - const QMetaObject *meta = cache->firstCppMetaObject(); - bool found = false; - for (int ii = 0; !found && ii < meta->enumeratorCount(); ++ii) { - QMetaEnum e = meta->enumerator(ii); - for (int jj = 0; !found && jj < e.keyCount(); ++jj) { - if (0 == strcmp(e.key(jj), enumName)) { - found = true; - _expr.code = _block->CONST(IR::IntType, e.value(jj)); - } - } - } - if (!found && qmlVerboseCompiler()) - qWarning() << "*** unresolved enum:" - << (*baseName->id + QLatin1Char('.') + ast->name.toString()); - } else { - QQmlPropertyCache *cache = baseName->meta.propertyCache(m_engine); - if (!cache) return false; - QQmlPropertyData *data = cache->property(name, 0, 0); - - if (!data || data->isFunction()) - return false; // Don't support methods (or non-existing properties ;) - - if (!data->isFinal()) - _invalidatable = true; - - IR::Type irType = irTypeFromVariantType(data->propType, m_engine); - _expr.code = _block->SYMBOL(baseName, irType, name, baseName->meta, data, line, column); - } - } - break; - - case IR::Name::IdObject: { - const QQmlScript::Object *idObject = baseName->idObject; - QQmlPropertyCache *cache = - idObject->synthCache?idObject->synthCache:idObject->metatype; - - QQmlPropertyData *data = cache->property(name, 0, 0); - - if (!data || data->isFunction()) - return false; // Don't support methods (or non-existing properties ;) - - if (data->hasRevision()) { - if (qmlVerboseCompiler()) - qWarning() << "*** versioned symbol:" << name; - discard(); - return false; - } - - IR::Type irType = irTypeFromVariantType(data->propType, m_engine); - _expr.code = _block->SYMBOL(baseName, irType, name, QQmlMetaObject(cache), data, line, column); - } - break; - - case IR::Name::Property: - if (baseName->type == IR::ObjectType && !baseName->meta.isNull()) { - QQmlMetaObject meta = m_engine->metaObjectForType(baseName->property->propType); - QQmlPropertyCache *cache = meta.propertyCache(m_engine); - if (!cache) - return false; - - if (QQmlPropertyData *data = cache->property(name, 0, 0)) { - if (!baseName->property->isFinal() || !data->isFinal()) - _invalidatable = true; - - IR::Type irType = irTypeFromVariantType(data->propType, m_engine); - _expr.code = _block->SYMBOL(baseName, irType, name, - meta, data, line, column); - } - } - break; - - case IR::Name::Object: - case IR::Name::Slot: - break; - } - } - } - - return false; -} - -bool QV4IRBuilder::preVisit(AST::Node *) -{ - return ! _discard; -} - -bool QV4IRBuilder::visit(AST::NewMemberExpression *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::NewExpression *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::CallExpression *ast) -{ - QList<QStringRef> names; - QList<AST::ExpressionNode *> nameNodes; - - names.reserve(4); - nameNodes.reserve(4); - - if (buildName(names, ast->base, &nameNodes)) { - //ExprResult base = expression(ast->base); - QString id; - for (int i = 0; i < names.size(); ++i) { - if (i) - id += QLatin1Char('.'); - id += names.at(i); - } - const AST::SourceLocation loc = nameNodes.last()->firstSourceLocation(); - IR::Expr *base = _block->NAME(id, loc.startLine, loc.startColumn); - - IR::ExprList *args = 0, **argsInserter = &args; - for (AST::ArgumentList *it = ast->arguments; it; it = it->next) { - IR::Expr *arg = expression(it->expression); - *argsInserter = _function->pool->New<IR::ExprList>(); - (*argsInserter)->init(arg); - argsInserter = &(*argsInserter)->next; - } - - IR::Temp *r = _block->TEMP(IR::InvalidType); - IR::Expr *call = _block->CALL(base, args); - _block->MOVE(r, call); - r->type = call->type; - _expr.code = r; - } - - return false; -} - -bool QV4IRBuilder::visit(AST::PostIncrementExpression *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::PostDecrementExpression *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::DeleteExpression *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::VoidExpression *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::TypeOfExpression *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::PreIncrementExpression *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::PreDecrementExpression *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::UnaryPlusExpression *ast) -{ - ExprResult expr = expression(ast->expression); - if (expr.isNot(IR::InvalidType)) { - if (expr.code->asConst() != 0) { - _expr = expr; - return false; - } - - IR::Expr *code = _block->UNOP(IR::OpUPlus, expr); - _expr.code = _block->TEMP(code->type); - _block->MOVE(_expr, code); - } - - return false; -} - -bool QV4IRBuilder::visit(AST::UnaryMinusExpression *ast) -{ - ExprResult expr = expression(ast->expression); - if (expr.isNot(IR::InvalidType)) { - if (IR::Const *c = expr.code->asConst()) { - _expr = expr; - _expr.code = _block->CONST(expr->type, -c->value); - return false; - } - - IR::Expr *code = _block->UNOP(IR::OpUMinus, expr); - _expr.code = _block->TEMP(code->type); - _block->MOVE(_expr, code); - } - - return false; -} - -bool QV4IRBuilder::visit(AST::TildeExpression *ast) -{ - ExprResult expr = expression(ast->expression); - if (expr.isNot(IR::InvalidType)) { - if (IR::Const *c = expr.code->asConst()) { - _expr = expr; - _expr.code = _block->CONST(expr->type, ~int(c->value)); - return false; - } - IR::Expr *code = _block->UNOP(IR::OpCompl, expr); - _expr.code = _block->TEMP(code->type); - _block->MOVE(_expr, code); - } - - return false; -} - -bool QV4IRBuilder::visit(AST::NotExpression *ast) -{ - ExprResult expr = expression(ast->expression); - - if (expr.isNot(IR::InvalidType)) { - if (IR::Const *c = expr.code->asConst()) { - _expr = expr; - _expr.code = _block->CONST(IR::BoolType, !c->value); - return false; - } - - IR::Expr *code = _block->UNOP(IR::OpNot, expr); - _expr.code = _block->TEMP(code->type); - _block->MOVE(_expr, code); - - } else if (expr.hint == ExprResult::cx) { - expr.format = ExprResult::cx; - _block->CJUMP(_block->UNOP(IR::OpNot, expr), _expr.iftrue, _expr.iffalse); - return false; - } - - return false; -} - -void QV4IRBuilder::binop(AST::BinaryExpression *ast, ExprResult left, ExprResult right) -{ - if (IR::Type t = maxType(left.type(), right.type())) { - if (!left->asConst() && !right->asConst()) { - // the implicit conversions are needed only - // when compiling non-constant expressions. - implicitCvt(left, t); - implicitCvt(right, t); - } - } else if ((left.type() != IR::ObjectType && left.type() != IR::NullType) || - (right.type() != IR::ObjectType && right.type() != IR::NullType)) - return; - - if (_expr.hint == ExprResult::cx) { - _expr.format = ExprResult::cx; - _block->CJUMP(_block->BINOP(IR::binaryOperator(ast->op), left, right), _expr.iftrue, _expr.iffalse); - } else { - IR::Expr *e = _block->BINOP(IR::binaryOperator(ast->op), left, right); - if (e->asConst() != 0 || e->asString() != 0) - _expr.code = e; - else { - IR::Temp *t = _block->TEMP(e->type); - _block->MOVE(t, e); - _expr.code = t; - } - } -} - -bool QV4IRBuilder::visit(AST::BinaryExpression *ast) -{ - switch (ast->op) { - case QSOperator::And: { - if (_expr.hint == ExprResult::cx) { - _expr.format = ExprResult::cx; - - Q_ASSERT(_expr.iffalse != 0); - Q_ASSERT(_expr.iftrue != 0); - - IR::BasicBlock *iftrue = _function->newBasicBlock(); - condition(ast->left, iftrue, _expr.iffalse); - - _block = iftrue; - condition(ast->right, _expr.iftrue, _expr.iffalse); - } else { - IR::BasicBlock *iftrue = _function->newBasicBlock(); - IR::BasicBlock *iffalse = _function->newBasicBlock(); - IR::BasicBlock *endif = _function->newBasicBlock(); - - ExprResult left = expression(ast->left); - IR::Temp *cond = _block->TEMP(IR::BoolType); - _block->MOVE(cond, left); - _block->CJUMP(cond, iftrue, iffalse); - - IR::Temp *r = _block->TEMP(IR::InvalidType); - - _block = iffalse; - _block->MOVE(r, cond); - _block->JUMP(endif); - - _block = iftrue; - ExprResult right = expression(ast->right); - _block->MOVE(r, right); - _block->JUMP(endif); - - if (left.type() != right.type()) - discard(); - - _block = endif; - - r->type = right.type(); - _expr.code = r; - } - } break; - - case QSOperator::Or: { - IR::BasicBlock *iftrue = _function->newBasicBlock(); - IR::BasicBlock *endif = _function->newBasicBlock(); - - ExprResult left = expression(ast->left); - IR::Temp *r = _block->TEMP(left.type()); - _block->MOVE(r, left); - - IR::Expr *cond = r; - if (r->type != IR::BoolType) { - cond = _block->TEMP(IR::BoolType); - _block->MOVE(cond, r); - } - - _block->CJUMP(_block->UNOP(IR::OpNot, cond), iftrue, endif); - - _block = iftrue; - ExprResult right = expression(ast->right); - _block->MOVE(r, right); - _block->JUMP(endif); - - if (left.type() != right.type()) - discard(); - - _expr.code = r; - - _block = endif; - } break; - - case QSOperator::Lt: - case QSOperator::Gt: - case QSOperator::Le: - case QSOperator::Ge: { - ExprResult left = expression(ast->left); - ExprResult right = expression(ast->right); - if (left.type() == IR::StringType && right.type() == IR::StringType) { - binop(ast, left, right); - } else if (left.isValid() && right.isValid()) { - implicitCvt(left, IR::NumberType); - implicitCvt(right, IR::NumberType); - binop(ast, left, right); - } - } break; - - case QSOperator::NotEqual: - case QSOperator::Equal: { - ExprResult left = expression(ast->left); - ExprResult right = expression(ast->right); - if ((left.type() == IR::NullType || left.type() == IR::UndefinedType) && - (right.type() == IR::NullType || right.type() == IR::UndefinedType)) { - const bool isEq = ast->op == QSOperator::Equal; - if (_expr.hint == ExprResult::cx) { - _expr.format = ExprResult::cx; - _block->JUMP(isEq ? _expr.iftrue : _expr.iffalse); - } else { - _expr.code = _block->CONST(IR::BoolType, isEq ? 1 : 0); - } - } else if ((left.type() == IR::StringType && right.type() >= IR::FirstNumberType) || - (left.type() >= IR::FirstNumberType && right.type() == IR::StringType)) { - implicitCvt(left, IR::NumberType); - implicitCvt(right, IR::NumberType); - binop(ast, left, right); - } else if (left.isValid() && right.isValid()) { - binop(ast, left, right); - } - } break; - - case QSOperator::StrictEqual: - case QSOperator::StrictNotEqual: { - ExprResult left = expression(ast->left); - ExprResult right = expression(ast->right); - if (left.type() == right.type()) { - binop(ast, left, right); - } else if (left.type() > IR::BoolType && right.type() > IR::BoolType) { - // left and right have numeric type (int or real) - binop(ast, left, right); - } else if ((left.type() == IR::ObjectType && right.type() == IR::NullType) || - (right.type() == IR::ObjectType && left.type() == IR::NullType)) { - // comparing a qobject with null - binop(ast, left, right); - } else if (left.isValid() && right.isValid()) { - // left and right have different types - const bool isEq = ast->op == QSOperator::StrictEqual; - if (_expr.hint == ExprResult::cx) { - _expr.format = ExprResult::cx; - _block->JUMP(isEq ? _expr.iffalse : _expr.iftrue); - } else { - _expr.code = _block->CONST(IR::BoolType, isEq ? 0 : 1); - } - } - } break; - - case QSOperator::BitAnd: - case QSOperator::BitOr: - case QSOperator::BitXor: - case QSOperator::LShift: - case QSOperator::RShift: - case QSOperator::URShift: { - ExprResult left = expression(ast->left); - if (left.is(IR::InvalidType)) - return false; - - ExprResult right = expression(ast->right); - if (right.is(IR::InvalidType)) - return false; - - implicitCvt(left, IR::IntType); - implicitCvt(right, IR::IntType); - - IR::Expr *code = _block->BINOP(IR::binaryOperator(ast->op), left, right); - _expr.code = _block->TEMP(code->type); - _block->MOVE(_expr.code, code); - - } break; - - case QSOperator::Add: { - ExprResult left = expression(ast->left); - if (left.is(IR::InvalidType)) - return false; - - ExprResult right = expression(ast->right); - if (right.is(IR::InvalidType)) - return false; - - if (left.isPrimitive() && right.isPrimitive()) { - if (left.type() == IR::StringType || right.type() == IR::StringType) { - implicitCvt(left, IR::StringType); - implicitCvt(right, IR::StringType); - } - binop(ast, left, right); - } - } break; - - case QSOperator::Div: - case QSOperator::Mod: - case QSOperator::Mul: - case QSOperator::Sub: { - ExprResult left = expression(ast->left); - if (left.is(IR::InvalidType)) - return false; - - ExprResult right = expression(ast->right); - if (right.is(IR::InvalidType)) - return false; - - IR::Type t = maxType(left.type(), right.type()); - if (t >= IR::FirstNumberType) { - implicitCvt(left, IR::NumberType); - implicitCvt(right, IR::NumberType); - - IR::Expr *code = _block->BINOP(IR::binaryOperator(ast->op), left, right); - _expr.code = _block->TEMP(code->type); - _block->MOVE(_expr.code, code); - } - } break; - - case QSOperator::In: - case QSOperator::InstanceOf: - case QSOperator::Assign: - case QSOperator::InplaceAnd: - case QSOperator::InplaceSub: - case QSOperator::InplaceDiv: - case QSOperator::InplaceAdd: - case QSOperator::InplaceLeftShift: - case QSOperator::InplaceMod: - case QSOperator::InplaceMul: - case QSOperator::InplaceOr: - case QSOperator::InplaceRightShift: - case QSOperator::InplaceURightShift: - case QSOperator::InplaceXor: - // yup, we don't do those. - break; - } // switch - - return false; -} - -bool QV4IRBuilder::visit(AST::ConditionalExpression *ast) -{ - IR::BasicBlock *iftrue = _function->newBasicBlock(); - IR::BasicBlock *iffalse = _function->newBasicBlock(); - IR::BasicBlock *endif = _function->newBasicBlock(); - - condition(ast->expression, iftrue, iffalse); - - IR::Temp *r = _block->TEMP(IR::InvalidType); - - qSwap(_block, iftrue); - ExprResult ok = expression(ast->ok); - _block->MOVE(r, ok); - _block->JUMP(endif); - qSwap(_block, iftrue); - - qSwap(_block, iffalse); - ExprResult ko = expression(ast->ko); - _block->MOVE(r, ko); - _block->JUMP(endif); - qSwap(_block, iffalse); - - r->type = maxType(ok.type(), ko.type()); - _expr.code = r; - - _block = endif; - - return false; -} - -bool QV4IRBuilder::visit(AST::Expression *ast) -{ - _block->EXP(expression(ast->left)); - _expr = expression(ast->right); - - return false; -} - - -// statements -bool QV4IRBuilder::visit(AST::Block *ast) -{ - if (ast->statements && ! ast->statements->next) { - // we have one and only one statement - accept(ast->statements->statement); - } - - return false; -} - -bool QV4IRBuilder::visit(AST::StatementList *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::VariableStatement *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::VariableDeclarationList *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::VariableDeclaration *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::EmptyStatement *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::ExpressionStatement *ast) -{ - if (ast->expression) { - // return the value of this expression - return true; - } - - return false; -} - -bool QV4IRBuilder::visit(AST::IfStatement *ast) -{ - if (! ast->ko) { - // This is an if statement without an else branch. - discard(); - } else { - IR::BasicBlock *iftrue = _function->newBasicBlock(); - IR::BasicBlock *iffalse = _function->newBasicBlock(); - IR::BasicBlock *endif = _function->newBasicBlock(); - - condition(ast->expression, iftrue, iffalse); - - IR::Temp *r = _block->TEMP(IR::InvalidType); - - qSwap(_block, iftrue); - ExprResult ok = statement(ast->ok); - _block->MOVE(r, ok); - _block->JUMP(endif); - qSwap(_block, iftrue); - - qSwap(_block, iffalse); - ExprResult ko = statement(ast->ko); - _block->MOVE(r, ko); - _block->JUMP(endif); - qSwap(_block, iffalse); - - r->type = maxType(ok.type(), ko.type()); - _expr.code = r; - - _block = endif; - } - - return false; -} - -bool QV4IRBuilder::visit(AST::DoWhileStatement *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::WhileStatement *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::ForStatement *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::LocalForStatement *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::ForEachStatement *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::LocalForEachStatement *) -{ - discard(); - return false; -} - -bool QV4IRBuilder::visit(AST::ContinueStatement *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::BreakStatement *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::ReturnStatement *ast) -{ - if (ast->expression) { - // return the value of the expression - return true; - } - - return false; -} - -bool QV4IRBuilder::visit(AST::WithStatement *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::SwitchStatement *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::CaseBlock *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::CaseClauses *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::CaseClause *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::DefaultClause *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::LabelledStatement *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::ThrowStatement *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::TryStatement *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::Catch *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::Finally *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::FunctionDeclaration *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::FunctionExpression *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::FormalParameterList *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::FunctionBody *) -{ - return false; -} - -bool QV4IRBuilder::visit(AST::DebuggerStatement *) -{ - return false; -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4irbuilder_p.h b/src/qml/qml/v4/qv4irbuilder_p.h deleted file mode 100644 index 86baae463d..0000000000 --- a/src/qml/qml/v4/qv4irbuilder_p.h +++ /dev/null @@ -1,239 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4IRBUILDER_P_H -#define QV4IRBUILDER_P_H - -#include <QtCore/qglobal.h> - -#include "qv4ir_p.h" - -QT_BEGIN_NAMESPACE - -class QV4IRBuilder : public QQmlJS::AST::Visitor -{ -public: - QV4IRBuilder(const QV4Compiler::Expression *, QQmlEnginePrivate *); - - bool operator()(QQmlJS::IR::Function *, QQmlJS::AST::Node *, bool *invalidatable); - -protected: - struct ExprResult { - enum Format { - ex, // expression - cx // condition - }; - - QQmlJS::IR::Expr *code; - QQmlJS::IR::BasicBlock *iftrue; - QQmlJS::IR::BasicBlock *iffalse; - Format hint; // requested format - Format format; // instruction format - - ExprResult(QQmlJS::IR::Expr *expr = 0) - : code(expr), iftrue(0), iffalse(0), hint(ex), format(ex) {} - - ExprResult(QQmlJS::IR::BasicBlock *iftrue, QQmlJS::IR::BasicBlock *iffalse) - : code(0), iftrue(iftrue), iffalse(iffalse), hint(cx), format(ex) {} - - inline QQmlJS::IR::Type type() const { return code ? code->type : QQmlJS::IR::InvalidType; } - - inline QQmlJS::IR::Expr *get() const { return code; } - inline operator QQmlJS::IR::Expr *() const { return get(); } - inline QQmlJS::IR::Expr *operator->() const { return get(); } - inline bool isValid() const { return code ? code->type != QQmlJS::IR::InvalidType : false; } - inline bool is(QQmlJS::IR::Type t) const { return type() == t; } - inline bool isNot(QQmlJS::IR::Type t) const { return type() != t; } - - bool isPrimitive() const { - switch (type()) { - case QQmlJS::IR::UndefinedType: // ### TODO - case QQmlJS::IR::NullType: // ### TODO - case QQmlJS::IR::UrlType: // ### TODO - return false; - - case QQmlJS::IR::StringType: - case QQmlJS::IR::BoolType: - case QQmlJS::IR::IntType: - case QQmlJS::IR::FloatType: - case QQmlJS::IR::NumberType: - return true; - - default: - return false; - } // switch - } - }; - - inline void accept(QQmlJS::AST::Node *ast) { QQmlJS::AST::Node::accept(ast, this); } - - ExprResult expression(QQmlJS::AST::ExpressionNode *ast); - ExprResult statement(QQmlJS::AST::Statement *ast); - void sourceElement(QQmlJS::AST::SourceElement *ast); - void condition(QQmlJS::AST::ExpressionNode *ast, QQmlJS::IR::BasicBlock *iftrue, QQmlJS::IR::BasicBlock *iffalse); - void binop(QQmlJS::AST::BinaryExpression *ast, ExprResult left, ExprResult right); - - void implicitCvt(ExprResult &expr, QQmlJS::IR::Type type); - - virtual bool preVisit(QQmlJS::AST::Node *ast); - - // QML - virtual bool visit(QQmlJS::AST::UiProgram *ast); - virtual bool visit(QQmlJS::AST::UiImportList *ast); - virtual bool visit(QQmlJS::AST::UiImport *ast); - virtual bool visit(QQmlJS::AST::UiPublicMember *ast); - virtual bool visit(QQmlJS::AST::UiSourceElement *ast); - virtual bool visit(QQmlJS::AST::UiObjectDefinition *ast); - virtual bool visit(QQmlJS::AST::UiObjectInitializer *ast); - virtual bool visit(QQmlJS::AST::UiObjectBinding *ast); - virtual bool visit(QQmlJS::AST::UiScriptBinding *ast); - virtual bool visit(QQmlJS::AST::UiArrayBinding *ast); - virtual bool visit(QQmlJS::AST::UiObjectMemberList *ast); - virtual bool visit(QQmlJS::AST::UiArrayMemberList *ast); - virtual bool visit(QQmlJS::AST::UiQualifiedId *ast); - - // JS - virtual bool visit(QQmlJS::AST::Program *ast); - virtual bool visit(QQmlJS::AST::SourceElements *ast); - virtual bool visit(QQmlJS::AST::FunctionSourceElement *ast); - virtual bool visit(QQmlJS::AST::StatementSourceElement *ast); - - // object literals - virtual bool visit(QQmlJS::AST::PropertyAssignmentList *ast); - virtual bool visit(QQmlJS::AST::PropertyNameAndValue *ast); - virtual bool visit(QQmlJS::AST::PropertyGetterSetter *ast); - virtual bool visit(QQmlJS::AST::IdentifierPropertyName *ast); - virtual bool visit(QQmlJS::AST::StringLiteralPropertyName *ast); - virtual bool visit(QQmlJS::AST::NumericLiteralPropertyName *ast); - - // array literals - virtual bool visit(QQmlJS::AST::ElementList *ast); - virtual bool visit(QQmlJS::AST::Elision *ast); - - // function calls - virtual bool visit(QQmlJS::AST::ArgumentList *ast); - - // expressions - virtual bool visit(QQmlJS::AST::ObjectLiteral *ast); - virtual bool visit(QQmlJS::AST::ArrayLiteral *ast); - virtual bool visit(QQmlJS::AST::ThisExpression *ast); - virtual bool visit(QQmlJS::AST::IdentifierExpression *ast); - virtual bool visit(QQmlJS::AST::NullExpression *ast); - virtual bool visit(QQmlJS::AST::TrueLiteral *ast); - virtual bool visit(QQmlJS::AST::FalseLiteral *ast); - virtual bool visit(QQmlJS::AST::StringLiteral *ast); - virtual bool visit(QQmlJS::AST::NumericLiteral *ast); - virtual bool visit(QQmlJS::AST::RegExpLiteral *ast); - virtual bool visit(QQmlJS::AST::NestedExpression *ast); - virtual bool visit(QQmlJS::AST::ArrayMemberExpression *ast); - virtual bool visit(QQmlJS::AST::FieldMemberExpression *ast); - virtual bool visit(QQmlJS::AST::NewMemberExpression *ast); - virtual bool visit(QQmlJS::AST::NewExpression *ast); - virtual bool visit(QQmlJS::AST::CallExpression *ast); - virtual bool visit(QQmlJS::AST::PostIncrementExpression *ast); - virtual bool visit(QQmlJS::AST::PostDecrementExpression *ast); - virtual bool visit(QQmlJS::AST::DeleteExpression *ast); - virtual bool visit(QQmlJS::AST::VoidExpression *ast); - virtual bool visit(QQmlJS::AST::TypeOfExpression *ast); - virtual bool visit(QQmlJS::AST::PreIncrementExpression *ast); - virtual bool visit(QQmlJS::AST::PreDecrementExpression *ast); - virtual bool visit(QQmlJS::AST::UnaryPlusExpression *ast); - virtual bool visit(QQmlJS::AST::UnaryMinusExpression *ast); - virtual bool visit(QQmlJS::AST::TildeExpression *ast); - virtual bool visit(QQmlJS::AST::NotExpression *ast); - virtual bool visit(QQmlJS::AST::BinaryExpression *ast); - virtual bool visit(QQmlJS::AST::ConditionalExpression *ast); - virtual bool visit(QQmlJS::AST::Expression *ast); - - // statements - virtual bool visit(QQmlJS::AST::Block *ast); - virtual bool visit(QQmlJS::AST::StatementList *ast); - virtual bool visit(QQmlJS::AST::VariableStatement *ast); - virtual bool visit(QQmlJS::AST::VariableDeclarationList *ast); - virtual bool visit(QQmlJS::AST::VariableDeclaration *ast); - virtual bool visit(QQmlJS::AST::EmptyStatement *ast); - virtual bool visit(QQmlJS::AST::ExpressionStatement *ast); - virtual bool visit(QQmlJS::AST::IfStatement *ast); - virtual bool visit(QQmlJS::AST::DoWhileStatement *ast); - virtual bool visit(QQmlJS::AST::WhileStatement *ast); - virtual bool visit(QQmlJS::AST::ForStatement *ast); - virtual bool visit(QQmlJS::AST::LocalForStatement *ast); - virtual bool visit(QQmlJS::AST::ForEachStatement *ast); - virtual bool visit(QQmlJS::AST::LocalForEachStatement *ast); - virtual bool visit(QQmlJS::AST::ContinueStatement *ast); - virtual bool visit(QQmlJS::AST::BreakStatement *ast); - virtual bool visit(QQmlJS::AST::ReturnStatement *ast); - virtual bool visit(QQmlJS::AST::WithStatement *ast); - virtual bool visit(QQmlJS::AST::SwitchStatement *ast); - virtual bool visit(QQmlJS::AST::CaseBlock *ast); - virtual bool visit(QQmlJS::AST::CaseClauses *ast); - virtual bool visit(QQmlJS::AST::CaseClause *ast); - virtual bool visit(QQmlJS::AST::DefaultClause *ast); - virtual bool visit(QQmlJS::AST::LabelledStatement *ast); - virtual bool visit(QQmlJS::AST::ThrowStatement *ast); - virtual bool visit(QQmlJS::AST::TryStatement *ast); - virtual bool visit(QQmlJS::AST::Catch *ast); - virtual bool visit(QQmlJS::AST::Finally *ast); - virtual bool visit(QQmlJS::AST::FunctionDeclaration *ast); - virtual bool visit(QQmlJS::AST::FunctionExpression *ast); - virtual bool visit(QQmlJS::AST::FormalParameterList *ast); - virtual bool visit(QQmlJS::AST::FunctionBody *ast); - virtual bool visit(QQmlJS::AST::DebuggerStatement *ast); - -private: - bool buildName(QList<QStringRef> &name, QQmlJS::AST::Node *node, - QList<QQmlJS::AST::ExpressionNode *> *nodes); - void discard(); - - const QV4Compiler::Expression *m_expression; - QQmlEnginePrivate *m_engine; - - QQmlJS::IR::Function *_function; - QQmlJS::IR::BasicBlock *_block; - bool _discard; - bool _invalidatable; - - ExprResult _expr; -}; - -QT_END_NAMESPACE - -#endif // QV4IRBUILDER_P_H diff --git a/src/qml/qml/v4/qv4isel_llvm.cpp b/src/qml/qml/v4/qv4isel_llvm.cpp new file mode 100644 index 0000000000..70378a4d8a --- /dev/null +++ b/src/qml/qml/v4/qv4isel_llvm.cpp @@ -0,0 +1,1388 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-parameter" +#endif // __clang__ + +#include <llvm/Analysis/Passes.h> +#include <llvm/Analysis/Verifier.h> +#include <llvm/Assembly/PrintModulePass.h> +#include <llvm/Bitcode/ReaderWriter.h> +#include <llvm/ExecutionEngine/ExecutionEngine.h> +#include <llvm/ExecutionEngine/JIT.h> +#include <llvm/ExecutionEngine/JITMemoryManager.h> +#include <llvm/Support/FormattedStream.h> +#include <llvm/Support/Host.h> +#include <llvm/Support/MemoryBuffer.h> +#include <llvm/Support/raw_ostream.h> +#include <llvm/Support/system_error.h> +#include <llvm/Support/TargetRegistry.h> +#include <llvm/Support/TargetSelect.h> +#include <llvm/Target/TargetMachine.h> +#include <llvm/Transforms/Scalar.h> +#include <llvm/Transforms/IPO.h> +#include <llvm/Linker.h> + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif // __clang__ + +#include <QtCore/QFileInfo> +#include <QtCore/QLibrary> +#include <QtCore/QStringList> +#include <QtCore/QTextStream> +#include <cstdio> +#include <iostream> + +// These includes have to come last, because WTF/Platform.h defines some macros +// with very unfriendly names that collide with class fields in LLVM. +#include "qv4isel_llvm_p.h" +#include "qv4_llvm_p.h" +#include "qv4jsir_p.h" +#include "qv4string_p.h" +#include "qv4global_p.h" + +namespace QQmlJS { + +Q_QML_EXPORT int compileWithLLVM(V4IR::Module *module, const QString &fileName, LLVMOutputType outputType, int (*exec)(void *)) +{ + Q_ASSERT(module); + Q_ASSERT(exec || outputType != LLVMOutputJit); + + // TODO: should this be done here? + LLVMInitializeX86TargetInfo(); + LLVMInitializeX86Target(); + LLVMInitializeX86AsmPrinter(); + LLVMInitializeX86AsmParser(); + LLVMInitializeX86Disassembler(); + LLVMInitializeX86TargetMC(); + + //---- + + llvm::InitializeNativeTarget(); + LLVM::InstructionSelection llvmIsel(llvm::getGlobalContext()); + + const QString moduleName = QFileInfo(fileName).fileName(); + llvm::StringRef moduleId(moduleName.toUtf8().constData()); + llvm::Module *llvmModule = new llvm::Module(moduleId, llvmIsel.getContext()); + + if (outputType == LLVMOutputJit) { + // The execution engine takes ownership of the model. No need to delete it anymore. + std::string errStr; + llvm::ExecutionEngine *execEngine = llvm::EngineBuilder(llvmModule) +// .setUseMCJIT(true) + .setErrorStr(&errStr).create(); + if (!execEngine) { + std::cerr << "Could not create LLVM JIT: " << errStr << std::endl; + return EXIT_FAILURE; + } + + llvm::FunctionPassManager functionPassManager(llvmModule); + // Set up the optimizer pipeline. Start with registering info about how the + // target lays out data structures. + functionPassManager.add(new llvm::DataLayout(*execEngine->getDataLayout())); + // Promote allocas to registers. + functionPassManager.add(llvm::createPromoteMemoryToRegisterPass()); + // Provide basic AliasAnalysis support for GVN. + functionPassManager.add(llvm::createBasicAliasAnalysisPass()); + // Do simple "peephole" optimizations and bit-twiddling optzns. + functionPassManager.add(llvm::createInstructionCombiningPass()); + // Reassociate expressions. + functionPassManager.add(llvm::createReassociatePass()); + // Eliminate Common SubExpressions. + functionPassManager.add(llvm::createGVNPass()); + // Simplify the control flow graph (deleting unreachable blocks, etc). + functionPassManager.add(llvm::createCFGSimplificationPass()); + + functionPassManager.doInitialization(); + + llvmIsel.buildLLVMModule(module, llvmModule, &functionPassManager); + + llvm::Function *entryPoint = llvmModule->getFunction("%entry"); + Q_ASSERT(entryPoint); + void *funcPtr = execEngine->getPointerToFunction(entryPoint); + return exec(funcPtr); + } else { + llvm::FunctionPassManager functionPassManager(llvmModule); + // Set up the optimizer pipeline. + // Promote allocas to registers. + functionPassManager.add(llvm::createPromoteMemoryToRegisterPass()); + // Provide basic AliasAnalysis support for GVN. + functionPassManager.add(llvm::createBasicAliasAnalysisPass()); + // Do simple "peephole" optimizations and bit-twiddling optzns. + functionPassManager.add(llvm::createInstructionCombiningPass()); + // Reassociate expressions. + functionPassManager.add(llvm::createReassociatePass()); + // Eliminate Common SubExpressions. + functionPassManager.add(llvm::createGVNPass()); + // Simplify the control flow graph (deleting unreachable blocks, etc). + functionPassManager.add(llvm::createCFGSimplificationPass()); + + functionPassManager.doInitialization(); + + llvmIsel.buildLLVMModule(module, llvmModule, &functionPassManager); + + // TODO: if output type is .ll, print the module to file + + const std::string triple = llvm::sys::getDefaultTargetTriple(); + + std::string err; + const llvm::Target *target = llvm::TargetRegistry::lookupTarget(triple, err); + if (! err.empty()) { + std::cerr << err << ", triple: " << triple << std::endl; + assert(!"cannot create target for the host triple"); + } + + std::string cpu; + std::string features; + llvm::TargetOptions options; + llvm::TargetMachine *targetMachine = target->createTargetMachine(triple, cpu, features, options, llvm::Reloc::PIC_); + assert(targetMachine); + + llvm::TargetMachine::CodeGenFileType ft; + QString ofName; + + if (outputType == LLVMOutputObject) { + ft = llvm::TargetMachine::CGFT_ObjectFile; + ofName = fileName + QLatin1String(".o"); + } else if (outputType == LLVMOutputAssembler) { + ft = llvm::TargetMachine::CGFT_AssemblyFile; + ofName = fileName + QLatin1String(".s"); + } else { + // ft is not used. + ofName = fileName + QLatin1String(".ll"); + } + + llvm::raw_fd_ostream dest(ofName.toUtf8().constData(), err, llvm::raw_fd_ostream::F_Binary); + llvm::formatted_raw_ostream destf(dest); + if (!err.empty()) { + std::cerr << err << std::endl; + delete llvmModule; + } + + llvm::PassManager globalPassManager; + globalPassManager.add(llvm::createScalarReplAggregatesPass()); + globalPassManager.add(llvm::createInstructionCombiningPass()); + globalPassManager.add(llvm::createGlobalOptimizerPass()); + globalPassManager.add(llvm::createFunctionInliningPass(25)); +// globalPassManager.add(llvm::createFunctionInliningPass(125)); + + if (outputType == LLVMOutputObject || outputType == LLVMOutputAssembler) { + if (targetMachine->addPassesToEmitFile(globalPassManager, destf, ft)) { + std::cerr << err << " (probably no DataLayout in TargetMachine)" << std::endl; + } else { + globalPassManager.run(*llvmModule); + + destf.flush(); + dest.flush(); + } + } else { // .ll + globalPassManager.run(*llvmModule); + llvmModule->print(destf, 0); + + destf.flush(); + dest.flush(); + } + + delete llvmModule; + return EXIT_SUCCESS; + } +} + +} // QQmlJS + +using namespace QQmlJS; +using namespace QQmlJS::LLVM; + +namespace { +QTextStream qerr(stderr, QIODevice::WriteOnly); +} + +InstructionSelection::InstructionSelection(llvm::LLVMContext &context) + : llvm::IRBuilder<>(context) + , _llvmModule(0) + , _llvmFunction(0) + , _llvmValue(0) + , _numberTy(0) + , _valueTy(0) + , _contextPtrTy(0) + , _stringPtrTy(0) + , _functionTy(0) + , _allocaInsertPoint(0) + , _function(0) + , _block(0) + , _fpm(0) +{ +} + +void InstructionSelection::buildLLVMModule(V4IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm) +{ + qSwap(_llvmModule, llvmModule); + qSwap(_fpm, fpm); + + _numberTy = getDoubleTy(); + + std::string err; + + llvm::OwningPtr<llvm::MemoryBuffer> buffer; + qDebug()<<"llvm runtime:"<<LLVM_RUNTIME; + llvm::error_code ec = llvm::MemoryBuffer::getFile(llvm::StringRef(LLVM_RUNTIME), buffer); + if (ec) { + qWarning() << ec.message().c_str(); + assert(!"cannot load QML/JS LLVM runtime, you can generate the runtime with the command `make llvm_runtime'"); + } + + llvm::Module *llvmRuntime = llvm::getLazyBitcodeModule(buffer.get(), getContext(), &err); + if (! err.empty()) { + qWarning() << err.c_str(); + assert(!"cannot load QML/JS LLVM runtime"); + } + + err.clear(); + llvm::Linker::LinkModules(_llvmModule, llvmRuntime, llvm::Linker::DestroySource, &err); + if (! err.empty()) { + qWarning() << err.c_str(); + assert(!"cannot link the QML/JS LLVM runtime"); + } + + _valueTy = _llvmModule->getTypeByName("struct.QV4::Value"); + _contextPtrTy = _llvmModule->getTypeByName("struct.QV4::ExecutionContext")->getPointerTo(); + _stringPtrTy = _llvmModule->getTypeByName("struct.QV4::String")->getPointerTo(); + + { + llvm::Type *args[] = { _contextPtrTy }; + _functionTy = llvm::FunctionType::get(getVoidTy(), llvm::makeArrayRef(args), false); + } + + + foreach (V4IR::Function *function, module->functions) + (void) compileLLVMFunction(function); + qSwap(_fpm, fpm); + qSwap(_llvmModule, llvmModule); +} + +void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinTypeofName(const QString &name, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteName(const QString &name, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinThrow(V4IR::Temp *arg, int line) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinCreateExceptionHandler(V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinFinishTry() +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPushWithScope(V4IR::Temp *arg) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinPopScope() +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +{ + llvm::ConstantInt *isDeletable = getInt1(deletable != 0); + llvm::Value *varName = getIdentifier(name); + CreateCall3(getRuntimeFunction("__qmljs_builtin_declare_var"), + _llvmFunction->arg_begin(), isDeletable, varName); +} + +void InstructionSelection::callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::constructActivationProperty(V4IR::Name *func, + V4IR::ExprList *args, + V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::loadThisObject(V4IR::Temp *temp) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::loadConst(V4IR::Const *con, V4IR::Temp *temp) +{ + llvm::Value *target = getLLVMTemp(temp); + llvm::Value *source = CreateLoad(createValue(con)); + CreateStore(source, target); +} + +void InstructionSelection::loadString(const QString &str, V4IR::Temp *targetTemp) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +void InstructionSelection::setActivationProperty(V4IR::Temp *source, const QString &targetName) +{ + llvm::Value *name = getIdentifier(targetName); + llvm::Value *src = toValuePtr(source); + CreateCall3(getRuntimeFunction("__qmljs_llvm_set_activation_property"), + _llvmFunction->arg_begin(), name, src); +} + +void InstructionSelection::initClosure(V4IR::Closure *closure, V4IR::Temp *target) +{ + V4IR::Function *f = closure->value; + QString name; + if (f->name) + name = *f->name; + + llvm::Value *args[] = { + _llvmFunction->arg_begin(), + getLLVMTemp(target), + getIdentifier(name), + getInt1(f->hasDirectEval), + getInt1(f->usesArgumentsObject), + getInt1(f->isStrict), + getInt1(!f->nestedFunctions.isEmpty()), + genStringList(f->formals, "formals", "formal"), + getInt32(f->formals.size()), + genStringList(f->locals, "locals", "local"), + getInt32(f->locals.size()) + }; + llvm::Function *callee = _llvmModule->getFunction("__qmljs_llvm_init_closure"); + CreateCall(callee, args); +} + +void InstructionSelection::getProperty(V4IR::Temp *sourceBase, const QString &sourceName, V4IR::Temp *target) +{ + llvm::Value *base = getLLVMTempReference(sourceBase); + llvm::Value *name = getIdentifier(sourceName); + llvm::Value *t = getLLVMTemp(target); + CreateCall4(getRuntimeFunction("__qmljs_llvm_get_property"), + _llvmFunction->arg_begin(), t, base, name); +} + +void InstructionSelection::setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + llvm::Value *base = getLLVMTempReference(targetBase); + llvm::Value *name = getIdentifier(targetName); + llvm::Value *src = toValuePtr(source); + CreateCall4(getRuntimeFunction("__qmljs_llvm_set_property"), + _llvmFunction->arg_begin(), base, name, src); +} + +void InstructionSelection::getElement(V4IR::Temp *sourceBase, V4IR::Temp *sourceIndex, V4IR::Temp *target) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); + + llvm::Value *base = getLLVMTempReference(sourceBase); + llvm::Value *index = getLLVMTempReference(sourceIndex); + llvm::Value *t = getLLVMTemp(target); + CreateCall4(getRuntimeFunction("__qmljs_llvm_get_element"), + _llvmFunction->arg_begin(), t, base, index); +} + +void InstructionSelection::setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex) +{ + llvm::Value *base = getLLVMTempReference(targetBase); + llvm::Value *index = getLLVMTempReference(targetIndex); + llvm::Value *src = toValuePtr(source); + CreateCall4(getRuntimeFunction("__qmljs_llvm_set_element"), + _llvmFunction->arg_begin(), base, index, src); +} + +void InstructionSelection::copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + llvm::Value *t = getLLVMTemp(targetTemp); + llvm::Value *s = getLLVMTemp(sourceTemp); + CreateStore(s, t); +} + +void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + const char *opName = 0; + switch (oper) { + case V4IR::OpNot: opName = "__qmljs_not"; break; + case V4IR::OpUMinus: opName = "__qmljs_uminus"; break; + case V4IR::OpUPlus: opName = "__qmljs_uplus"; break; + case V4IR::OpCompl: opName = "__qmljs_compl"; break; + case V4IR::OpIncrement: opName = "__qmljs_increment"; break; + case V4IR::OpDecrement: opName = "__qmljs_decrement"; break; + default: assert(!"unreachable"); break; + } + + if (opName) { + llvm::Value *t = getLLVMTemp(targetTemp); + llvm::Value *s = getLLVMTemp(sourceTemp); + CreateCall3(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), t, s); + } +} + +void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target) +{ + const char *opName = 0; + switch (oper) { + case V4IR::OpBitAnd: opName = "__qmljs_llvm_bit_and"; break; + case V4IR::OpBitOr: opName = "__qmljs_llvm_bit_or"; break; + case V4IR::OpBitXor: opName = "__qmljs_llvm_bit_xor"; break; + case V4IR::OpAdd: opName = "__qmljs_llvm_add"; break; + case V4IR::OpSub: opName = "__qmljs_llvm_sub"; break; + case V4IR::OpMul: opName = "__qmljs_llvm_mul"; break; + case V4IR::OpDiv: opName = "__qmljs_llvm_div"; break; + case V4IR::OpMod: opName = "__qmljs_llvm_mod"; break; + case V4IR::OpLShift: opName = "__qmljs_llvm_shl"; break; + case V4IR::OpRShift: opName = "__qmljs_llvm_shr"; break; + case V4IR::OpURShift: opName = "__qmljs_llvm_ushr"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *t = getLLVMTemp(target); + llvm::Value *s1 = toValuePtr(leftSource); + llvm::Value *s2 = toValuePtr(rightSource); + CreateCall4(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), t, s1, s2); + return; + } +} + +void InstructionSelection::inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName) +{ + const char *opName = 0; + switch (oper) { + case V4IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_name"; break; + case V4IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_name"; break; + case V4IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_name"; break; + case V4IR::OpAdd: opName = "__qmljs_llvm_inplace_add_name"; break; + case V4IR::OpSub: opName = "__qmljs_llvm_inplace_sub_name"; break; + case V4IR::OpMul: opName = "__qmljs_llvm_inplace_mul_name"; break; + case V4IR::OpDiv: opName = "__qmljs_llvm_inplace_div_name"; break; + case V4IR::OpMod: opName = "__qmljs_llvm_inplace_mod_name"; break; + case V4IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_name"; break; + case V4IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_name"; break; + case V4IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_name"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *dst = getIdentifier(targetName); + llvm::Value *src = toValuePtr(rightSource); + CreateCall3(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), dst, src); + return; + } +} + +void InstructionSelection::inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp) +{ + const char *opName = 0; + switch (oper) { + case V4IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_element"; break; + case V4IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_element"; break; + case V4IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_element"; break; + case V4IR::OpAdd: opName = "__qmljs_llvm_inplace_add_element"; break; + case V4IR::OpSub: opName = "__qmljs_llvm_inplace_sub_element"; break; + case V4IR::OpMul: opName = "__qmljs_llvm_inplace_mul_element"; break; + case V4IR::OpDiv: opName = "__qmljs_llvm_inplace_div_element"; break; + case V4IR::OpMod: opName = "__qmljs_llvm_inplace_mod_element"; break; + case V4IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_element"; break; + case V4IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_element"; break; + case V4IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_element"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *base = getLLVMTemp(targetBaseTemp); + llvm::Value *index = getLLVMTemp(targetIndexTemp); + llvm::Value *value = toValuePtr(source); + CreateCall4(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), base, index, value); + } +} + +void InstructionSelection::inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + const char *opName = 0; + switch (oper) { + case V4IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_member"; break; + case V4IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_member"; break; + case V4IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_member"; break; + case V4IR::OpAdd: opName = "__qmljs_llvm_inplace_add_member"; break; + case V4IR::OpSub: opName = "__qmljs_llvm_inplace_sub_member"; break; + case V4IR::OpMul: opName = "__qmljs_llvm_inplace_mul_member"; break; + case V4IR::OpDiv: opName = "__qmljs_llvm_inplace_div_member"; break; + case V4IR::OpMod: opName = "__qmljs_llvm_inplace_mod_member"; break; + case V4IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_member"; break; + case V4IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_member"; break; + case V4IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_member"; break; + default: + Q_UNREACHABLE(); + break; + } + + if (opName) { + llvm::Value *base = getLLVMTemp(targetBase); + llvm::Value *member = getIdentifier(targetName); + llvm::Value *value = toValuePtr(source); + CreateCall4(getRuntimeFunction(opName), + _llvmFunction->arg_begin(), value, base, member); + } +} + +llvm::Function *InstructionSelection::getLLVMFunction(V4IR::Function *function) +{ + llvm::Function *&f = _functionMap[function]; + if (! f) { + QString name = QStringLiteral("__qmljs_native_"); + if (function->name) { + if (*function->name == QStringLiteral("%entry")) + name = *function->name; + else + name += *function->name; + } + f = llvm::Function::Create(_functionTy, llvm::Function::ExternalLinkage, // ### make it internal + qPrintable(name), _llvmModule); + } + return f; +} + +llvm::Function *InstructionSelection::compileLLVMFunction(V4IR::Function *function) +{ + llvm::Function *llvmFunction = getLLVMFunction(function); + + QHash<V4IR::BasicBlock *, llvm::BasicBlock *> blockMap; + QVector<llvm::Value *> tempMap; + + qSwap(_llvmFunction, llvmFunction); + qSwap(_function, function); + qSwap(_tempMap, tempMap); + qSwap(_blockMap, blockMap); + + // create the LLVM blocks + foreach (V4IR::BasicBlock *block, _function->basicBlocks) + (void) getLLVMBasicBlock(block); + + // entry block + SetInsertPoint(getLLVMBasicBlock(_function->basicBlocks.first())); + + llvm::Instruction *allocaInsertPoint = new llvm::BitCastInst(llvm::UndefValue::get(getInt32Ty()), + getInt32Ty(), "", GetInsertBlock()); + qSwap(_allocaInsertPoint, allocaInsertPoint); + + for (int i = 0; i < _function->tempCount; ++i) { + llvm::AllocaInst *t = newLLVMTemp(_valueTy); + _tempMap.append(t); + } + + foreach (llvm::Value *t, _tempMap) { + CreateStore(llvm::Constant::getNullValue(_valueTy), t); + } + +// CreateCall(getRuntimeFunction("__qmljs_llvm_init_this_object"), +// _llvmFunction->arg_begin()); + + foreach (V4IR::BasicBlock *block, _function->basicBlocks) { + qSwap(_block, block); + SetInsertPoint(getLLVMBasicBlock(_block)); + foreach (V4IR::Stmt *s, _block->statements) + s->accept(this); + qSwap(_block, block); + } + + qSwap(_allocaInsertPoint, allocaInsertPoint); + + allocaInsertPoint->eraseFromParent(); + + qSwap(_blockMap, blockMap); + qSwap(_tempMap, tempMap); + qSwap(_function, function); + qSwap(_llvmFunction, llvmFunction); + + // Validate the generated code, checking for consistency. + llvm::verifyFunction(*llvmFunction); + // Optimize the function. + if (_fpm) + _fpm->run(*llvmFunction); + + return llvmFunction; +} + +llvm::BasicBlock *InstructionSelection::getLLVMBasicBlock(V4IR::BasicBlock *block) +{ + llvm::BasicBlock *&llvmBlock = _blockMap[block]; + if (! llvmBlock) + llvmBlock = llvm::BasicBlock::Create(getContext(), llvm::Twine(), + _llvmFunction); + return llvmBlock; +} + +llvm::Value *InstructionSelection::getLLVMTempReference(V4IR::Expr *expr) +{ + if (V4IR::Temp *t = expr->asTemp()) + return getLLVMTemp(t); + + assert(!"TODO!"); + llvm::Value *addr = newLLVMTemp(_valueTy); +// CreateStore(getLLVMValue(expr), addr); + return addr; +} + +llvm::Value *InstructionSelection::getLLVMCondition(V4IR::Expr *expr) +{ + llvm::Value *value = 0; + if (V4IR::Temp *t = expr->asTemp()) { + value = getLLVMTemp(t); + } else { + assert(!"TODO!"); + Q_UNREACHABLE(); + +#if 0 + value = getLLVMValue(expr); + if (! value) { + Q_UNIMPLEMENTED(); + return getInt1(false); + } + + llvm::Value *tmp = newLLVMTemp(_valueTy); + CreateStore(value, tmp); + value = tmp; +#endif + } + + return CreateCall2(getRuntimeFunction("__qmljs_llvm_to_boolean"), + _llvmFunction->arg_begin(), + value); +} + +llvm::Value *InstructionSelection::getLLVMTemp(V4IR::Temp *temp) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); + return 0; +#if 0 + if (temp->idx < 0) { + const int index = -temp->idx -1; + return CreateCall2(getRuntimeFunction("__qmljs_llvm_get_argument"), + _llvmFunction->arg_begin(), getInt32(index)); + } + + return _tempMap[temp->idx]; +#endif +} + +llvm::Value *InstructionSelection::getStringPtr(const QString &s) +{ + llvm::Value *&value = _stringMap[s]; + if (! value) { + const QByteArray bytes = s.toUtf8(); + value = CreateGlobalStringPtr(llvm::StringRef(bytes.constData(), bytes.size())); + _stringMap[s] = value; + } + return value; +} + +llvm::Value *InstructionSelection::getIdentifier(const QString &s) +{ + llvm::Value *str = getStringPtr(s); + llvm::Value *id = CreateCall2(getRuntimeFunction("__qmljs_identifier_from_utf8"), + _llvmFunction->arg_begin(), str); + return id; +} + +void InstructionSelection::visitJump(V4IR::Jump *s) +{ + CreateBr(getLLVMBasicBlock(s->target)); +} + +void InstructionSelection::visitCJump(V4IR::CJump *s) +{ + CreateCondBr(getLLVMCondition(s->cond), + getLLVMBasicBlock(s->iftrue), + getLLVMBasicBlock(s->iffalse)); +} + +void InstructionSelection::visitRet(V4IR::Ret *s) +{ + V4IR::Temp *t = s->expr->asTemp(); + assert(t != 0); + llvm::Value *result = getLLVMTemp(t); + llvm::Value *ctx = _llvmFunction->arg_begin(); + CreateCall2(getRuntimeFunction("__qmljs_llvm_return"), ctx, result); + CreateRetVoid(); +} + +void InstructionSelection::visitTry(V4IR::Try *) +{ + // TODO + assert(!"TODO!"); + Q_UNREACHABLE(); +} + +#if 0 +void InstructionSelection::visitString(V4IR::String *e) +{ + llvm::Value *tmp = newLLVMTemp(_valueTy); + CreateCall3(getRuntimeFunction("__qmljs_llvm_init_string"), + _llvmFunction->arg_begin(), tmp, + getStringPtr(*e->value)); + _llvmValue = CreateLoad(tmp); +} +#endif + +llvm::AllocaInst *InstructionSelection::newLLVMTemp(llvm::Type *type, llvm::Value *size) +{ + llvm::AllocaInst *addr = new llvm::AllocaInst(type, size, llvm::Twine(), _allocaInsertPoint); + return addr; +} + +llvm::Value * InstructionSelection::genArguments(V4IR::ExprList *exprs, int &argc) +{ + llvm::Value *args = 0; + + argc = 0; + for (V4IR::ExprList *it = exprs; it; it = it->next) + ++argc; + + if (argc) + args = newLLVMTemp(_valueTy, getInt32(argc)); + else + args = llvm::Constant::getNullValue(_valueTy->getPointerTo()); + + int i = 0; + for (V4IR::ExprList *it = exprs; it; it = it->next) { +// llvm::Value *arg = getLLVMValue(it->expr); +// CreateStore(arg, CreateConstGEP1_32(args, i++)); + } + + return args; +} + +void InstructionSelection::genCallMember(V4IR::Call *e, llvm::Value *result) +{ + if (! result) + result = newLLVMTemp(_valueTy); + + V4IR::Member *m = e->base->asMember(); + llvm::Value *thisObject = getLLVMTemp(m->base->asTemp()); + llvm::Value *name = getIdentifier(*m->name); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *actuals[] = { + _llvmFunction->arg_begin(), + result, + thisObject, + name, + args, + getInt32(argc) + }; + + CreateCall(getRuntimeFunction("__qmljs_llvm_call_property"), llvm::ArrayRef<llvm::Value *>(actuals)); + _llvmValue = CreateLoad(result); +} + +void InstructionSelection::genConstructMember(V4IR::New *e, llvm::Value *result) +{ + if (! result) + result = newLLVMTemp(_valueTy); + + V4IR::Member *m = e->base->asMember(); + llvm::Value *thisObject = getLLVMTemp(m->base->asTemp()); + llvm::Value *name = getIdentifier(*m->name); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *actuals[] = { + _llvmFunction->arg_begin(), + result, + thisObject, + name, + args, + getInt32(argc) + }; + + CreateCall(getRuntimeFunction("__qmljs_llvm_construct_property"), llvm::ArrayRef<llvm::Value *>(actuals)); + _llvmValue = CreateLoad(result); +} + +void InstructionSelection::genCallTemp(V4IR::Call *e, llvm::Value *result) +{ + if (! result) + result = newLLVMTemp(_valueTy); + + llvm::Value *func = getLLVMTempReference(e->base); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *thisObject = llvm::Constant::getNullValue(_valueTy->getPointerTo()); + + llvm::Value *actuals[] = { + _llvmFunction->arg_begin(), + result, + thisObject, + func, + args, + getInt32(argc) + }; + + CreateCall(getRuntimeFunction("__qmljs_llvm_call_value"), actuals); + + _llvmValue = CreateLoad(result); +} + +void InstructionSelection::genConstructTemp(V4IR::New *e, llvm::Value *result) +{ + if (! result) + result = newLLVMTemp(_valueTy); + + llvm::Value *func = getLLVMTempReference(e->base); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *actuals[] = { + _llvmFunction->arg_begin(), + result, + func, + args, + getInt32(argc) + }; + + CreateCall(getRuntimeFunction("__qmljs_llvm_construct_value"), actuals); + + _llvmValue = CreateLoad(result); +} + +void InstructionSelection::genCallName(V4IR::Call *e, llvm::Value *result) +{ + V4IR::Name *base = e->base->asName(); + + if (! result) + result = newLLVMTemp(_valueTy); + + if (! base->id) { + switch (base->builtin) { + case V4IR::Name::builtin_invalid: + break; + + case V4IR::Name::builtin_typeof: + CreateCall3(getRuntimeFunction("__qmljs_llvm_typeof"), + _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr)); + _llvmValue = CreateLoad(result); + return; + + case V4IR::Name::builtin_throw: + CreateCall2(getRuntimeFunction("__qmljs_llvm_throw"), + _llvmFunction->arg_begin(), getLLVMTempReference(e->args->expr)); + _llvmValue = llvm::UndefValue::get(_valueTy); + return; + + case V4IR::Name::builtin_finish_try: + // ### FIXME. + return; + + case V4IR::Name::builtin_foreach_iterator_object: + CreateCall3(getRuntimeFunction("__qmljs_llvm_foreach_iterator_object"), + _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr)); + _llvmValue = CreateLoad(result); + return; + + case V4IR::Name::builtin_foreach_next_property_name: + CreateCall2(getRuntimeFunction("__qmljs_llvm_foreach_next_property_name"), + result, getLLVMTempReference(e->args->expr)); + _llvmValue = CreateLoad(result); + return; + + case V4IR::Name::builtin_delete: { + if (V4IR::Subscript *subscript = e->args->expr->asSubscript()) { + CreateCall4(getRuntimeFunction("__qmljs_llvm_delete_subscript"), + _llvmFunction->arg_begin(), + result, + getLLVMTempReference(subscript->base), + getLLVMTempReference(subscript->index)); + _llvmValue = CreateLoad(result); + return; + } else if (V4IR::Member *member = e->args->expr->asMember()) { + CreateCall4(getRuntimeFunction("__qmljs_llvm_delete_member"), + _llvmFunction->arg_begin(), + result, + getLLVMTempReference(member->base), + getIdentifier(*member->name)); + _llvmValue = CreateLoad(result); + return; + } else if (V4IR::Name *name = e->args->expr->asName()) { + CreateCall3(getRuntimeFunction("__qmljs_llvm_delete_property"), + _llvmFunction->arg_begin(), + result, + getIdentifier(*name->id)); + _llvmValue = CreateLoad(result); + return; + } else { + CreateCall3(getRuntimeFunction("__qmljs_llvm_delete_value"), + _llvmFunction->arg_begin(), + result, + getLLVMTempReference(e->args->expr)); + _llvmValue = CreateLoad(result); + return; + } + } break; + + default: + Q_UNREACHABLE(); + } + } else { + llvm::Value *name = getIdentifier(*base->id); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + CreateCall5(getRuntimeFunction("__qmljs_llvm_call_activation_property"), + _llvmFunction->arg_begin(), result, name, args, getInt32(argc)); + + _llvmValue = CreateLoad(result); + } +} + +void InstructionSelection::genConstructName(V4IR::New *e, llvm::Value *result) +{ + V4IR::Name *base = e->base->asName(); + + if (! result) + result = newLLVMTemp(_valueTy); + + if (! base->id) { + Q_UNREACHABLE(); + } else { + llvm::Value *name = getIdentifier(*base->id); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + CreateCall5(getRuntimeFunction("__qmljs_llvm_construct_activation_property"), + _llvmFunction->arg_begin(), result, name, args, getInt32(argc)); + + _llvmValue = CreateLoad(result); + } +} + +#if 0 +void InstructionSelection::visitCall(V4IR::Call *e) +{ + if (e->base->asMember()) { + genCallMember(e); + } else if (e->base->asTemp()) { + genCallTemp(e); + } else if (e->base->asName()) { + genCallName(e); + } else if (V4IR::Temp *t = e->base->asTemp()) { + llvm::Value *base = getLLVMTemp(t); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *result = newLLVMTemp(_valueTy); + CreateStore(llvm::Constant::getNullValue(_valueTy), result); + CreateCall5(getRuntimeFunction("__qmljs_llvm_call_value"), + _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); + _llvmValue = CreateLoad(result); + } else { + Q_UNIMPLEMENTED(); + } +} +#endif + +#if 0 +void InstructionSelection::visitNew(V4IR::New *e) +{ + if (e->base->asMember()) { + genConstructMember(e); + } else if (e->base->asTemp()) { + genConstructTemp(e); + } else if (e->base->asName()) { + genConstructName(e); + } else if (V4IR::Temp *t = e->base->asTemp()) { + llvm::Value *base = getLLVMTemp(t); + + int argc = 0; + llvm::Value *args = genArguments(e->args, argc); + + llvm::Value *result = newLLVMTemp(_valueTy); + CreateStore(llvm::Constant::getNullValue(_valueTy), result); + CreateCall5(getRuntimeFunction("__qmljs_llvm_construct_value"), + _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); + _llvmValue = CreateLoad(result); + } else { + Q_UNIMPLEMENTED(); + } +} +#endif + +#if 0 +void InstructionSelection::visitSubscript(V4IR::Subscript *e) +{ + llvm::Value *result = newLLVMTemp(_valueTy); + llvm::Value *base = getLLVMTempReference(e->base); + llvm::Value *index = getLLVMTempReference(e->index); + CreateCall4(getRuntimeFunction("__qmljs_llvm_get_element"), + _llvmFunction->arg_begin(), result, base, index); + _llvmValue = CreateLoad(result); +} +#endif + +#if 0 +void InstructionSelection::visitMember(V4IR::Member *e) +{ + llvm::Value *result = newLLVMTemp(_valueTy); + llvm::Value *base = getLLVMTempReference(e->base); + llvm::Value *name = getIdentifier(*e->name); + + CreateCall4(getRuntimeFunction("__qmljs_llvm_get_property"), + _llvmFunction->arg_begin(), result, base, name); + _llvmValue = CreateLoad(result); +} +#endif + +llvm::Function *InstructionSelection::getRuntimeFunction(llvm::StringRef str) +{ + llvm::Function *func = _llvmModule->getFunction(str); + if (!func) { + std::cerr << "Cannot find runtime function \"" + << str.str() << "\"!" << std::endl; + assert(func); + } + return func; +} + +llvm::Value *InstructionSelection::createValue(V4IR::Const *e) +{ + llvm::Value *tmp = newLLVMTemp(_valueTy); + + switch (e->type) { + case V4IR::UndefinedType: + CreateCall(getRuntimeFunction("__qmljs_llvm_init_undefined"), tmp); + break; + + case V4IR::NullType: + CreateCall(getRuntimeFunction("__qmljs_llvm_init_null"), tmp); + break; + + case V4IR::BoolType: + CreateCall2(getRuntimeFunction("__qmljs_llvm_init_boolean"), tmp, + getInt1(e->value ? 1 : 0)); + break; + + case V4IR::NumberType: + CreateCall2(getRuntimeFunction("__qmljs_llvm_init_number"), tmp, + llvm::ConstantFP::get(_numberTy, e->value)); + break; + + default: + Q_UNREACHABLE(); + } + + return tmp; +} + +llvm::Value *InstructionSelection::toValuePtr(V4IR::Expr *e) +{ + if (V4IR::Temp *t = e->asTemp()) { + return getLLVMTemp(t); + } else if (V4IR::Const *c = e->asConst()) { + return createValue(c); + } else { + Q_UNREACHABLE(); + } +} + +llvm::Value *InstructionSelection::genStringList(const QList<const QString *> &strings, const char *arrayName, const char *elementName) +{ + llvm::Value *array = CreateAlloca(_stringPtrTy, getInt32(strings.size()), + arrayName); + for (int i = 0, ei = strings.size(); i < ei; ++i) { + llvm::Value *el; + if (const QString *string = strings.at(i)) + el = getIdentifier(*string); + else + el = llvm::Constant::getNullValue(_stringPtrTy); + llvm::Value *ptr = CreateGEP(array, getInt32(i), elementName); + CreateStore(el, ptr); + } + + return array; +} diff --git a/src/qml/qml/v4/qv4isel_llvm_p.h b/src/qml/qml/v4/qv4isel_llvm_p.h new file mode 100644 index 0000000000..ab0bcb0e23 --- /dev/null +++ b/src/qml/qml/v4/qv4isel_llvm_p.h @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ISEL_LLVM_P_H +#define QV4ISEL_LLVM_P_H + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-parameter" +#endif // __clang__ + +#include <llvm/Module.h> +#include <llvm/PassManager.h> +#include <llvm/IRBuilder.h> + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif // __clang__ + +#include "qv4isel_p.h" +#include "qv4jsir_p.h" + +namespace QQmlJS { +namespace LLVM { + +class InstructionSelection: + public llvm::IRBuilder<>, + public V4IR::InstructionSelection +{ +public: + InstructionSelection(llvm::LLVMContext &context); + + void buildLLVMModule(V4IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm); + +public: // methods from InstructionSelection: + virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteValue(V4IR::Temp *result); + virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinThrow(V4IR::Temp *arg, int line); + virtual void callBuiltinCreateExceptionHandler(V4IR::Temp *result); + virtual void callBuiltinFinishTry(); + virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinPushWithScope(V4IR::Temp *arg); + virtual void callBuiltinPopScope(); + virtual void callBuiltinDeclareVar(bool deletable, const QString &name); + virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter); + virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value); + virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args); + virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args); + virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + virtual void loadThisObject(V4IR::Temp *temp); + virtual void loadConst(V4IR::Const *con, V4IR::Temp *temp); + virtual void loadString(const QString &str, V4IR::Temp *targetTemp); + virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp); + virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp); + virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName); + virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target); + virtual void getProperty(V4IR::Temp *sourceBase, const QString &sourceName, V4IR::Temp *target); + virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + virtual void getElement(V4IR::Temp *sourceBase, V4IR::Temp *sourceIndex, V4IR::Temp *target); + virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex); + virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target); + virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName); + virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + +public: // visitor methods for StmtVisitor: + virtual void visitJump(V4IR::Jump *); + virtual void visitCJump(V4IR::CJump *); + virtual void visitRet(V4IR::Ret *); + virtual void visitTry(V4IR::Try *); + +private: + llvm::Function *getRuntimeFunction(llvm::StringRef str); + llvm::Function *getLLVMFunction(V4IR::Function *function); + llvm::Function *compileLLVMFunction(V4IR::Function *function); + llvm::BasicBlock *getLLVMBasicBlock(V4IR::BasicBlock *block); + llvm::Value *getLLVMTempReference(V4IR::Expr *expr); + llvm::Value *getLLVMCondition(V4IR::Expr *expr); + llvm::Value *getLLVMTemp(V4IR::Temp *temp); + llvm::Value *getStringPtr(const QString &s); + llvm::Value *getIdentifier(const QString &s); + llvm::AllocaInst *newLLVMTemp(llvm::Type *type, llvm::Value *size = 0); + llvm::Value * genArguments(V4IR::ExprList *args, int &argc); + void genCallTemp(V4IR::Call *e, llvm::Value *result = 0); + void genCallName(V4IR::Call *e, llvm::Value *result = 0); + void genCallMember(V4IR::Call *e, llvm::Value *result = 0); + void genConstructTemp(V4IR::New *e, llvm::Value *result = 0); + void genConstructName(V4IR::New *e, llvm::Value *result = 0); + void genConstructMember(V4IR::New *e, llvm::Value *result = 0); + llvm::Value *createValue(V4IR::Const *e); + llvm::Value *toValuePtr(V4IR::Expr *e); + llvm::Value *genStringList(const QList<const QString *> &strings, + const char *arrayName, const char *elementName); + + +private: + llvm::Module *_llvmModule; + llvm::Function *_llvmFunction; + llvm::Value *_llvmValue; + llvm::Type *_numberTy; + llvm::Type *_valueTy; + llvm::Type *_contextPtrTy; + llvm::Type *_stringPtrTy; + llvm::FunctionType *_functionTy; + llvm::Instruction *_allocaInsertPoint; + V4IR::Function *_function; + V4IR::BasicBlock *_block; + QHash<V4IR::Function *, llvm::Function *> _functionMap; + QHash<V4IR::BasicBlock *, llvm::BasicBlock *> _blockMap; + QVector<llvm::Value *> _tempMap; + QHash<QString, llvm::Value *> _stringMap; + llvm::FunctionPassManager *_fpm; +}; + +} // LLVM namespace +} // QQmlJS namespace + +#endif // QV4ISEL_LLVM_P_H diff --git a/src/qml/qml/v4/qv4isel_masm.cpp b/src/qml/qml/v4/qv4isel_masm.cpp new file mode 100644 index 0000000000..af77a7cb17 --- /dev/null +++ b/src/qml/qml/v4/qv4isel_masm.cpp @@ -0,0 +1,1450 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4isel_masm_p.h" +#include "qv4runtime_p.h" +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include "qv4regexpobject_p.h" +#include "qv4unwindhelper_p.h" +#include "qv4lookup_p.h" +#include "qv4function_p.h" +#include "qv4ssa_p.h" +#include "qv4exception_p.h" + +#include <assembler/LinkBuffer.h> +#include <WTFStubs.h> + +#include <iostream> +#include <cassert> + +#if USE(UDIS86) +# include <udis86.h> +#endif + +using namespace QQmlJS; +using namespace QQmlJS::MASM; +using namespace QV4; + +namespace { +class ConvertTemps: protected V4IR::StmtVisitor, protected V4IR::ExprVisitor +{ + int _nextFreeStackSlot; + QHash<V4IR::Temp, int> _stackSlotForTemp; + + void renumber(V4IR::Temp *t) + { + if (t->kind != V4IR::Temp::VirtualRegister) + return; + + int stackSlot = _stackSlotForTemp.value(*t, -1); + if (stackSlot == -1) { + stackSlot = _nextFreeStackSlot++; + _stackSlotForTemp[*t] = stackSlot; + } + + t->kind = V4IR::Temp::StackSlot; + t->index = stackSlot; + } + +public: + ConvertTemps() + : _nextFreeStackSlot(0) + {} + + void toStackSlots(V4IR::Function *function) + { + _stackSlotForTemp.reserve(function->tempCount); + + foreach (V4IR::BasicBlock *bb, function->basicBlocks) + foreach (V4IR::Stmt *s, bb->statements) + s->accept(this); + + function->tempCount = _nextFreeStackSlot; + } + +protected: + virtual void visitConst(V4IR::Const *) {} + virtual void visitString(V4IR::String *) {} + virtual void visitRegExp(V4IR::RegExp *) {} + virtual void visitName(V4IR::Name *) {} + virtual void visitTemp(V4IR::Temp *e) { renumber(e); } + virtual void visitClosure(V4IR::Closure *) {} + virtual void visitConvert(V4IR::Convert *e) { e->expr->accept(this); } + virtual void visitUnop(V4IR::Unop *e) { e->expr->accept(this); } + virtual void visitBinop(V4IR::Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitCall(V4IR::Call *e) { + e->base->accept(this); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + virtual void visitNew(V4IR::New *e) { + e->base->accept(this); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + virtual void visitSubscript(V4IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(V4IR::Member *e) { e->base->accept(this); } + virtual void visitExp(V4IR::Exp *s) { s->expr->accept(this); } + virtual void visitMove(V4IR::Move *s) { s->target->accept(this); s->source->accept(this); } + virtual void visitJump(V4IR::Jump *) {} + virtual void visitCJump(V4IR::CJump *s) { s->cond->accept(this); } + virtual void visitRet(V4IR::Ret *s) { s->expr->accept(this); } + virtual void visitTry(V4IR::Try *s) { s->exceptionVar->accept(this); } + virtual void visitPhi(V4IR::Phi *) { Q_UNREACHABLE(); } +}; +} // anonymous namespace + +/* Platform/Calling convention/Architecture specific section */ + +#if CPU(X86_64) +static const Assembler::RegisterID calleeSavedRegisters[] = { + // Not used: JSC::X86Registers::rbx, + // Not used: JSC::X86Registers::r10, + JSC::X86Registers::r12, // LocalsRegister + // Not used: JSC::X86Registers::r13, + JSC::X86Registers::r14 // ContextRegister + // Not used: JSC::X86Registers::r15, +}; +#endif + +#if CPU(X86) +static const Assembler::RegisterID calleeSavedRegisters[] = { + // Not used: JSC::X86Registers::ebx, + JSC::X86Registers::esi, // ContextRegister + JSC::X86Registers::edi // LocalsRegister +}; +#endif + +#if CPU(ARM) +static const Assembler::RegisterID calleeSavedRegisters[] = { + // ### FIXME: remove unused registers. + // Keep these in reverse order and make sure to also edit the unwind program in + // qv4unwindhelper_p-arm.h when changing this list. + JSC::ARMRegisters::r12, + JSC::ARMRegisters::r10, + JSC::ARMRegisters::r9, + JSC::ARMRegisters::r8, + JSC::ARMRegisters::r7, + JSC::ARMRegisters::r6, + JSC::ARMRegisters::r5, + JSC::ARMRegisters::r4 +}; +#endif + +const int Assembler::calleeSavedRegisterCount = sizeof(calleeSavedRegisters) / sizeof(calleeSavedRegisters[0]); + +/* End of platform/calling convention/architecture specific section */ + + +const Assembler::VoidType Assembler::Void; + +Assembler::Assembler(V4IR::Function* function, QV4::Function *vmFunction, QV4::ExecutionEngine *engine) + : _function(function), _vmFunction(vmFunction), _engine(engine), _nextBlock(0) +{ +} + +void Assembler::registerBlock(V4IR::BasicBlock* block, V4IR::BasicBlock *nextBlock) +{ + _addrs[block] = label(); + _nextBlock = nextBlock; +} + +void Assembler::jumpToBlock(V4IR::BasicBlock* current, V4IR::BasicBlock *target) +{ + if (target != _nextBlock) + _patches[target].append(jump()); +} + +void Assembler::addPatch(V4IR::BasicBlock* targetBlock, Jump targetJump) +{ + _patches[targetBlock].append(targetJump); +} + +void Assembler::addPatch(DataLabelPtr patch, Label target) +{ + DataLabelPatch p; + p.dataLabel = patch; + p.target = target; + _dataLabelPatches.append(p); +} + +void Assembler::addPatch(DataLabelPtr patch, V4IR::BasicBlock *target) +{ + _labelPatches[target].append(patch); +} + +Assembler::Pointer Assembler::loadTempAddress(RegisterID reg, V4IR::Temp *t) +{ + int32_t offset = 0; + int scope = t->scope; + QV4::Function *f = _vmFunction; + RegisterID context = ContextRegister; + if (scope) { + loadPtr(Address(ContextRegister, offsetof(ExecutionContext, outer)), ScratchRegister); + --scope; + f = f->outer; + context = ScratchRegister; + while (scope) { + loadPtr(Address(context, offsetof(ExecutionContext, outer)), context); + f = f->outer; + --scope; + } + } + switch (t->kind) { + case V4IR::Temp::Formal: + case V4IR::Temp::ScopedFormal: { + loadPtr(Address(context, offsetof(CallContext, arguments)), reg); + offset = t->index * sizeof(Value); + } break; + case V4IR::Temp::Local: + case V4IR::Temp::ScopedLocal: { + loadPtr(Address(context, offsetof(CallContext, locals)), reg); + offset = t->index * sizeof(Value); + } break; + case V4IR::Temp::StackSlot: { + assert(t->scope == 0); + const int arg = _function->maxNumberOfArguments + t->index + 1; + offset = - sizeof(Value) * (arg + 1); + offset -= sizeof(void*) * calleeSavedRegisterCount; + reg = LocalsRegister; + } break; + default: + Q_UNIMPLEMENTED(); + } + return Pointer(reg, offset); +} + +template <typename Result, typename Source> +void Assembler::copyValue(Result result, Source source) +{ +#ifdef VALUE_FITS_IN_REGISTER + // Use ReturnValueRegister as "scratch" register because loadArgument + // and storeArgument are functions that may need a scratch register themselves. + loadArgumentInRegister(source, ReturnValueRegister); + storeReturnValue(result); +#else + loadDouble(source, FPGpr0); + storeDouble(FPGpr0, result); +#endif +} + +template <typename Result> +void Assembler::copyValue(Result result, V4IR::Expr* source) +{ +#ifdef VALUE_FITS_IN_REGISTER + // Use ReturnValueRegister as "scratch" register because loadArgument + // and storeArgument are functions that may need a scratch register themselves. + loadArgumentInRegister(source, ReturnValueRegister); + storeReturnValue(result); +#else + if (V4IR::Temp *temp = source->asTemp()) { + loadDouble(temp, FPGpr0); + storeDouble(FPGpr0, result); + } else if (V4IR::Const *c = source->asConst()) { + QV4::Value v = convertToValue(c); + storeValue(v, result); + } else { + assert(! "not implemented"); + } +#endif +} + + +void Assembler::storeValue(QV4::Value value, V4IR::Temp* destination) +{ + Address addr = loadTempAddress(ScratchRegister, destination); + storeValue(value, addr); +} + +int Assembler::calculateStackFrameSize(int locals) +{ + const int stackSpaceAllocatedOtherwise = StackSpaceAllocatedUponFunctionEntry + + RegisterSize; // saved StackFrameRegister + + // space for the locals and the callee saved registers + int frameSize = locals * sizeof(QV4::Value) + sizeof(void*) * calleeSavedRegisterCount; + + frameSize = WTF::roundUpToMultipleOf(StackAlignment, frameSize + stackSpaceAllocatedOtherwise); + frameSize -= stackSpaceAllocatedOtherwise; + + return frameSize; +} + +void Assembler::enterStandardStackFrame(int locals) +{ + platformEnterStandardStackFrame(); + + // ### FIXME: Handle through calleeSavedRegisters mechanism + // or eliminate StackFrameRegister altogether. + push(StackFrameRegister); + move(StackPointerRegister, StackFrameRegister); + + int frameSize = calculateStackFrameSize(locals); + + subPtr(TrustedImm32(frameSize), StackPointerRegister); + + for (int i = 0; i < calleeSavedRegisterCount; ++i) + storePtr(calleeSavedRegisters[i], Address(StackFrameRegister, -(i + 1) * sizeof(void*))); + + move(StackFrameRegister, LocalsRegister); +} + +void Assembler::leaveStandardStackFrame(int locals) +{ + // restore the callee saved registers + for (int i = calleeSavedRegisterCount - 1; i >= 0; --i) + loadPtr(Address(StackFrameRegister, -(i + 1) * sizeof(void*)), calleeSavedRegisters[i]); + + int frameSize = calculateStackFrameSize(locals); + // Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't + // work well for large immediates. +#if CPU(ARM_THUMB2) + move(TrustedImm32(frameSize), JSC::ARMRegisters::r3); + add32(JSC::ARMRegisters::r3, StackPointerRegister); +#else + addPtr(TrustedImm32(frameSize), StackPointerRegister); +#endif + + pop(StackFrameRegister); + platformLeaveStandardStackFrame(); +} + + + +#define OP(op) \ + { isel_stringIfy(op), op, 0, 0 } + +#define INLINE_OP(op, memOp, immOp) \ + { isel_stringIfy(op), op, memOp, immOp } + +#define NULL_OP \ + { 0, 0, 0, 0 } + +const Assembler::BinaryOperationInfo Assembler::binaryOperations[QQmlJS::V4IR::LastAluOp + 1] = { + NULL_OP, // OpInvalid + NULL_OP, // OpIfTrue + NULL_OP, // OpNot + NULL_OP, // OpUMinus + NULL_OP, // OpUPlus + NULL_OP, // OpCompl + NULL_OP, // OpIncrement + NULL_OP, // OpDecrement + + INLINE_OP(__qmljs_bit_and, &Assembler::inline_and32, &Assembler::inline_and32), // OpBitAnd + INLINE_OP(__qmljs_bit_or, &Assembler::inline_or32, &Assembler::inline_or32), // OpBitOr + INLINE_OP(__qmljs_bit_xor, &Assembler::inline_xor32, &Assembler::inline_xor32), // OpBitXor + + INLINE_OP(__qmljs_add, &Assembler::inline_add32, &Assembler::inline_add32), // OpAdd + INLINE_OP(__qmljs_sub, &Assembler::inline_sub32, &Assembler::inline_sub32), // OpSub + INLINE_OP(__qmljs_mul, &Assembler::inline_mul32, &Assembler::inline_mul32), // OpMul + + OP(__qmljs_div), // OpDiv + OP(__qmljs_mod), // OpMod + + INLINE_OP(__qmljs_shl, &Assembler::inline_shl32, &Assembler::inline_shl32), // OpLShift + INLINE_OP(__qmljs_shr, &Assembler::inline_shr32, &Assembler::inline_shr32), // OpRShift + INLINE_OP(__qmljs_ushr, &Assembler::inline_ushr32, &Assembler::inline_ushr32), // OpURShift + + OP(__qmljs_gt), // OpGt + OP(__qmljs_lt), // OpLt + OP(__qmljs_ge), // OpGe + OP(__qmljs_le), // OpLe + OP(__qmljs_eq), // OpEqual + OP(__qmljs_ne), // OpNotEqual + OP(__qmljs_se), // OpStrictEqual + OP(__qmljs_sne), // OpStrictNotEqual + + OP(__qmljs_instanceof), // OpInstanceof + OP(__qmljs_in), // OpIn + + NULL_OP, // OpAnd + NULL_OP // OpOr +}; + +void Assembler::generateBinOp(V4IR::AluOp operation, V4IR::Temp* target, V4IR::Temp *left, V4IR::Temp *right) +{ + const BinaryOperationInfo& info = binaryOperations[operation]; + if (!info.fallbackImplementation) { + assert(!"unreachable"); + return; + } + + Value leftConst = Value::undefinedValue(); + Value rightConst = Value::undefinedValue(); + + bool canDoInline = info.inlineMemRegOp && info.inlineImmRegOp; + + if (canDoInline) { + if (left->asConst()) { + leftConst = convertToValue(left->asConst()); + canDoInline = canDoInline && leftConst.tryIntegerConversion(); + } + if (right->asConst()) { + rightConst = convertToValue(right->asConst()); + canDoInline = canDoInline && rightConst.tryIntegerConversion(); + } + } + + Jump binOpFinished; + + if (canDoInline) { + + Jump leftTypeCheck; + if (left->asTemp()) { + Address typeAddress = loadTempAddress(ScratchRegister, left->asTemp()); + typeAddress.offset += offsetof(QV4::Value, tag); + leftTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(QV4::Value::_Integer_Type)); + } + + Jump rightTypeCheck; + if (right->asTemp()) { + Address typeAddress = loadTempAddress(ScratchRegister, right->asTemp()); + typeAddress.offset += offsetof(QV4::Value, tag); + rightTypeCheck = branch32(NotEqual, typeAddress, TrustedImm32(QV4::Value::_Integer_Type)); + } + + if (left->asTemp()) { + Address leftValue = loadTempAddress(ScratchRegister, left->asTemp()); + leftValue.offset += offsetof(QV4::Value, int_32); + load32(leftValue, IntegerOpRegister); + } else { // left->asConst() + move(TrustedImm32(leftConst.integerValue()), IntegerOpRegister); + } + + Jump overflowCheck; + + if (right->asTemp()) { + Address rightValue = loadTempAddress(ScratchRegister, right->asTemp()); + rightValue.offset += offsetof(QV4::Value, int_32); + + overflowCheck = (this->*info.inlineMemRegOp)(rightValue, IntegerOpRegister); + } else { // right->asConst() + overflowCheck = (this->*info.inlineImmRegOp)(TrustedImm32(rightConst.integerValue()), IntegerOpRegister); + } + + Address resultAddr = loadTempAddress(ScratchRegister, target); + Address resultValueAddr = resultAddr; + resultValueAddr.offset += offsetof(QV4::Value, int_32); + store32(IntegerOpRegister, resultValueAddr); + + Address resultTypeAddr = resultAddr; + resultTypeAddr.offset += offsetof(QV4::Value, tag); + store32(TrustedImm32(QV4::Value::_Integer_Type), resultTypeAddr); + + binOpFinished = jump(); + + if (leftTypeCheck.isSet()) + leftTypeCheck.link(this); + if (rightTypeCheck.isSet()) + rightTypeCheck.link(this); + if (overflowCheck.isSet()) + overflowCheck.link(this); + } + + // Fallback + generateFunctionCallImp(Assembler::Void, info.name, info.fallbackImplementation, ContextRegister, + Assembler::PointerToValue(target), Assembler::Reference(left), Assembler::Reference(right)); + + if (binOpFinished.isSet()) + binOpFinished.link(this); +} +#if OS(LINUX) || OS(MAC_OS_X) +static void printDisassembledOutputWithCalls(const char* output, const QHash<void*, const char*>& functions, + const QVector<String*> &identifiers) +{ + QByteArray processedOutput(output); + for (QHash<void*, const char*>::ConstIterator it = functions.begin(), end = functions.end(); + it != end; ++it) { + QByteArray ptrString = QByteArray::number(quintptr(it.key()), 16); + ptrString.prepend("0x"); + processedOutput = processedOutput.replace(ptrString, it.value()); + } + for (QVector<String*>::ConstIterator it = identifiers.begin(), end = identifiers.end(); + it != end; ++it) { + QByteArray ptrString = QByteArray::number(quintptr(*it), 16); + ptrString.prepend("0x"); + QByteArray replacement = "\"" + (*it)->toQString().toUtf8() + "\""; + processedOutput = processedOutput.replace(ptrString, replacement); + } + fprintf(stderr, "%s\n", processedOutput.constData()); +} +#endif + +void Assembler::recordLineNumber(int lineNumber) +{ + CodeLineNumerMapping mapping; + mapping.location = label(); + mapping.lineNumber = lineNumber; + codeLineNumberMappings << mapping; +} + + +void Assembler::link(QV4::Function *vmFunc) +{ + Label endOfCode = label(); +#if defined(Q_PROCESSOR_ARM) && !defined(Q_OS_IOS) + // Let the ARM exception table follow right after that + for (int i = 0, nops = UnwindHelper::unwindInfoSize() / 2; i < nops; ++i) + nop(); +#endif + + { + QHashIterator<V4IR::BasicBlock *, QVector<Jump> > it(_patches); + while (it.hasNext()) { + it.next(); + V4IR::BasicBlock *block = it.key(); + Label target = _addrs.value(block); + assert(target.isSet()); + foreach (Jump jump, it.value()) + jump.linkTo(target, this); + } + } + + JSC::JSGlobalData dummy(_engine->executableAllocator); + JSC::LinkBuffer linkBuffer(dummy, this, 0); + vmFunc->codeSize = linkBuffer.offsetOf(endOfCode); + + vmFunc->lineNumberMappings.resize(codeLineNumberMappings.count()); + for (int i = 0; i < codeLineNumberMappings.count(); ++i) { + QV4::LineNumberMapping mapping; + mapping.codeOffset = linkBuffer.offsetOf(codeLineNumberMappings.at(i).location); + mapping.lineNumber = codeLineNumberMappings.at(i).lineNumber; + vmFunc->lineNumberMappings[i] = mapping; + } + + QHash<void*, const char*> functions; + foreach (CallToLink ctl, _callsToLink) { + linkBuffer.link(ctl.call, ctl.externalFunction); + functions[ctl.externalFunction.value()] = ctl.functionName; + } + + foreach (const DataLabelPatch &p, _dataLabelPatches) + linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target)); + + { + QHashIterator<V4IR::BasicBlock *, QVector<DataLabelPtr> > it(_labelPatches); + while (it.hasNext()) { + it.next(); + V4IR::BasicBlock *block = it.key(); + Label target = _addrs.value(block); + assert(target.isSet()); + foreach (DataLabelPtr label, it.value()) + linkBuffer.patch(label, linkBuffer.locationOf(target)); + } + } + +#if defined(Q_PROCESSOR_ARM) && !defined(Q_OS_IOS) + UnwindHelper::writeARMUnwindInfo(linkBuffer.debugAddress(), linkBuffer.offsetOf(endOfCode)); +#endif + + static bool showCode = !qgetenv("SHOW_CODE").isNull(); + if (showCode) { +#if OS(LINUX) && !defined(Q_OS_ANDROID) + char* disasmOutput = 0; + size_t disasmLength = 0; + FILE* disasmStream = open_memstream(&disasmOutput, &disasmLength); + WTF::setDataFile(disasmStream); +#elif OS(MAC_OS_X) + struct MemStream { + QByteArray buf; + static int write(void *cookie, const char *buf, int len) { + MemStream *stream = reinterpret_cast<MemStream *>(cookie); + stream->buf.append(buf, len); + return len; + } + }; + MemStream memStream; + + FILE* disasmStream = fwopen(&memStream, MemStream::write); + WTF::setDataFile(disasmStream); +#endif + + QByteArray name = _function->name->toUtf8(); + if (name.isEmpty()) { + name = QByteArray::number(quintptr(_function), 16); + name.prepend("IR::Function(0x"); + name.append(")"); + } + vmFunc->codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); + + WTF::setDataFile(stderr); +#if (OS(LINUX) && !defined(Q_OS_ANDROID)) || OS(MAC_OS_X) + fclose(disasmStream); +# if OS(MAC_OS_X) + char *disasmOutput = memStream.buf.data(); +# endif +# if CPU(X86) || CPU(X86_64) + QHash<void*, String*> idents; + printDisassembledOutputWithCalls(disasmOutput, functions, _vmFunction->identifiers); +# endif +# if OS(LINUX) + free(disasmOutput); +# endif +#endif + } else { + vmFunc->codeRef = linkBuffer.finalizeCodeWithoutDisassembly(); + } + + vmFunc->code = (Value (*)(QV4::ExecutionContext *, const uchar *)) vmFunc->codeRef.code().executableAddress(); +} + +InstructionSelection::InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module) + : EvalInstructionSelection(engine, module) + , _block(0) + , _function(0) + , _vmFunction(0) + , _as(0) + , _locals(0) +{ +} + +InstructionSelection::~InstructionSelection() +{ + delete _as; +} + +void InstructionSelection::run(QV4::Function *vmFunction, V4IR::Function *function) +{ + QVector<Lookup> lookups; + QSet<V4IR::BasicBlock*> reentryBlocks; + qSwap(_function, function); + qSwap(_vmFunction, vmFunction); + qSwap(_lookups, lookups); + qSwap(_reentryBlocks, reentryBlocks); + Assembler* oldAssembler = _as; + _as = new Assembler(_function, _vmFunction, engine()); + + V4IR::Optimizer opt(_function); + opt.run(); + if (opt.isInSSA()) { +#if CPU(X86_64) && (OS(MAC_OS_X) || OS(LINUX)) && 0 + // TODO: add a register allocator here. +#else + // No register allocator available for this platform, so: + opt.convertOutOfSSA(); + ConvertTemps().toStackSlots(_function); +#endif + } else { + ConvertTemps().toStackSlots(_function); + } + + int locals = (_function->tempCount + _function->maxNumberOfArguments) + 1; + locals = (locals + 1) & ~1; + qSwap(_locals, locals); + _as->enterStandardStackFrame(_locals); + + int contextPointer = 0; +#if !defined(RETURN_VALUE_IN_REGISTER) + // When the return VM value doesn't fit into a register, then + // the caller provides a pointer for storage as first argument. + // That shifts the index the context pointer argument by one. + contextPointer++; +#endif + +#ifdef ARGUMENTS_IN_REGISTERS + _as->move(_as->registerForArgument(contextPointer), Assembler::ContextRegister); +#else + _as->loadPtr(addressForArgument(contextPointer), Assembler::ContextRegister); +#endif + + for (int i = 0, ei = _function->basicBlocks.size(); i != ei; ++i) { + V4IR::BasicBlock *nextBlock = (i < ei - 1) ? _function->basicBlocks[i + 1] : 0; + _block = _function->basicBlocks[i]; + _as->registerBlock(_block, nextBlock); + + if (_reentryBlocks.contains(_block)) { + _as->enterStandardStackFrame(/*locals*/0); +#ifdef ARGUMENTS_IN_REGISTERS + _as->move(Assembler::registerForArgument(0), Assembler::ContextRegister); + _as->move(Assembler::registerForArgument(1), Assembler::LocalsRegister); +#else + _as->loadPtr(addressForArgument(0), Assembler::ContextRegister); + _as->loadPtr(addressForArgument(1), Assembler::LocalsRegister); +#endif + } + + foreach (V4IR::Stmt *s, _block->statements) { + if (s->location.isValid()) + _as->recordLineNumber(s->location.startLine); + s->accept(this); + } + } + + _as->link(_vmFunction); + + if (_lookups.size()) { + _vmFunction->lookups = new Lookup[_lookups.size()]; + memcpy(_vmFunction->lookups, _lookups.constData(), _lookups.size()*sizeof(Lookup)); + } + + UnwindHelper::registerFunction(_vmFunction); + + qSwap(_vmFunction, vmFunction); + qSwap(_function, function); + qSwap(_lookups, lookups); + qSwap(_reentryBlocks, reentryBlocks); + qSwap(_locals, locals); + delete _as; + _as = oldAssembler; +} + +void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) +{ + int argc = prepareVariableArguments(args); + QV4::String *s = identifier(*func->id); + + if (useFastLookups && func->global) { + uint index = addGlobalLookup(s); + generateFunctionCall(Assembler::Void, __qmljs_call_global_lookup, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::TrustedImm32(index), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } else { + generateFunctionCall(Assembler::Void, __qmljs_call_activation_property, + Assembler::ContextRegister, Assembler::PointerToValue(result), + s, + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } +} + +void InstructionSelection::callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); +} + +void InstructionSelection::callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_element, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::Reference(base), Assembler::Reference(index)); +} + +void InstructionSelection::callBuiltinTypeofName(const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof_name, Assembler::ContextRegister, Assembler::PointerToValue(result), identifier(name)); +} + +void InstructionSelection::callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_typeof, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(value)); +} + +void InstructionSelection::callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_delete_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); +} + +void InstructionSelection::callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_delete_subscript, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), Assembler::Reference(index)); +} + +void InstructionSelection::callBuiltinDeleteName(const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_delete_name, Assembler::ContextRegister, Assembler::PointerToValue(result), identifier(name)); +} + +void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result) +{ + _as->storeValue(Value::fromBoolean(false), result); +} + +void InstructionSelection::callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::PointerToValue(base), identifier(name)); +} + +void InstructionSelection::callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_element, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), Assembler::PointerToValue(index)); +} + +void InstructionSelection::callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment_name, Assembler::ContextRegister, + Assembler::PointerToValue(result), identifier(name)); +} + +void InstructionSelection::callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_increment, + Assembler::PointerToValue(result), Assembler::PointerToValue(value)); +} + +void InstructionSelection::callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_member, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name)); +} + +void InstructionSelection::callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_element, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), + Assembler::Reference(index)); +} + +void InstructionSelection::callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement_name, Assembler::ContextRegister, + Assembler::PointerToValue(result), identifier(name)); +} + +void InstructionSelection::callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_post_decrement, + Assembler::PointerToValue(result), Assembler::PointerToValue(value)); +} + +void InstructionSelection::callBuiltinThrow(V4IR::Temp *arg) +{ + generateFunctionCall(Assembler::Void, __qmljs_throw, Assembler::ContextRegister, Assembler::Reference(arg)); +} + +typedef void *(*MiddleOfFunctionEntryPoint(ExecutionContext *, void *localsPtr)); +static void *tryWrapper(ExecutionContext *context, void *localsPtr, MiddleOfFunctionEntryPoint tryBody, MiddleOfFunctionEntryPoint catchBody, + QV4::String *exceptionVarName, Value *exceptionVar) +{ + *exceptionVar = Value::undefinedValue(); + void *addressToContinueAt = 0; + try { + addressToContinueAt = tryBody(context, localsPtr); + } catch (Exception& ex) { + ex.accept(context); + *exceptionVar = ex.value(); + try { + ExecutionContext *catchContext = __qmljs_builtin_push_catch_scope(exceptionVarName, ex.value(), context); + addressToContinueAt = catchBody(catchContext, localsPtr); + context = __qmljs_builtin_pop_scope(catchContext); + } catch (Exception& ex) { + *exceptionVar = ex.value(); + ex.accept(context); + addressToContinueAt = catchBody(context, localsPtr); + } + } + return addressToContinueAt; +} + +void InstructionSelection::visitTry(V4IR::Try *t) +{ + // Call tryWrapper, which is going to re-enter the same function at the address of the try block. At then end + // of the try function the JIT code will return with the address of the sub-sequent instruction, which tryWrapper + // returns and to which we jump to. + + _reentryBlocks.insert(t->tryBlock); + _reentryBlocks.insert(t->catchBlock); + + generateFunctionCall(Assembler::ReturnValueRegister, tryWrapper, Assembler::ContextRegister, Assembler::LocalsRegister, + Assembler::ReentryBlock(t->tryBlock), Assembler::ReentryBlock(t->catchBlock), + identifier(t->exceptionVarName), Assembler::PointerToValue(t->exceptionVar)); + _as->jump(Assembler::ReturnValueRegister); +} + +void InstructionSelection::callBuiltinFinishTry() +{ + // This assumes that we're in code that was called by tryWrapper, so we return to try wrapper + // with the address that we'd like to continue at, which is right after the ret below. + Assembler::DataLabelPtr continuation = _as->moveWithPatch(Assembler::TrustedImmPtr(0), Assembler::ReturnValueRegister); + _as->leaveStandardStackFrame(/*locals*/0); + _as->ret(); + _as->addPatch(continuation, _as->label()); +} + +void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_foreach_iterator_object, Assembler::ContextRegister, Assembler::PointerToValue(result), Assembler::Reference(arg)); +} + +void InstructionSelection::callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) +{ + generateFunctionCall(Assembler::Void, __qmljs_foreach_next_property_name, Assembler::PointerToValue(result), Assembler::Reference(arg)); +} + +void InstructionSelection::callBuiltinPushWithScope(V4IR::Temp *arg) +{ + generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_with_scope, Assembler::Reference(arg), Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinPopScope() +{ + generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_pop_scope, Assembler::ContextRegister); +} + +void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_declare_var, Assembler::ContextRegister, + Assembler::TrustedImm32(deletable), identifier(name)); +} + +void InstructionSelection::callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_getter_setter, Assembler::ContextRegister, + Assembler::Reference(object), identifier(name), Assembler::PointerToValue(getter), Assembler::PointerToValue(setter)); +} + +void InstructionSelection::callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) +{ + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_property, Assembler::ContextRegister, + Assembler::Reference(object), identifier(name), Assembler::PointerToValue(value)); +} + +void InstructionSelection::callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) +{ + int length = prepareVariableArguments(args); + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_array, Assembler::ContextRegister, + Assembler::PointerToValue(result), + baseAddressForCallArguments(), Assembler::TrustedImm32(length)); +} + +void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args) +{ + int argc = 0; + + InternalClass *klass = engine()->emptyClass; + V4IR::ExprList *it = args; + while (it) { + V4IR::Name *name = it->expr->asName(); + it = it->next; + + bool isData = it->expr->asConst()->value; + it = it->next; + klass = klass->addMember(identifier(*name->id), isData ? QV4::Attr_Data : QV4::Attr_Accessor); + + _as->copyValue(argumentAddressForCall(argc++), it->expr); + + if (!isData) { + it = it->next; + _as->copyValue(argumentAddressForCall(argc++), it->expr); + } + + it = it->next; + } + + generateFunctionCall(Assembler::Void, __qmljs_builtin_define_object_literal, Assembler::ContextRegister, + Assembler::PointerToValue(result), baseAddressForCallArguments(), + Assembler::TrustedImmPtr(klass)); +} + +void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + int argc = prepareVariableArguments(args); + V4IR::Temp* thisObject = 0; + generateFunctionCall(Assembler::Void, __qmljs_call_value, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::PointerToValue(thisObject), + Assembler::Reference(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::loadThisObject(V4IR::Temp *temp) +{ +#if defined(VALUE_FITS_IN_REGISTER) + _as->load64(Pointer(Assembler::ContextRegister, offsetof(ExecutionContext, thisObject)), Assembler::ReturnValueRegister); + _as->storeReturnValue(temp); +#else + _as->copyValue(temp, Pointer(Assembler::ContextRegister, offsetof(ExecutionContext, thisObject))); +#endif +} + +void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) +{ + _as->storeValue(convertToValue(sourceConst), targetTemp); +} + +void InstructionSelection::loadString(const QString &str, V4IR::Temp *targetTemp) +{ + Value v = Value::fromString(identifier(str)); + _as->storeValue(v, targetTemp); +} + +void InstructionSelection::loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) +{ + Value v = Value::fromObject(engine()->newRegExpObject(*sourceRegexp->value, + sourceRegexp->flags)); + _vmFunction->generatedValues.append(v); + _as->storeValue(v, targetTemp); +} + +void InstructionSelection::getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp) +{ + String *propertyName = identifier(*name->id); + if (useFastLookups && name->global) { + uint index = addGlobalLookup(propertyName); + generateLookupCall(index, offsetof(QV4::Lookup, globalGetter), Assembler::ContextRegister, Assembler::PointerToValue(temp)); + return; + } + generateFunctionCall(Assembler::Void, __qmljs_get_activation_property, Assembler::ContextRegister, Assembler::PointerToValue(temp), propertyName); +} + +void InstructionSelection::setActivationProperty(V4IR::Temp *source, const QString &targetName) +{ + String *propertyName = identifier(targetName); + generateFunctionCall(Assembler::Void, __qmljs_set_activation_property, + Assembler::ContextRegister, propertyName, Assembler::Reference(source)); +} + +void InstructionSelection::initClosure(V4IR::Closure *closure, V4IR::Temp *target) +{ + QV4::Function *vmFunc = vmFunction(closure->value); + assert(vmFunc); + generateFunctionCall(Assembler::Void, __qmljs_init_closure, Assembler::ContextRegister, Assembler::PointerToValue(target), Assembler::TrustedImmPtr(vmFunc)); +} + +void InstructionSelection::getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target) +{ + if (useFastLookups) { + QV4::String *s = identifier(name); + uint index = addLookup(s); + generateLookupCall(index, offsetof(QV4::Lookup, getter), Assembler::PointerToValue(target), + Assembler::Reference(base)); + } else { + generateFunctionCall(Assembler::Void, __qmljs_get_property, Assembler::ContextRegister, Assembler::PointerToValue(target), + Assembler::Reference(base), identifier(name)); + } +} + +void InstructionSelection::setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + if (useFastLookups) { + QV4::String *s = identifier(targetName); + uint index = addSetterLookup(s); + generateLookupCall(index, offsetof(QV4::Lookup, setter), Assembler::Reference(targetBase), Assembler::Reference(source)); + } else { + generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister, + Assembler::Reference(targetBase), + identifier(targetName), Assembler::Reference(source)); + } +} + +void InstructionSelection::getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target) +{ + generateFunctionCall(Assembler::Void, __qmljs_get_element, Assembler::ContextRegister, + Assembler::PointerToValue(target), Assembler::Reference(base), + Assembler::Reference(index)); +} + +void InstructionSelection::setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex) +{ + generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister, + Assembler::Reference(targetBase), Assembler::Reference(targetIndex), + Assembler::Reference(source)); +} + +void InstructionSelection::copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + _as->copyValue(targetTemp, sourceTemp); +} + +#define setOp(op, opName, operation) \ + do { op = operation; opName = isel_stringIfy(operation); } while (0) + +void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) +{ + UnaryOpName op = 0; + const char *opName = 0; + switch (oper) { + case V4IR::OpIfTrue: assert(!"unreachable"); break; + case V4IR::OpNot: setOp(op, opName, __qmljs_not); break; + case V4IR::OpUMinus: setOp(op, opName, __qmljs_uminus); break; + case V4IR::OpUPlus: setOp(op, opName, __qmljs_uplus); break; + case V4IR::OpCompl: setOp(op, opName, __qmljs_compl); break; + case V4IR::OpIncrement: setOp(op, opName, __qmljs_increment); break; + case V4IR::OpDecrement: setOp(op, opName, __qmljs_decrement); break; + default: assert(!"unreachable"); break; + } // switch + + if (op) + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::PointerToValue(targetTemp), + Assembler::Reference(sourceTemp)); +} + +void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target) +{ + Q_ASSERT(leftSource->asTemp() && rightSource->asTemp()); + _as->generateBinOp(oper, target, leftSource->asTemp(), rightSource->asTemp()); +} + +void InstructionSelection::inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName) +{ + InplaceBinOpName op = 0; + const char *opName = 0; + switch (oper) { + case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_name); break; + case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_name); break; + case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_name); break; + case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_name); break; + case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_name); break; + case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_name); break; + case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_name); break; + case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_name); break; + case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_name); break; + case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_name); break; + case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_name); break; + default: + Q_UNREACHABLE(); + break; + } + if (op) { + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, + identifier(targetName), Assembler::Reference(rightSource)); + } +} + +void InstructionSelection::inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp) +{ + InplaceBinOpElement op = 0; + const char *opName = 0; + switch (oper) { + case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_element); break; + case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_element); break; + case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_element); break; + case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_element); break; + case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_element); break; + case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_element); break; + case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_element); break; + case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_element); break; + case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_element); break; + case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_element); break; + case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_element); break; + default: + Q_UNREACHABLE(); + break; + } + + if (op) { + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, + Assembler::Reference(targetBaseTemp), Assembler::Reference(targetIndexTemp), + Assembler::Reference(source)); + } +} + +void InstructionSelection::inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) +{ + InplaceBinOpMember op = 0; + const char *opName = 0; + switch (oper) { + case V4IR::OpBitAnd: setOp(op, opName, __qmljs_inplace_bit_and_member); break; + case V4IR::OpBitOr: setOp(op, opName, __qmljs_inplace_bit_or_member); break; + case V4IR::OpBitXor: setOp(op, opName, __qmljs_inplace_bit_xor_member); break; + case V4IR::OpAdd: setOp(op, opName, __qmljs_inplace_add_member); break; + case V4IR::OpSub: setOp(op, opName, __qmljs_inplace_sub_member); break; + case V4IR::OpMul: setOp(op, opName, __qmljs_inplace_mul_member); break; + case V4IR::OpDiv: setOp(op, opName, __qmljs_inplace_div_member); break; + case V4IR::OpMod: setOp(op, opName, __qmljs_inplace_mod_member); break; + case V4IR::OpLShift: setOp(op, opName, __qmljs_inplace_shl_member); break; + case V4IR::OpRShift: setOp(op, opName, __qmljs_inplace_shr_member); break; + case V4IR::OpURShift: setOp(op, opName, __qmljs_inplace_ushr_member); break; + default: + Q_UNREACHABLE(); + break; + } + + if (op) { + String* member = identifier(targetName); + _as->generateFunctionCallImp(Assembler::Void, opName, op, Assembler::ContextRegister, + Assembler::Reference(targetBase), identifier(targetName), + Assembler::Reference(source)); + } +} + +void InstructionSelection::callProperty(V4IR::Temp *base, const QString &name, + V4IR::ExprList *args, V4IR::Temp *result) +{ + assert(base != 0); + + int argc = prepareVariableArguments(args); + QV4::String *s = identifier(name); + + if (useFastLookups) { + uint index = addLookup(s); + generateFunctionCall(Assembler::Void, __qmljs_call_property_lookup, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::Reference(base), Assembler::TrustedImm32(index), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } else { + generateFunctionCall(Assembler::Void, __qmljs_call_property, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::Reference(base), s, + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + } +} + +void InstructionSelection::callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) +{ + assert(base != 0); + + int argc = prepareVariableArguments(args); + generateFunctionCall(Assembler::Void, __qmljs_call_element, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::Reference(base), Assembler::Reference(index), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::convertType(V4IR::Temp *source, V4IR::Temp *target) +{ + // FIXME: do something more useful with this info + copyValue(source, target); +} + +String *InstructionSelection::identifier(const QString &s) +{ + String *str = engine()->newIdentifier(s); + _vmFunction->identifiers.append(str); + return str; +} + +void InstructionSelection::constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) +{ + assert(func != 0); + + if (useFastLookups && func->global) { + int argc = prepareVariableArguments(args); + QV4::String *s = identifier(*func->id); + + uint index = addGlobalLookup(s); + generateFunctionCall(Assembler::Void, __qmljs_construct_global_lookup, + Assembler::ContextRegister, Assembler::PointerToValue(result), + Assembler::TrustedImm32(index), + baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); + return; + } + + callRuntimeMethod(result, __qmljs_construct_activation_property, func, args); +} + +void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) +{ + int argc = prepareVariableArguments(args); + generateFunctionCall(Assembler::Void, __qmljs_construct_property, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(base), identifier(name), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) +{ + assert(value != 0); + + int argc = prepareVariableArguments(args); + generateFunctionCall(Assembler::Void, __qmljs_construct_value, Assembler::ContextRegister, + Assembler::PointerToValue(result), Assembler::Reference(value), baseAddressForCallArguments(), Assembler::TrustedImm32(argc)); +} + +void InstructionSelection::visitJump(V4IR::Jump *s) +{ + _as->jumpToBlock(_block, s->target); +} + +void InstructionSelection::visitCJump(V4IR::CJump *s) +{ + if (V4IR::Temp *t = s->cond->asTemp()) { + Address temp = _as->loadTempAddress(Assembler::ScratchRegister, t); + Address tag = temp; + tag.offset += offsetof(QV4::Value, tag); + Assembler::Jump booleanConversion = _as->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(QV4::Value::Boolean_Type)); + + Address data = temp; + data.offset += offsetof(QV4::Value, int_32); + _as->load32(data, Assembler::ReturnValueRegister); + Assembler::Jump testBoolean = _as->jump(); + + booleanConversion.link(_as); + { + generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean, Assembler::Reference(t)); + } + + testBoolean.link(_as); + Assembler::Jump target = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); + _as->addPatch(s->iftrue, target); + + _as->jumpToBlock(_block, s->iffalse); + return; + } else if (V4IR::Binop *b = s->cond->asBinop()) { + if (b->left->asTemp() && b->right->asTemp()) { + CmpOp op = 0; + const char *opName = 0; + switch (b->op) { + default: Q_UNREACHABLE(); assert(!"todo"); break; + case V4IR::OpGt: setOp(op, opName, __qmljs_cmp_gt); break; + case V4IR::OpLt: setOp(op, opName, __qmljs_cmp_lt); break; + case V4IR::OpGe: setOp(op, opName, __qmljs_cmp_ge); break; + case V4IR::OpLe: setOp(op, opName, __qmljs_cmp_le); break; + case V4IR::OpEqual: setOp(op, opName, __qmljs_cmp_eq); break; + case V4IR::OpNotEqual: setOp(op, opName, __qmljs_cmp_ne); break; + case V4IR::OpStrictEqual: setOp(op, opName, __qmljs_cmp_se); break; + case V4IR::OpStrictNotEqual: setOp(op, opName, __qmljs_cmp_sne); break; + case V4IR::OpInstanceof: setOp(op, opName, __qmljs_cmp_instanceof); break; + case V4IR::OpIn: setOp(op, opName, __qmljs_cmp_in); break; + } // switch + + _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op, Assembler::ContextRegister, + Assembler::Reference(b->left->asTemp()), + Assembler::Reference(b->right->asTemp())); + + Assembler::Jump target = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0)); + _as->addPatch(s->iftrue, target); + + _as->jumpToBlock(_block, s->iffalse); + return; + } else { + assert(!"wip"); + } + Q_UNIMPLEMENTED(); + } + Q_UNIMPLEMENTED(); + assert(!"TODO"); +} + +void InstructionSelection::visitRet(V4IR::Ret *s) +{ + if (V4IR::Temp *t = s->expr->asTemp()) { +#if defined(RETURN_VALUE_IN_REGISTER) +#if CPU(X86) + Address addr = _as->loadTempAddress(Assembler::ScratchRegister, t); + _as->load32(addr, JSC::X86Registers::eax); + addr.offset += 4; + _as->load32(addr, JSC::X86Registers::edx); +#else + _as->copyValue(Assembler::ReturnValueRegister, t); +#endif +#else + _as->loadPtr(addressForArgument(0), Assembler::ReturnValueRegister); + _as->copyValue(Address(Assembler::ReturnValueRegister, 0), t); +#endif + } else if (V4IR::Const *c = s->expr->asConst()) { + _as->copyValue(Assembler::ReturnValueRegister, c); + } else { + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); + Q_UNUSED(s); + } + + _as->leaveStandardStackFrame(_locals); +#if !defined(ARGUMENTS_IN_REGISTERS) && !defined(RETURN_VALUE_IN_REGISTER) + // Emulate ret(n) instruction + // Pop off return address into scratch register ... + _as->pop(Assembler::ScratchRegister); + // ... and overwrite the invisible argument with + // the return address. + _as->poke(Assembler::ScratchRegister); +#endif + _as->ret(); +} + +int InstructionSelection::prepareVariableArguments(V4IR::ExprList* args) +{ + int argc = 0; + for (V4IR::ExprList *it = args; it; it = it->next) { + ++argc; + } + + int i = 0; + for (V4IR::ExprList *it = args; it; it = it->next, ++i) { +// V4IR::Temp *arg = it->expr->asTemp(); +// assert(arg != 0); + _as->copyValue(argumentAddressForCall(i), it->expr); + } + + return argc; +} + +void InstructionSelection::callRuntimeMethodImp(V4IR::Temp *result, const char* name, ActivationMethod method, V4IR::Expr *base, V4IR::ExprList *args) +{ + V4IR::Name *baseName = base->asName(); + assert(baseName != 0); + + int argc = prepareVariableArguments(args); + _as->generateFunctionCallImp(Assembler::Void, name, method, Assembler::ContextRegister, Assembler::PointerToValue(result), + identifier(*baseName->id), baseAddressForCallArguments(), + Assembler::TrustedImm32(argc)); +} + + +uint InstructionSelection::addLookup(QV4::String *name) +{ + uint index = (uint)_lookups.size(); + QV4::Lookup l; + l.getter = Lookup::getterGeneric; + for (int i = 0; i < Lookup::Size; ++i) + l.classList[i] = 0; + l.level = -1; + l.index = UINT_MAX; + l.name = name; + _lookups.append(l); + return index; +} + +uint InstructionSelection::addSetterLookup(QV4::String *name) +{ + uint index = (uint)_lookups.size(); + QV4::Lookup l; + l.setter = Lookup::setterGeneric; + for (int i = 0; i < Lookup::Size; ++i) + l.classList[i] = 0; + l.level = -1; + l.index = UINT_MAX; + l.name = name; + _lookups.append(l); + return index; +} + +uint InstructionSelection::addGlobalLookup(QV4::String *name) +{ + uint index = (uint)_lookups.size(); + QV4::Lookup l; + l.globalGetter = Lookup::globalGetterGeneric; + for (int i = 0; i < Lookup::Size; ++i) + l.classList[i] = 0; + l.level = -1; + l.index = UINT_MAX; + l.name = name; + _lookups.append(l); + return index; +} diff --git a/src/qml/qml/v4/qv4isel_masm_p.h b/src/qml/qml/v4/qv4isel_masm_p.h new file mode 100644 index 0000000000..b53b0e5c74 --- /dev/null +++ b/src/qml/qml/v4/qv4isel_masm_p.h @@ -0,0 +1,938 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ISEL_MASM_P_H +#define QV4ISEL_MASM_P_H + +#include "qv4global_p.h" +#include "qv4jsir_p.h" +#include "qv4isel_p.h" +#include "qv4isel_util_p.h" +#include "qv4object_p.h" +#include "qv4runtime_p.h" +#include "qv4lookup_p.h" + +#include <QtCore/QHash> +#include <config.h> +#include <wtf/Vector.h> +#include <assembler/MacroAssembler.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace MASM { + +class Assembler : public JSC::MacroAssembler +{ +public: + Assembler(V4IR::Function* function, QV4::Function *vmFunction, QV4::ExecutionEngine *engine); +#if CPU(X86) + +#undef VALUE_FITS_IN_REGISTER +#undef ARGUMENTS_IN_REGISTERS + +#if OS(WINDOWS) + // Returned in EAX:EDX pair +#define RETURN_VALUE_IN_REGISTER +#else +#undef RETURN_VALUE_IN_REGISTER +#endif + +#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 + + static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::edi; + static const RegisterID ContextRegister = JSC::X86Registers::esi; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::ecx; + static const RegisterID IntegerOpRegister = JSC::X86Registers::eax; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + + static const int RegisterSize = 4; + + static const int RegisterArgumentCount = 0; + static RegisterID registerForArgument(int) + { + assert(false); + // Not reached. + return JSC::X86Registers::eax; + } + + // Return address is pushed onto stack by the CPU. + static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; + static const int StackShadowSpace = 0; + inline void platformEnterStandardStackFrame() {} + inline void platformLeaveStandardStackFrame() {} +#elif CPU(X86_64) + +#define VALUE_FITS_IN_REGISTER +#define ARGUMENTS_IN_REGISTERS +#define RETURN_VALUE_IN_REGISTER +#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 + + static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::r12; + static const RegisterID ContextRegister = JSC::X86Registers::r14; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::r10; + static const RegisterID IntegerOpRegister = JSC::X86Registers::eax; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + + static const int RegisterSize = 8; + +#if OS(WINDOWS) + static const int RegisterArgumentCount = 4; + static RegisterID registerForArgument(int index) + { + static RegisterID regs[RegisterArgumentCount] = { + JSC::X86Registers::ecx, + JSC::X86Registers::edx, + JSC::X86Registers::r8, + JSC::X86Registers::r9 + }; + assert(index >= 0 && index < RegisterArgumentCount); + return regs[index]; + }; + static const int StackShadowSpace = 32; +#else // Unix + static const int RegisterArgumentCount = 6; + static RegisterID registerForArgument(int index) + { + static RegisterID regs[RegisterArgumentCount] = { + JSC::X86Registers::edi, + JSC::X86Registers::esi, + JSC::X86Registers::edx, + JSC::X86Registers::ecx, + JSC::X86Registers::r8, + JSC::X86Registers::r9 + }; + assert(index >= 0 && index < RegisterArgumentCount); + return regs[index]; + }; + static const int StackShadowSpace = 0; +#endif + + // Return address is pushed onto stack by the CPU. + static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; + inline void platformEnterStandardStackFrame() {} + inline void platformLeaveStandardStackFrame() {} +#elif CPU(ARM) + +#undef VALUE_FITS_IN_REGISTER +#define ARGUMENTS_IN_REGISTERS +#undef RETURN_VALUE_IN_REGISTER +#undef HAVE_ALU_OPS_WITH_MEM_OPERAND + + static const RegisterID StackFrameRegister = JSC::ARMRegisters::r4; + static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp; + static const RegisterID LocalsRegister = JSC::ARMRegisters::r7; + static const RegisterID ContextRegister = JSC::ARMRegisters::r5; + static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; + static const RegisterID ScratchRegister = JSC::ARMRegisters::r6; + static const RegisterID IntegerOpRegister = JSC::ARMRegisters::r0; + static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; + + static const int RegisterSize = 4; + + static const RegisterID RegisterArgument1 = JSC::ARMRegisters::r0; + static const RegisterID RegisterArgument2 = JSC::ARMRegisters::r1; + static const RegisterID RegisterArgument3 = JSC::ARMRegisters::r2; + static const RegisterID RegisterArgument4 = JSC::ARMRegisters::r3; + + static const int RegisterArgumentCount = 4; + static RegisterID registerForArgument(int index) + { + assert(index >= 0 && index < RegisterArgumentCount); + return static_cast<RegisterID>(JSC::ARMRegisters::r0 + index); + }; + + // Registers saved in platformEnterStandardStackFrame below. + static const int StackSpaceAllocatedUponFunctionEntry = 5 * RegisterSize; + static const int StackShadowSpace = 0; + inline void platformEnterStandardStackFrame() + { + // Move the register arguments onto the stack as if they were + // pushed by the caller, just like on ia32. This gives us consistent + // access to the parameters if we need to. + push(JSC::ARMRegisters::r3); + push(JSC::ARMRegisters::r2); + push(JSC::ARMRegisters::r1); + push(JSC::ARMRegisters::r0); + push(JSC::ARMRegisters::lr); + } + inline void platformLeaveStandardStackFrame() + { + pop(JSC::ARMRegisters::lr); + addPtr(TrustedImm32(4 * RegisterSize), StackPointerRegister); + } +#else +#error The JIT needs to be ported to this platform. +#endif + static const int calleeSavedRegisterCount; + +#if CPU(X86) || CPU(X86_64) + static const int StackAlignment = 16; +#elif CPU(ARM) + // Per AAPCS + static const int StackAlignment = 8; +#else +#error Stack alignment unknown for this platform. +#endif + + // Explicit type to allow distinguishing between + // pushing an address itself or the value it points + // to onto the stack when calling functions. + struct Pointer : public Address + { + explicit Pointer(const Address& addr) + : Address(addr) + {} + explicit Pointer(RegisterID reg, int32_t offset) + : Address(reg, offset) + {} + }; + + struct VoidType { VoidType() {} }; + static const VoidType Void; + + + typedef JSC::FunctionPtr FunctionPtr; + + struct CallToLink { + Call call; + FunctionPtr externalFunction; + const char* functionName; + }; + struct PointerToValue { + PointerToValue(V4IR::Temp *value) : value(value) {} + V4IR::Temp *value; + }; + struct Reference { + Reference(V4IR::Temp *value) : value(value) {} + V4IR::Temp *value; + }; + + struct ReentryBlock { + ReentryBlock(V4IR::BasicBlock *b) : block(b) {} + V4IR::BasicBlock *block; + }; + + void callAbsolute(const char* functionName, FunctionPtr function) { + CallToLink ctl; + ctl.call = call(); + ctl.externalFunction = function; + ctl.functionName = functionName; + _callsToLink.append(ctl); + } + + void callAbsolute(const char* /*functionName*/, Address addr) { + call(addr); + } + + void registerBlock(V4IR::BasicBlock*, V4IR::BasicBlock *nextBlock); + void jumpToBlock(V4IR::BasicBlock* current, V4IR::BasicBlock *target); + void addPatch(V4IR::BasicBlock* targetBlock, Jump targetJump); + void addPatch(DataLabelPtr patch, Label target); + void addPatch(DataLabelPtr patch, V4IR::BasicBlock *target); + + Pointer loadTempAddress(RegisterID reg, V4IR::Temp *t); + + void loadArgumentInRegister(RegisterID source, RegisterID dest) + { + move(source, dest); + } + + void loadArgumentInRegister(TrustedImmPtr ptr, RegisterID dest) + { + move(TrustedImmPtr(ptr), dest); + } + + void loadArgumentInRegister(const Pointer& ptr, RegisterID dest) + { + addPtr(TrustedImm32(ptr.offset), ptr.base, dest); + } + + void loadArgumentInRegister(PointerToValue temp, RegisterID dest) + { + if (!temp.value) { + loadArgumentInRegister(TrustedImmPtr(0), dest); + } else { + Pointer addr = loadTempAddress(dest, temp.value); + loadArgumentInRegister(addr, dest); + } + } + + void loadArgumentInRegister(Reference temp, RegisterID dest) + { + assert(temp.value); + Pointer addr = loadTempAddress(dest, temp.value); + loadArgumentInRegister(addr, dest); + } + + void loadArgumentInRegister(ReentryBlock block, RegisterID dest) + { + assert(block.block); + DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), dest); + addPatch(patch, block.block); + } + +#ifdef VALUE_FITS_IN_REGISTER + void loadArgumentInRegister(V4IR::Temp* temp, RegisterID dest) + { + if (!temp) { + QV4::Value undefined = QV4::Value::undefinedValue(); + move(TrustedImm64(undefined.val), dest); + } else { + Pointer addr = loadTempAddress(dest, temp); + load64(addr, dest); + } + } + + void loadArgumentInRegister(V4IR::Const* c, RegisterID dest) + { + QV4::Value v = convertToValue(c); + move(TrustedImm64(v.val), dest); + } + + void loadArgumentInRegister(V4IR::Expr* expr, RegisterID dest) + { + if (!expr) { + QV4::Value undefined = QV4::Value::undefinedValue(); + move(TrustedImm64(undefined.val), dest); + } else if (expr->asTemp()){ + loadArgumentInRegister(expr->asTemp(), dest); + } else if (expr->asConst()) { + loadArgumentInRegister(expr->asConst(), dest); + } else { + assert(!"unimplemented expression type in loadArgument"); + } + } +#else + void loadArgumentInRegister(V4IR::Expr*, RegisterID) + { + assert(!"unimplemented: expression in loadArgument"); + } +#endif + + void loadArgumentInRegister(QV4::String* string, RegisterID dest) + { + loadArgumentInRegister(TrustedImmPtr(string), dest); + } + + void loadArgumentInRegister(TrustedImm32 imm32, RegisterID dest) + { + xorPtr(dest, dest); + if (imm32.m_value) + move(imm32, dest); + } + + void storeReturnValue(RegisterID dest) + { + move(ReturnValueRegister, dest); + } + +#ifdef VALUE_FITS_IN_REGISTER + void storeReturnValue(const Pointer &dest) + { + store64(ReturnValueRegister, dest); + } + + void storeReturnValue(V4IR::Temp *temp) + { + if (!temp) + return; + Pointer addr = loadTempAddress(ScratchRegister, temp); + storeReturnValue(addr); + } +#endif + + void storeReturnValue(VoidType) + { + } + + template <int StackSlot> + void loadArgumentOnStack(RegisterID reg) + { + poke(reg, StackSlot); + } + + template <int StackSlot> + void loadArgumentOnStack(TrustedImm32 value) + { + poke(value, StackSlot); + } + + template <int StackSlot> + void loadArgumentOnStack(const Pointer& ptr) + { + addPtr(TrustedImm32(ptr.offset), ptr.base, ScratchRegister); + poke(ScratchRegister, StackSlot); + } + + template <int StackSlot> + void loadArgumentOnStack(PointerToValue temp) + { + if (temp.value) { + Pointer ptr = loadTempAddress(ScratchRegister, temp.value); + loadArgumentOnStack<StackSlot>(ptr); + } else { + poke(TrustedImmPtr(0), StackSlot); + } + } + + template <int StackSlot> + void loadArgumentOnStack(Reference temp) + { + assert (temp.value); + + Pointer ptr = loadTempAddress(ScratchRegister, temp.value); + loadArgumentOnStack<StackSlot>(ptr); + } + + template <int StackSlot> + void loadArgumentOnStack(ReentryBlock block) + { + assert(block.block); + DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), ScratchRegister); + poke(ScratchRegister, StackSlot); + addPatch(patch, block.block); + } + + template <int StackSlot> + void loadArgumentOnStack(TrustedImmPtr ptr) + { + move(TrustedImmPtr(ptr), ScratchRegister); + poke(ScratchRegister, StackSlot); + } + + template <int StackSlot> + void loadArgumentOnStack(QV4::String* name) + { + poke(TrustedImmPtr(name), StackSlot); + } + + using JSC::MacroAssembler::loadDouble; + void loadDouble(V4IR::Temp* temp, FPRegisterID dest) + { + Pointer ptr = loadTempAddress(ScratchRegister, temp); + loadDouble(ptr, dest); + } + + using JSC::MacroAssembler::storeDouble; + void storeDouble(FPRegisterID source, V4IR::Temp* temp) + { + Pointer ptr = loadTempAddress(ScratchRegister, temp); + storeDouble(source, ptr); + } + + template <typename Result, typename Source> + void copyValue(Result result, Source source); + template <typename Result> + void copyValue(Result result, V4IR::Expr* source); + + void storeValue(QV4::Value value, Address destination) + { +#ifdef VALUE_FITS_IN_REGISTER + store64(TrustedImm64(value.val), destination); +#else + store32(TrustedImm32(value.int_32), destination); + destination.offset += 4; + store32(TrustedImm32(value.tag), destination); +#endif + } + + void storeValue(QV4::Value value, V4IR::Temp* temp); + + static int calculateStackFrameSize(int locals); + void enterStandardStackFrame(int locals); + void leaveStandardStackFrame(int locals); + + template <int argumentNumber, typename T> + void loadArgumentOnStackOrRegister(const T &value) + { + if (argumentNumber < RegisterArgumentCount) + loadArgumentInRegister(value, registerForArgument(argumentNumber)); + else +#if OS(WINDOWS) && CPU(X86_64) + loadArgumentOnStack<argumentNumber>(value); +#else // Sanity: + loadArgumentOnStack<argumentNumber - RegisterArgumentCount>(value); +#endif + } + + template <int argumentNumber> + void loadArgumentOnStackOrRegister(const VoidType &value) + { + Q_UNUSED(value); + } + + template <bool selectFirst, int First, int Second> + struct Select + { + enum { Chosen = First }; + }; + + template <int First, int Second> + struct Select<false, First, Second> + { + enum { Chosen = Second }; + }; + + template <int ArgumentIndex, typename Parameter> + struct SizeOnStack + { + enum { Size = Select<ArgumentIndex >= RegisterArgumentCount, QT_POINTER_SIZE, 0>::Chosen }; + }; + + template <int ArgumentIndex> + struct SizeOnStack<ArgumentIndex, VoidType> + { + enum { Size = 0 }; + }; + + + template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) + { + int stackSpaceNeeded = SizeOnStack<0, Arg1>::Size + + SizeOnStack<1, Arg2>::Size + + SizeOnStack<2, Arg3>::Size + + SizeOnStack<3, Arg4>::Size + + SizeOnStack<4, Arg5>::Size + + SizeOnStack<5, Arg6>::Size + + StackShadowSpace; + + if (stackSpaceNeeded) { + stackSpaceNeeded = WTF::roundUpToMultipleOf(StackAlignment, stackSpaceNeeded); + sub32(TrustedImm32(stackSpaceNeeded), StackPointerRegister); + } + + loadArgumentOnStackOrRegister<5>(arg6); + loadArgumentOnStackOrRegister<4>(arg5); + loadArgumentOnStackOrRegister<3>(arg4); + loadArgumentOnStackOrRegister<2>(arg3); + loadArgumentOnStackOrRegister<1>(arg2); + loadArgumentOnStackOrRegister<0>(arg1); + + callAbsolute(functionName, function); + + storeReturnValue(r); + + if (stackSpaceNeeded) + add32(TrustedImm32(stackSpaceNeeded), StackPointerRegister); + } + + template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, arg5, VoidType()); + } + + template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, VoidType()); + } + + template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType()); + } + + template <typename ArgRet, typename Callable, typename Arg1, typename Arg2> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType()); + } + + template <typename ArgRet, typename Callable, typename Arg1> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1) + { + generateFunctionCallImp(r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType()); + } + + typedef Jump (Assembler::*MemRegBinOp)(Address, RegisterID); + typedef Jump (Assembler::*ImmRegBinOp)(TrustedImm32, RegisterID); + + struct BinaryOperationInfo { + const char *name; + QV4::BinOp fallbackImplementation; + MemRegBinOp inlineMemRegOp; + ImmRegBinOp inlineImmRegOp; + }; + + static const BinaryOperationInfo binaryOperations[QQmlJS::V4IR::LastAluOp + 1]; + + void generateBinOp(V4IR::AluOp operation, V4IR::Temp* target, V4IR::Temp* left, V4IR::Temp* right); + + Jump inline_add32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + return branchAdd32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchAdd32(Overflow, ScratchRegister, reg); +#endif + } + + Jump inline_add32(TrustedImm32 imm, RegisterID reg) + { + return branchAdd32(Overflow, imm, reg); + } + + Jump inline_sub32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + return branchSub32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchSub32(Overflow, ScratchRegister, reg); +#endif + } + + Jump inline_sub32(TrustedImm32 imm, RegisterID reg) + { + return branchSub32(Overflow, imm, reg); + } + + Jump inline_mul32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + return branchMul32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchMul32(Overflow, ScratchRegister, reg); +#endif + } + + Jump inline_mul32(TrustedImm32 imm, RegisterID reg) + { + return branchMul32(Overflow, imm, reg, reg); + } + + Jump inline_shl32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + lshift32(ScratchRegister, reg); + return Jump(); + } + + Jump inline_shl32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + lshift32(imm, reg); + return Jump(); + } + + Jump inline_shr32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + rshift32(ScratchRegister, reg); + return Jump(); + } + + Jump inline_shr32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + rshift32(imm, reg); + return Jump(); + } + + Jump inline_ushr32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + urshift32(ScratchRegister, reg); + return branchTest32(Signed, reg, reg); + } + + Jump inline_ushr32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + urshift32(imm, reg); + return branchTest32(Signed, reg, reg); + } + + Jump inline_and32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + and32(addr, reg); +#else + load32(addr, ScratchRegister); + and32(ScratchRegister, reg); +#endif + return Jump(); + } + + Jump inline_and32(TrustedImm32 imm, RegisterID reg) + { + and32(imm, reg); + return Jump(); + } + + Jump inline_or32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + or32(addr, reg); +#else + load32(addr, ScratchRegister); + or32(ScratchRegister, reg); +#endif + return Jump(); + } + + Jump inline_or32(TrustedImm32 imm, RegisterID reg) + { + or32(imm, reg); + return Jump(); + } + + Jump inline_xor32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + xor32(addr, reg); +#else + load32(addr, ScratchRegister); + xor32(ScratchRegister, reg); +#endif + return Jump(); + } + + Jump inline_xor32(TrustedImm32 imm, RegisterID reg) + { + xor32(imm, reg); + return Jump(); + } + + void link(QV4::Function *vmFunc); + + void recordLineNumber(int lineNumber); + +private: + V4IR::Function *_function; + QV4::Function *_vmFunction; + QHash<V4IR::BasicBlock *, Label> _addrs; + QHash<V4IR::BasicBlock *, QVector<Jump> > _patches; + QList<CallToLink> _callsToLink; + + struct DataLabelPatch { + DataLabelPtr dataLabel; + Label target; + }; + QList<DataLabelPatch> _dataLabelPatches; + + QHash<V4IR::BasicBlock *, QVector<DataLabelPtr> > _labelPatches; + V4IR::BasicBlock *_nextBlock; + + QV4::ExecutionEngine *_engine; + + struct CodeLineNumerMapping + { + Assembler::Label location; + int lineNumber; + }; + QVector<CodeLineNumerMapping> codeLineNumberMappings; +}; + +class Q_QML_EXPORT InstructionSelection: + protected V4IR::IRDecoder, + public EvalInstructionSelection +{ +public: + InstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module); + ~InstructionSelection(); + + virtual void run(QV4::Function *vmFunction, V4IR::Function *function); + +protected: + virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinDeleteValue(V4IR::Temp *result); + virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result); + virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result); + virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result); + virtual void callBuiltinThrow(V4IR::Temp *arg); + virtual void callBuiltinFinishTry(); + virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result); + virtual void callBuiltinPushWithScope(V4IR::Temp *arg); + virtual void callBuiltinPopScope(); + virtual void callBuiltinDeclareVar(bool deletable, const QString &name); + virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter); + virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value); + virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args); + virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args); + virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result); + virtual void convertType(V4IR::Temp *source, V4IR::Temp *target); + virtual void loadThisObject(V4IR::Temp *temp); + virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp); + virtual void loadString(const QString &str, V4IR::Temp *targetTemp); + virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp); + virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp); + virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName); + virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target); + virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target); + virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target); + virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex); + virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp); + virtual void binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target); + virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName); + virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName); + + typedef Assembler::Address Address; + typedef Assembler::Pointer Pointer; + + Address addressForArgument(int index) const + { + // StackFrameRegister points to its old value on the stack, and above + // it we have the return address, hence the need to step over two + // values before reaching the first argument. + return Address(Assembler::StackFrameRegister, (index + 2) * sizeof(void*)); + } + + // Some run-time functions take (Value* args, int argc). This function is for populating + // the args. + Pointer argumentAddressForCall(int argument) + { + const int index = _function->maxNumberOfArguments - argument; + return Pointer(Assembler::LocalsRegister, sizeof(QV4::Value) * (-index) + - sizeof(void*) // size of ebp + - sizeof(void*) * Assembler::calleeSavedRegisterCount + ); + } + Pointer baseAddressForCallArguments() + { + return argumentAddressForCall(0); + } + + QV4::String *identifier(const QString &s); + virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); + virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); + + virtual void visitJump(V4IR::Jump *); + virtual void visitCJump(V4IR::CJump *); + virtual void visitRet(V4IR::Ret *); + virtual void visitTry(V4IR::Try *); + +private: + #define isel_stringIfyx(s) #s + #define isel_stringIfy(s) isel_stringIfyx(s) + + #define generateFunctionCall(t, function, ...) \ + _as->generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__) + + int prepareVariableArguments(V4IR::ExprList* args); + + typedef void (*ActivationMethod)(QV4::ExecutionContext *, QV4::Value *result, QV4::String *name, QV4::Value *args, int argc); + void callRuntimeMethodImp(V4IR::Temp *result, const char* name, ActivationMethod method, V4IR::Expr *base, V4IR::ExprList *args); +#define callRuntimeMethod(result, function, ...) \ + callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__) + + uint addLookup(QV4::String *name); + uint addSetterLookup(QV4::String *name); + uint addGlobalLookup(QV4::String *name); + + template <typename Arg1, typename Arg2> + void generateLookupCall(uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2) + { + _as->loadPtr(Assembler::Address(Assembler::ContextRegister, offsetof(QV4::ExecutionContext, lookups)), + Assembler::ReturnValueRegister); + + Assembler::Pointer lookupAddr(Assembler::ReturnValueRegister, index * sizeof(QV4::Lookup)); + + Assembler::Address getterSetter = lookupAddr; + getterSetter.offset += getterSetterOffset; + + _as->generateFunctionCallImp(Assembler::Void, "lookup getter/setter", getterSetter, lookupAddr, arg1, arg2); + } + + template <typename Arg1> + void generateLookupCall(uint index, uint getterSetterOffset, Arg1 arg1) + { + generateLookupCall(index, getterSetterOffset, arg1, Assembler::VoidType()); + } + + V4IR::BasicBlock *_block; + V4IR::Function* _function; + QV4::Function* _vmFunction; + QVector<QV4::Lookup> _lookups; + Assembler* _as; + QSet<V4IR::BasicBlock*> _reentryBlocks; + int _locals; +}; + +class Q_QML_EXPORT ISelFactory: public EvalISelFactory +{ +public: + virtual ~ISelFactory() {} + virtual EvalInstructionSelection *create(QV4::ExecutionEngine *engine, V4IR::Module *module) + { return new InstructionSelection(engine, module); } + virtual bool jitCompileRegexps() const + { return true; } +}; + +} // end of namespace MASM +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ISEL_MASM_P_H diff --git a/src/qml/qml/v4/qv4isel_p.cpp b/src/qml/qml/v4/qv4isel_p.cpp new file mode 100644 index 0000000000..ca8d249f9f --- /dev/null +++ b/src/qml/qml/v4/qv4isel_p.cpp @@ -0,0 +1,440 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4debugging_p.h" +#include "qv4engine_p.h" +#include "qv4jsir_p.h" +#include "qv4isel_p.h" +#include "qv4isel_util_p.h" +#include "qv4functionobject_p.h" +#include "qv4function_p.h" + +#include <QString> + +#include <cassert> + +namespace { +QTextStream qout(stderr, QIODevice::WriteOnly); +} // anonymous namespace + +using namespace QQmlJS; +using namespace QQmlJS::V4IR; + +EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutionEngine *engine, Module *module) + : _engine(engine) + , useFastLookups(true) +{ + assert(engine); + assert(module); + + createFunctionMapping(0, module->rootFunction); + foreach (V4IR::Function *f, module->functions) { + assert(_irToVM.contains(f)); + } +} + +EvalInstructionSelection::~EvalInstructionSelection() +{} + +EvalISelFactory::~EvalISelFactory() +{} + +QV4::Function *EvalInstructionSelection::createFunctionMapping(QV4::Function *outer, Function *irFunction) +{ + QV4::Function *vmFunction = _engine->newFunction(irFunction->name ? *irFunction->name : QString()); + _irToVM.insert(irFunction, vmFunction); + + vmFunction->hasDirectEval = irFunction->hasDirectEval; + vmFunction->usesArgumentsObject = irFunction->usesArgumentsObject; + vmFunction->hasNestedFunctions = !irFunction->nestedFunctions.isEmpty(); + vmFunction->isStrict = irFunction->isStrict; + vmFunction->outer = outer; + vmFunction->isNamedExpression = irFunction->isNamedExpression; + vmFunction->sourceFile = irFunction->sourceFile; + + if (outer) + outer->nestedFunctions.append(vmFunction); + + foreach (const QString *formal, irFunction->formals) + if (formal) + vmFunction->formals.append(_engine->newString(*formal)); + foreach (const QString *local, irFunction->locals) + if (local) + vmFunction->locals.append(_engine->newString(*local)); + + foreach (V4IR::Function *function, irFunction->nestedFunctions) + createFunctionMapping(vmFunction, function); + + return vmFunction; +} + +QV4::Function *EvalInstructionSelection::vmFunction(Function *f) { + QV4::Function *function = _irToVM[f]; + if (!function->code) + run(function, f); + return function; +} + +void IRDecoder::visitMove(V4IR::Move *s) +{ + if (s->op == V4IR::OpInvalid) { + if (V4IR::Name *n = s->target->asName()) { + if (s->source->asTemp()) { + setActivationProperty(s->source->asTemp(), *n->id); + return; + } + } else if (V4IR::Temp *t = s->target->asTemp()) { + if (V4IR::Name *n = s->source->asName()) { + if (*n->id == QStringLiteral("this")) // TODO: `this' should be a builtin. + loadThisObject(t); + else + getActivationProperty(n, t); + return; + } else if (V4IR::Const *c = s->source->asConst()) { + loadConst(c, t); + return; + } else if (V4IR::Temp *t2 = s->source->asTemp()) { + copyValue(t2, t); + return; + } else if (V4IR::String *str = s->source->asString()) { + loadString(*str->value, t); + return; + } else if (V4IR::RegExp *re = s->source->asRegExp()) { + loadRegexp(re, t); + return; + } else if (V4IR::Closure *clos = s->source->asClosure()) { + initClosure(clos, t); + return; + } else if (V4IR::New *ctor = s->source->asNew()) { + if (Name *func = ctor->base->asName()) { + constructActivationProperty(func, ctor->args, t); + return; + } else if (V4IR::Member *member = ctor->base->asMember()) { + constructProperty(member->base->asTemp(), *member->name, ctor->args, t); + return; + } else if (V4IR::Temp *value = ctor->base->asTemp()) { + constructValue(value, ctor->args, t); + return; + } + } else if (V4IR::Member *m = s->source->asMember()) { + if (V4IR::Temp *base = m->base->asTemp()) { + getProperty(base, *m->name, t); + return; + } + } else if (V4IR::Subscript *ss = s->source->asSubscript()) { + getElement(ss->base->asTemp(), ss->index->asTemp(), t); + return; + } else if (V4IR::Unop *u = s->source->asUnop()) { + if (V4IR::Temp *e = u->expr->asTemp()) { + unop(u->op, e, t); + return; + } + } else if (V4IR::Binop *b = s->source->asBinop()) { + binop(b->op, b->left, b->right, t); + return; + } else if (V4IR::Call *c = s->source->asCall()) { + if (c->base->asName()) { + callBuiltin(c, t); + return; + } else if (Member *member = c->base->asMember()) { + Q_ASSERT(member->base->asTemp()); + callProperty(member->base->asTemp(), *member->name, c->args, t); + return; + } else if (Subscript *s = c->base->asSubscript()) { + Q_ASSERT(s->base->asTemp()); + Q_ASSERT(s->index->asTemp()); + callSubscript(s->base->asTemp(), s->index->asTemp(), c->args, t); + return; + } else if (V4IR::Temp *value = c->base->asTemp()) { + callValue(value, c->args, t); + return; + } + } else if (V4IR::Convert *c = s->source->asConvert()) { + Q_ASSERT(c->expr->asTemp()); + convertType(c->expr->asTemp(), t); + return; + } + } else if (V4IR::Member *m = s->target->asMember()) { + if (V4IR::Temp *base = m->base->asTemp()) { + if (s->source->asTemp()) { + setProperty(s->source->asTemp(), base, *m->name); + return; + } + } + } else if (V4IR::Subscript *ss = s->target->asSubscript()) { + if (s->source->asTemp()) { + setElement(s->source->asTemp(), ss->base->asTemp(), ss->index->asTemp()); + return; + } + } + } else { + // inplace assignment, e.g. x += 1, ++x, ... + if (V4IR::Temp *t = s->target->asTemp()) { + if (s->source->asTemp()) { + binop(s->op, t, s->source->asTemp(), t); + return; + } + } else if (V4IR::Name *n = s->target->asName()) { + if (s->source->asTemp()) { + inplaceNameOp(s->op, s->source->asTemp(), *n->id); + return; + } + } else if (V4IR::Subscript *ss = s->target->asSubscript()) { + if (s->source->asTemp()) { + inplaceElementOp(s->op, s->source->asTemp(), ss->base->asTemp(), + ss->index->asTemp()); + return; + } + } else if (V4IR::Member *m = s->target->asMember()) { + if (s->source->asTemp()) { + inplaceMemberOp(s->op, s->source->asTemp(), m->base->asTemp(), *m->name); + return; + } + } + } + + // For anything else...: + Q_UNIMPLEMENTED(); + s->dump(qout, V4IR::Stmt::MIR); + qout << endl; + assert(!"TODO"); +} + +IRDecoder::~IRDecoder() +{ +} + +void IRDecoder::visitExp(V4IR::Exp *s) +{ + if (V4IR::Call *c = s->expr->asCall()) { + // These are calls where the result is ignored. + if (c->base->asName()) { + callBuiltin(c, 0); + } else if (Temp *value = c->base->asTemp()) { + callValue(value, c->args, 0); + } else if (Member *member = c->base->asMember()) { + Q_ASSERT(member->base->asTemp()); + callProperty(member->base->asTemp(), *member->name, c->args, 0); + } else if (Subscript *s = c->base->asSubscript()) { + Q_ASSERT(s->base->asTemp()); + Q_ASSERT(s->index->asTemp()); + callSubscript(s->base->asTemp(), s->index->asTemp(), c->args, 0); + } else { + Q_UNIMPLEMENTED(); + } + } else { + Q_UNIMPLEMENTED(); + } +} + +void IRDecoder::callBuiltin(V4IR::Call *call, V4IR::Temp *result) +{ + V4IR::Name *baseName = call->base->asName(); + assert(baseName != 0); + + switch (baseName->builtin) { + case V4IR::Name::builtin_invalid: + callBuiltinInvalid(baseName, call->args, result); + return; + + case V4IR::Name::builtin_typeof: { + if (V4IR::Member *m = call->args->expr->asMember()) { + callBuiltinTypeofMember(m->base->asTemp(), *m->name, result); + return; + } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinTypeofSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (V4IR::Name *n = call->args->expr->asName()) { + callBuiltinTypeofName(*n->id, result); + return; + } else if (V4IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinTypeofValue(arg, result); + return; + } + } break; + + case V4IR::Name::builtin_delete: { + if (V4IR::Member *m = call->args->expr->asMember()) { + callBuiltinDeleteMember(m->base->asTemp(), *m->name, result); + return; + } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinDeleteSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (V4IR::Name *n = call->args->expr->asName()) { + callBuiltinDeleteName(*n->id, result); + return; + } else if (call->args->expr->asTemp()){ + // TODO: should throw in strict mode + callBuiltinDeleteValue(result); + return; + } + } break; + + case V4IR::Name::builtin_postincrement: { + if (V4IR::Member *m = call->args->expr->asMember()) { + callBuiltinPostIncrementMember(m->base->asTemp(), *m->name, result); + return; + } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinPostIncrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (V4IR::Name *n = call->args->expr->asName()) { + callBuiltinPostIncrementName(*n->id, result); + return; + } else if (V4IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinPostIncrementValue(arg, result); + return; + } + } break; + + case V4IR::Name::builtin_postdecrement: { + if (V4IR::Member *m = call->args->expr->asMember()) { + callBuiltinPostDecrementMember(m->base->asTemp(), *m->name, result); + return; + } else if (V4IR::Subscript *ss = call->args->expr->asSubscript()) { + callBuiltinPostDecrementSubscript(ss->base->asTemp(), ss->index->asTemp(), result); + return; + } else if (V4IR::Name *n = call->args->expr->asName()) { + callBuiltinPostDecrementName(*n->id, result); + return; + } else if (V4IR::Temp *arg = call->args->expr->asTemp()){ + assert(arg != 0); + callBuiltinPostDecrementValue(arg, result); + return; + } + } break; + + case V4IR::Name::builtin_throw: { + V4IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinThrow(arg); + } return; + + case V4IR::Name::builtin_finish_try: + callBuiltinFinishTry(); + return; + + case V4IR::Name::builtin_foreach_iterator_object: { + V4IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinForeachIteratorObject(arg, result); + } return; + + case V4IR::Name::builtin_foreach_next_property_name: { + V4IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinForeachNextPropertyname(arg, result); + } return; + case V4IR::Name::builtin_push_with_scope: { + V4IR::Temp *arg = call->args->expr->asTemp(); + assert(arg != 0); + callBuiltinPushWithScope(arg); + } return; + + case V4IR::Name::builtin_pop_scope: + callBuiltinPopScope(); + return; + + case V4IR::Name::builtin_declare_vars: { + if (!call->args) + return; + V4IR::Const *deletable = call->args->expr->asConst(); + assert(deletable->type == V4IR::BoolType); + for (V4IR::ExprList *it = call->args->next; it; it = it->next) { + V4IR::Name *arg = it->expr->asName(); + assert(arg != 0); + callBuiltinDeclareVar(deletable->value != 0, *arg->id); + } + } return; + + case V4IR::Name::builtin_define_getter_setter: { + if (!call->args) + return; + V4IR::ExprList *args = call->args; + V4IR::Temp *object = args->expr->asTemp(); + assert(object); + args = args->next; + assert(args); + V4IR::Name *name = args->expr->asName(); + args = args->next; + assert(args); + V4IR::Temp *getter = args->expr->asTemp(); + args = args->next; + assert(args); + V4IR::Temp *setter = args->expr->asTemp(); + + callBuiltinDefineGetterSetter(object, *name->id, getter, setter); + } return; + + case V4IR::Name::builtin_define_property: { + if (!call->args) + return; + V4IR::ExprList *args = call->args; + V4IR::Temp *object = args->expr->asTemp(); + assert(object); + args = args->next; + assert(args); + V4IR::Name *name = args->expr->asName(); + args = args->next; + assert(args); + V4IR::Temp *value = args->expr->asTemp(); + + callBuiltinDefineProperty(object, *name->id, value); + } return; + + case V4IR::Name::builtin_define_array: + callBuiltinDefineArray(result, call->args); + return; + + case V4IR::Name::builtin_define_object_literal: + callBuiltinDefineObjectLiteral(result, call->args); + return; + + default: + break; + } + + Q_UNIMPLEMENTED(); + call->dump(qout); qout << endl; + assert(!"TODO!"); + Q_UNREACHABLE(); +} diff --git a/src/qml/qml/v4/qv4isel_p.h b/src/qml/qml/v4/qv4isel_p.h new file mode 100644 index 0000000000..ca49b4c8b1 --- /dev/null +++ b/src/qml/qml/v4/qv4isel_p.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ISEL_P_H +#define QV4ISEL_P_H + +#include "qv4global_p.h" +#include "qv4jsir_p.h" + +#include <qglobal.h> +#include <QHash> + +QT_BEGIN_NAMESPACE + +namespace QV4 { +struct ExecutionEngine; +struct Function; +} + +namespace QQmlJS { + +class Q_QML_EXPORT EvalInstructionSelection +{ +public: + EvalInstructionSelection(QV4::ExecutionEngine *engine, V4IR::Module *module); + virtual ~EvalInstructionSelection() = 0; + + QV4::Function *vmFunction(V4IR::Function *f); + + void setUseFastLookups(bool b) { useFastLookups = b; } + +protected: + QV4::Function *createFunctionMapping(QV4::Function *outer, V4IR::Function *irFunction); + QV4::ExecutionEngine *engine() const { return _engine; } + virtual void run(QV4::Function *vmFunction, V4IR::Function *function) = 0; + +private: + QV4::ExecutionEngine *_engine; + QHash<V4IR::Function *, QV4::Function *> _irToVM; +protected: + bool useFastLookups; +}; + +class Q_QML_EXPORT EvalISelFactory +{ +public: + virtual ~EvalISelFactory() = 0; + virtual EvalInstructionSelection *create(QV4::ExecutionEngine *engine, V4IR::Module *module) = 0; + virtual bool jitCompileRegexps() const = 0; +}; + +namespace V4IR { +class Q_QML_EXPORT IRDecoder: protected V4IR::StmtVisitor +{ +public: + virtual ~IRDecoder() = 0; + + virtual void visitPhi(V4IR::Phi *) {} + +public: // visitor methods for StmtVisitor: + virtual void visitMove(V4IR::Move *s); + virtual void visitExp(V4IR::Exp *s); + +public: // to implement by subclasses: + virtual void callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void callBuiltinTypeofMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinTypeofSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; + virtual void callBuiltinTypeofName(const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinTypeofValue(V4IR::Temp *value, V4IR::Temp *result) = 0; + virtual void callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; + virtual void callBuiltinDeleteName(const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinDeleteValue(V4IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementName(const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinPostDecrementValue(V4IR::Temp *value, V4IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementName(const QString &name, V4IR::Temp *result) = 0; + virtual void callBuiltinPostIncrementValue(V4IR::Temp *value, V4IR::Temp *result) = 0; + virtual void callBuiltinThrow(V4IR::Temp *arg) = 0; + virtual void callBuiltinFinishTry() = 0; + virtual void callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result) = 0; + virtual void callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result) = 0; + virtual void callBuiltinPushWithScope(V4IR::Temp *arg) = 0; + virtual void callBuiltinPopScope() = 0; + virtual void callBuiltinDeclareVar(bool deletable, const QString &name) = 0; + virtual void callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter) = 0; + virtual void callBuiltinDefineProperty(V4IR::Temp *object, const QString &name, V4IR::Temp *value) = 0; + virtual void callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args) = 0; + virtual void callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args) = 0; + virtual void callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void callProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void callSubscript(V4IR::Temp *base, V4IR::Temp *index, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void convertType(V4IR::Temp *source, V4IR::Temp *target) = 0; + virtual void constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) = 0; + virtual void loadThisObject(V4IR::Temp *temp) = 0; + virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) = 0; + virtual void loadString(const QString &str, V4IR::Temp *targetTemp) = 0; + virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) = 0; + virtual void getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp) = 0; + virtual void setActivationProperty(V4IR::Temp *source, const QString &targetName) = 0; + virtual void initClosure(V4IR::Closure *closure, V4IR::Temp *target) = 0; + virtual void getProperty(V4IR::Temp *base, const QString &name, V4IR::Temp *target) = 0; + virtual void setProperty(V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) = 0; + virtual void getElement(V4IR::Temp *base, V4IR::Temp *index, V4IR::Temp *target) = 0; + virtual void setElement(V4IR::Temp *source, V4IR::Temp *targetBase, V4IR::Temp *targetIndex) = 0; + virtual void copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) = 0; + virtual void unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp) = 0; + virtual void binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target) = 0; + virtual void inplaceNameOp(V4IR::AluOp oper, V4IR::Temp *rightSource, const QString &targetName) = 0; + virtual void inplaceElementOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBaseTemp, V4IR::Temp *targetIndexTemp) = 0; + virtual void inplaceMemberOp(V4IR::AluOp oper, V4IR::Temp *source, V4IR::Temp *targetBase, const QString &targetName) = 0; + +private: + void callBuiltin(V4IR::Call *c, V4IR::Temp *temp); +}; +} // namespace IR + +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ISEL_P_H diff --git a/src/qml/qml/v4/qv4isel_util_p.h b/src/qml/qml/v4/qv4isel_util_p.h new file mode 100644 index 0000000000..5aedaaff1b --- /dev/null +++ b/src/qml/qml/v4/qv4isel_util_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ISEL_UTIL_P_H +#define QV4ISEL_UTIL_P_H + +#include "qv4runtime_p.h" +#include "qv4jsir_p.h" + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { + +inline QV4::Value convertToValue(V4IR::Const *c) +{ + switch (c->type) { + case V4IR::MissingType: + return QV4::Value::emptyValue(); + case V4IR::NullType: + return QV4::Value::nullValue(); + case V4IR::UndefinedType: + return QV4::Value::undefinedValue(); + case V4IR::BoolType: + return QV4::Value::fromBoolean(c->value != 0); + case V4IR::SInt32Type: + return QV4::Value::fromInt32(int(c->value)); + case V4IR::UInt32Type: + return QV4::Value::fromUInt32(unsigned(c->value)); + case V4IR::DoubleType: + return QV4::Value::fromDouble(c->value); + case V4IR::NumberType: { + int ival = (int)c->value; + // +0 != -0, so we need to convert to double when negating 0 + if (ival == c->value && !(c->value == 0 && isNegative(c->value))) { + return QV4::Value::fromInt32(ival); + } else { + return QV4::Value::fromDouble(c->value); + } + } + default: + Q_UNREACHABLE(); + } +} + +} // namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4ISEL_UTIL_P_H diff --git a/src/qml/qml/v4/qv4jsir.cpp b/src/qml/qml/v4/qv4jsir.cpp new file mode 100644 index 0000000000..7f8d257429 --- /dev/null +++ b/src/qml/qml/v4/qv4jsir.cpp @@ -0,0 +1,1024 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4jsir_p.h" +#include <private/qqmljsast_p.h> + +#include <QtCore/qtextstream.h> +#include <QtCore/qdebug.h> +#include <QtCore/qset.h> +#include <cmath> +#include <cassert> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace V4IR { + +QString typeName(Type t) +{ + switch (t) { + case UnknownType: return QStringLiteral(""); + case MissingType: return QStringLiteral("missing"); + case UndefinedType: return QStringLiteral("undefined"); + case NullType: return QStringLiteral("null"); + case BoolType: return QStringLiteral("bool"); + case UInt32Type: return QStringLiteral("uint32"); + case SInt32Type: return QStringLiteral("int32"); + case DoubleType: return QStringLiteral("double"); + case NumberType: return QStringLiteral("number"); + case StringType: return QStringLiteral("string"); + case ObjectType: return QStringLiteral("object"); + default: return QStringLiteral("multiple"); + } +} + +const char *opname(AluOp op) +{ + switch (op) { + case OpInvalid: return "?"; + + case OpIfTrue: return "(bool)"; + case OpNot: return "!"; + case OpUMinus: return "-"; + case OpUPlus: return "+"; + case OpCompl: return "~"; + case OpIncrement: return "++"; + case OpDecrement: return "--"; + + case OpBitAnd: return "&"; + case OpBitOr: return "|"; + case OpBitXor: return "^"; + + case OpAdd: return "+"; + case OpSub: return "-"; + case OpMul: return "*"; + case OpDiv: return "/"; + case OpMod: return "%"; + + case OpLShift: return "<<"; + case OpRShift: return ">>"; + case OpURShift: return ">>>"; + + case OpGt: return ">"; + case OpLt: return "<"; + case OpGe: return ">="; + case OpLe: return "<="; + case OpEqual: return "=="; + case OpNotEqual: return "!="; + case OpStrictEqual: return "==="; + case OpStrictNotEqual: return "!=="; + + case OpInstanceof: return "instanceof"; + case OpIn: return "in"; + + case OpAnd: return "&&"; + case OpOr: return "||"; + + default: return "?"; + + } // switch +} + +AluOp binaryOperator(int op) +{ + switch (static_cast<QSOperator::Op>(op)) { + case QSOperator::Add: return OpAdd; + case QSOperator::And: return OpAnd; + case QSOperator::BitAnd: return OpBitAnd; + case QSOperator::BitOr: return OpBitOr; + case QSOperator::BitXor: return OpBitXor; + case QSOperator::Div: return OpDiv; + case QSOperator::Equal: return OpEqual; + case QSOperator::Ge: return OpGe; + case QSOperator::Gt: return OpGt; + case QSOperator::Le: return OpLe; + case QSOperator::LShift: return OpLShift; + case QSOperator::Lt: return OpLt; + case QSOperator::Mod: return OpMod; + case QSOperator::Mul: return OpMul; + case QSOperator::NotEqual: return OpNotEqual; + case QSOperator::Or: return OpOr; + case QSOperator::RShift: return OpRShift; + case QSOperator::StrictEqual: return OpStrictEqual; + case QSOperator::StrictNotEqual: return OpStrictNotEqual; + case QSOperator::Sub: return OpSub; + case QSOperator::URShift: return OpURShift; + case QSOperator::InstanceOf: return OpInstanceof; + case QSOperator::In: return OpIn; + default: return OpInvalid; + } +} + +struct RemoveSharedExpressions: V4IR::StmtVisitor, V4IR::ExprVisitor +{ + CloneExpr clone; + QSet<Expr *> subexpressions; // contains all the non-cloned subexpressions in the given function + Expr *uniqueExpr; + + RemoveSharedExpressions(): uniqueExpr(0) {} + + void operator()(V4IR::Function *function) + { + subexpressions.clear(); + + foreach (BasicBlock *block, function->basicBlocks) { + clone.setBasicBlock(block); + + foreach (Stmt *s, block->statements) { + s->accept(this); + } + } + } + + template <typename _Expr> + _Expr *cleanup(_Expr *expr) + { + if (subexpressions.contains(expr)) { + // the cloned expression is unique by definition + // so we don't need to add it to `subexpressions'. + return clone(expr); + } + + subexpressions.insert(expr); + V4IR::Expr *e = expr; + qSwap(uniqueExpr, e); + expr->accept(this); + qSwap(uniqueExpr, e); + return static_cast<_Expr *>(e); + } + + // statements + virtual void visitExp(Exp *s) + { + s->expr = cleanup(s->expr); + } + + virtual void visitMove(Move *s) + { + s->target = cleanup(s->target); + s->source = cleanup(s->source); + } + + virtual void visitJump(Jump *) + { + // nothing to do for Jump statements + } + + virtual void visitCJump(CJump *s) + { + s->cond = cleanup(s->cond); + } + + virtual void visitRet(Ret *s) + { + s->expr = cleanup(s->expr); + } + + virtual void visitTry(Try *) + { + // nothing to do for Try statements + } + + virtual void visitPhi(V4IR::Phi *) { Q_UNIMPLEMENTED(); abort(); } + + // expressions + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitTemp(Temp *) {} + virtual void visitClosure(Closure *) {} + + virtual void visitConvert(Convert *e) + { + e->expr = cleanup(e->expr); + } + + virtual void visitUnop(Unop *e) + { + e->expr = cleanup(e->expr); + } + + virtual void visitBinop(Binop *e) + { + e->left = cleanup(e->left); + e->right = cleanup(e->right); + } + + virtual void visitCall(Call *e) + { + e->base = cleanup(e->base); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr = cleanup(it->expr); + } + + virtual void visitNew(New *e) + { + e->base = cleanup(e->base); + for (V4IR::ExprList *it = e->args; it; it = it->next) + it->expr = cleanup(it->expr); + } + + virtual void visitSubscript(Subscript *e) + { + e->base = cleanup(e->base); + e->index = cleanup(e->index); + } + + virtual void visitMember(Member *e) + { + e->base = cleanup(e->base); + } +}; + +static QString dumpStart(const Expr *e) { + if (e->type == UnknownType) +// return QStringLiteral("**UNKNOWN**"); + return QString(); + else + return typeName(e->type) + QStringLiteral("{"); +} + +static const char *dumpEnd(const Expr *e) { + if (e->type == UnknownType) + return ""; + else + return "}"; +} + +void Const::dump(QTextStream &out) const +{ + if (type != UndefinedType && type != NullType) + out << dumpStart(this); + switch (type) { + case QQmlJS::V4IR::UndefinedType: + out << "undefined"; + break; + case QQmlJS::V4IR::NullType: + out << "null"; + break; + case QQmlJS::V4IR::BoolType: + out << (value ? "true" : "false"); + break; + case QQmlJS::V4IR::MissingType: + out << "missing"; + break; + default: + out << QString::number(value, 'g', 16); + break; + } + if (type != UndefinedType && type != NullType) + out << dumpEnd(this); +} + +void String::dump(QTextStream &out) const +{ + out << '"' << escape(*value) << '"'; +} + +QString String::escape(const QString &s) +{ + QString r; + for (int i = 0; i < s.length(); ++i) { + const QChar ch = s.at(i); + if (ch == QLatin1Char('\n')) + r += QStringLiteral("\\n"); + else if (ch == QLatin1Char('\r')) + r += QStringLiteral("\\r"); + else if (ch == QLatin1Char('\\')) + r += QStringLiteral("\\\\"); + else if (ch == QLatin1Char('"')) + r += QStringLiteral("\\\""); + else if (ch == QLatin1Char('\'')) + r += QStringLiteral("\\'"); + else + r += ch; + } + return r; +} + +void RegExp::dump(QTextStream &out) const +{ + char f[3]; + int i = 0; + if (flags & RegExp_Global) + f[i++] = 'g'; + if (flags & RegExp_IgnoreCase) + f[i++] = 'i'; + if (flags & RegExp_Multiline) + f[i++] = 'm'; + f[i] = 0; + + out << '/' << *value << '/' << f; +} + +void Name::initGlobal(const QString *id, quint32 line, quint32 column) +{ + this->id = id; + this->builtin = builtin_invalid; + this->global = true; + this->line = line; + this->column = column; +} + +void Name::init(const QString *id, quint32 line, quint32 column) +{ + this->id = id; + this->builtin = builtin_invalid; + this->global = false; + this->line = line; + this->column = column; +} + +void Name::init(Builtin builtin, quint32 line, quint32 column) +{ + this->id = 0; + this->builtin = builtin; + this->global = false; + this->line = line; + this->column = column; +} + +static const char *builtin_to_string(Name::Builtin b) +{ + switch (b) { + case Name::builtin_invalid: + return "builtin_invalid"; + case Name::builtin_typeof: + return "builtin_typeof"; + case Name::builtin_delete: + return "builtin_delete"; + case Name::builtin_postincrement: + return "builtin_postincrement"; + case Name::builtin_postdecrement: + return "builtin_postdecrement"; + case Name::builtin_throw: + return "builtin_throw"; + case Name::builtin_finish_try: + return "builtin_finish_try"; + case V4IR::Name::builtin_foreach_iterator_object: + return "builtin_foreach_iterator_object"; + case V4IR::Name::builtin_foreach_next_property_name: + return "builtin_foreach_next_property_name"; + case V4IR::Name::builtin_push_with_scope: + return "builtin_push_with_scope"; + case V4IR::Name::builtin_pop_scope: + return "builtin_pop_scope"; + case V4IR::Name::builtin_declare_vars: + return "builtin_declare_vars"; + case V4IR::Name::builtin_define_property: + return "builtin_define_property"; + case V4IR::Name::builtin_define_array: + return "builtin_define_array"; + case V4IR::Name::builtin_define_getter_setter: + return "builtin_define_getter_setter"; + case V4IR::Name::builtin_define_object_literal: + return "builtin_define_object_literal"; + } + return "builtin_(###FIXME)"; +}; + +void Name::dump(QTextStream &out) const +{ + if (id) + out << *id; + else + out << builtin_to_string(builtin); +} + +void Temp::dump(QTextStream &out) const +{ + out << dumpStart(this); + switch (kind) { + case Formal: out << '#' << index; break; + case ScopedFormal: out << '#' << index + << '@' << scope; break; + case Local: out << '$' << index; break; + case ScopedLocal: out << '$' << index + << '@' << scope; break; + case VirtualRegister: out << '%' << index; break; + default: out << "INVALID"; + } + out << dumpEnd(this); +} + +bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW +{ + if (t1.kind < t2.kind) return true; + if (t1.kind > t2.kind) return false; + if (t1.index < t2.index) return true; + if (t1.index > t2.index) return false; + return t1.scope < t2.scope; +} + +void Closure::dump(QTextStream &out) const +{ + QString name = value->name ? *value->name : QString(); + if (name.isEmpty()) + name.sprintf("%p", value); + out << "closure(" << name << ')'; +} + +void Convert::dump(QTextStream &out) const +{ + out << dumpStart(this); + out << "convert("; + expr->dump(out); + out << ')' << dumpEnd(this); +} + +void Unop::dump(QTextStream &out) const +{ + out << dumpStart(this) << opname(op); + expr->dump(out); + out << dumpEnd(this); +} + +void Binop::dump(QTextStream &out) const +{ + out << dumpStart(this); + left->dump(out); + out << ' ' << opname(op) << ' '; + right->dump(out); + out << dumpEnd(this); +} + +void Call::dump(QTextStream &out) const +{ + base->dump(out); + out << '('; + for (ExprList *it = args; it; it = it->next) { + if (it != args) + out << ", "; + it->expr->dump(out); + } + out << ')'; +} + +void New::dump(QTextStream &out) const +{ + out << "new "; + base->dump(out); + out << '('; + for (ExprList *it = args; it; it = it->next) { + if (it != args) + out << ", "; + it->expr->dump(out); + } + out << ')'; +} + +void Subscript::dump(QTextStream &out) const +{ + base->dump(out); + out << '['; + index->dump(out); + out << ']'; +} + +void Member::dump(QTextStream &out) const +{ + base->dump(out); + out << '.' << *name; +} + +void Exp::dump(QTextStream &out, Mode) +{ + out << "(void) "; + expr->dump(out); + out << ';'; +} + +void Move::dump(QTextStream &out, Mode) +{ + target->dump(out); + out << ' '; + if (op != OpInvalid) + out << opname(op); + out << "= "; +// if (source->type != target->type) +// out << typeName(source->type) << "_to_" << typeName(target->type) << '('; + source->dump(out); +// if (source->type != target->type) +// out << ')'; + out << ';'; +} + +void Jump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "goto " << 'L' << target->index << ';'; +} + +void CJump::dump(QTextStream &out, Mode mode) +{ + Q_UNUSED(mode); + out << "if ("; + cond->dump(out); + if (mode == HIR) + out << ") goto " << 'L' << iftrue->index << "; else goto " << 'L' << iffalse->index << ';'; + else + out << ") goto " << 'L' << iftrue->index << ";"; +} + +void Ret::dump(QTextStream &out, Mode) +{ + out << "return"; + if (expr) { + out << ' '; + expr->dump(out); + } + out << ';'; +} + +void Try::dump(QTextStream &out, Stmt::Mode mode) +{ + out << "try L" << tryBlock->index << "; catch exception in "; + exceptionVar->dump(out); + out << " with the name " << exceptionVarName << " and go to L" << catchBlock->index << ';'; +} + +void Phi::dump(QTextStream &out, Stmt::Mode mode) +{ + targetTemp->dump(out); + out << " = phi("; + for (int i = 0, ei = incoming.size(); i < ei; ++i) { + if (i > 0) + out << ", "; + if (incoming[i]) + incoming[i]->dump(out); + } + out << ");"; +} + +Function *Module::newFunction(const QString &name, Function *outer) +{ + Function *f = new Function(this, outer, name); + functions.append(f); + if (!outer) { + assert(!rootFunction); + rootFunction = f; + } else { + outer->nestedFunctions.append(f); + } + return f; +} + +Module::~Module() +{ + foreach (Function *f, functions) { + delete f; + } +} + +Function::~Function() +{ + // destroy the Stmt::Data blocks manually, because memory pool cleanup won't + // call the Stmt destructors. + foreach (V4IR::BasicBlock *b, basicBlocks) + foreach (V4IR::Stmt *s, b->statements) + s->destroyData(); + + qDeleteAll(basicBlocks); + pool = 0; + module = 0; +} + + +const QString *Function::newString(const QString &text) +{ + return &*strings.insert(text); +} + +BasicBlock *Function::newBasicBlock(BasicBlock *containingLoop, BasicBlockInsertMode mode) +{ + BasicBlock *block = new BasicBlock(this, containingLoop); + return mode == InsertBlock ? insertBasicBlock(block) : block; +} + +void Function::dump(QTextStream &out, Stmt::Mode mode) +{ + QString n = name ? *name : QString(); + if (n.isEmpty()) + n.sprintf("%p", this); + out << "function " << n << "() {" << endl; + foreach (const QString *formal, formals) + out << "\treceive " << *formal << ';' << endl; + foreach (const QString *local, locals) + out << "\tlocal " << *local << ';' << endl; + foreach (BasicBlock *bb, basicBlocks) + bb->dump(out, mode); + out << '}' << endl; +} + +void Function::removeSharedExpressions() +{ + RemoveSharedExpressions removeSharedExpressions; + removeSharedExpressions(this); +} + +int Function::indexOfArgument(const QStringRef &string) const +{ + for (int i = formals.size() - 1; i >= 0; --i) { + if (*formals.at(i) == string) + return i; + } + return -1; +} +unsigned BasicBlock::newTemp() +{ + return function->tempCount++; +} + +Temp *BasicBlock::TEMP(unsigned index) +{ + Temp *e = function->New<Temp>(); + e->init(Temp::VirtualRegister, index, 0); + return e; +} + +Temp *BasicBlock::ARG(unsigned index, unsigned scope) +{ + Temp *e = function->New<Temp>(); + e->init(scope ? Temp::ScopedFormal : Temp::Formal, index, scope); + return e; +} + +Temp *BasicBlock::LOCAL(unsigned index, unsigned scope) +{ + Temp *e = function->New<Temp>(); + e->init(scope ? Temp::ScopedLocal : Temp::Local, index, scope); + return e; +} + +Expr *BasicBlock::CONST(Type type, double value) +{ + Const *e = function->New<Const>(); + if (type == NumberType) { + int ival = (int)value; + // +0 != -0, so we need to convert to double when negating 0 + if (ival == value && !(value == 0 && isNegative(value))) + type = SInt32Type; + else + type = DoubleType; + } + e->init(type, value); + return e; +} + +Expr *BasicBlock::STRING(const QString *value) +{ + String *e = function->New<String>(); + e->init(value); + return e; +} + +Expr *BasicBlock::REGEXP(const QString *value, int flags) +{ + RegExp *e = function->New<RegExp>(); + e->init(value, flags); + return e; +} + +Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column) +{ + Name *e = function->New<Name>(); + e->init(function->newString(id), line, column); + return e; +} + +Name *BasicBlock::GLOBALNAME(const QString &id, quint32 line, quint32 column) +{ + Name *e = function->New<Name>(); + e->initGlobal(function->newString(id), line, column); + return e; +} + + +Name *BasicBlock::NAME(Name::Builtin builtin, quint32 line, quint32 column) +{ + Name *e = function->New<Name>(); + e->init(builtin, line, column); + return e; +} + +Closure *BasicBlock::CLOSURE(Function *function) +{ + Closure *clos = function->New<Closure>(); + clos->init(function); + return clos; +} + +Expr *BasicBlock::CONVERT(Expr *expr, Type type) +{ + Convert *e = function->New<Convert>(); + e->init(expr, type); + return e; +} + +Expr *BasicBlock::UNOP(AluOp op, Expr *expr) +{ + Unop *e = function->New<Unop>(); + e->init(op, expr); + return e; +} + +Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) +{ + Binop *e = function->New<Binop>(); + e->init(op, left, right); + return e; +} + +Expr *BasicBlock::CALL(Expr *base, ExprList *args) +{ + Call *e = function->New<Call>(); + e->init(base, args); + int argc = 0; + for (ExprList *it = args; it; it = it->next) + ++argc; + function->maxNumberOfArguments = qMax(function->maxNumberOfArguments, argc); + return e; +} + +Expr *BasicBlock::NEW(Expr *base, ExprList *args) +{ + New *e = function->New<New>(); + e->init(base, args); + return e; +} + +Expr *BasicBlock::SUBSCRIPT(Expr *base, Expr *index) +{ + Subscript *e = function->New<Subscript>(); + e->init(base, index); + return e; +} + +Expr *BasicBlock::MEMBER(Expr *base, const QString *name) +{ + Member*e = function->New<Member>(); + e->init(base, name); + return e; +} + +Stmt *BasicBlock::EXP(Expr *expr) +{ + if (isTerminated()) + return 0; + + Exp *s = function->New<Exp>(); + s->init(expr); + appendStatement(s); + return s; +} + +Stmt *BasicBlock::MOVE(Expr *target, Expr *source, AluOp op) +{ + if (isTerminated()) + return 0; + + Move *s = function->New<Move>(); + s->init(target, source, op); + appendStatement(s); + return s; +} + +Stmt *BasicBlock::JUMP(BasicBlock *target) +{ + if (isTerminated()) + return 0; + + Jump *s = function->New<Jump>(); + s->init(target); + appendStatement(s); + + assert(! out.contains(target)); + out.append(target); + + assert(! target->in.contains(this)); + target->in.append(this); + + return s; +} + +Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) +{ + if (isTerminated()) + return 0; + + if (iftrue == iffalse) { + MOVE(TEMP(newTemp()), cond); + return JUMP(iftrue); + } + + CJump *s = function->New<CJump>(); + s->init(cond, iftrue, iffalse); + appendStatement(s); + + assert(! out.contains(iftrue)); + out.append(iftrue); + + assert(! iftrue->in.contains(this)); + iftrue->in.append(this); + + assert(! out.contains(iffalse)); + out.append(iffalse); + + assert(! iffalse->in.contains(this)); + iffalse->in.append(this); + + return s; +} + +Stmt *BasicBlock::RET(Temp *expr) +{ + if (isTerminated()) + return 0; + + Ret *s = function->New<Ret>(); + s->init(expr); + appendStatement(s); + return s; +} + +Stmt *BasicBlock::TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar) +{ + if (isTerminated()) + return 0; + + Try *t = function->New<Try>(); + t->init(tryBlock, catchBlock, exceptionVarName, exceptionVar); + appendStatement(t); + + assert(! out.contains(tryBlock)); + out.append(tryBlock); + + assert(! out.contains(catchBlock)); + out.append(catchBlock); + + assert(! tryBlock->in.contains(this)); + tryBlock->in.append(this); + + assert(! catchBlock->in.contains(this)); + catchBlock->in.append(this); + + return t; +} + +void BasicBlock::dump(QTextStream &out, Stmt::Mode mode) +{ + out << 'L' << index << ':' << endl; + foreach (Stmt *s, statements) { + out << '\t'; + s->dump(out, mode); + + if (s->location.isValid()) + out << " // line: " << s->location.startLine << " ; column: " << s->location.startColumn; + + out << endl; + } +} + +void BasicBlock::appendStatement(Stmt *statement) +{ + if (nextLocation.isValid()) { + statement->location = nextLocation; + nextLocation = AST::SourceLocation(); + } + statements.append(statement); +} + +CloneExpr::CloneExpr(BasicBlock *block) + : block(block), cloned(0) +{ +} + +void CloneExpr::setBasicBlock(BasicBlock *block) +{ + this->block = block; +} + +ExprList *CloneExpr::clone(ExprList *list) +{ + if (! list) + return 0; + + ExprList *clonedList = block->function->New<V4IR::ExprList>(); + clonedList->init(clone(list->expr), clone(list->next)); + return clonedList; +} + +void CloneExpr::visitConst(Const *e) +{ + cloned = block->CONST(e->type, e->value); +} + +void CloneExpr::visitString(String *e) +{ + cloned = block->STRING(e->value); +} + +void CloneExpr::visitRegExp(RegExp *e) +{ + cloned = block->REGEXP(e->value, e->flags); +} + +void CloneExpr::visitName(Name *e) +{ + if (e->id) + cloned = block->NAME(*e->id, e->line, e->column); + else + cloned = block->NAME(e->builtin, e->line, e->column); +} + +void CloneExpr::visitTemp(Temp *e) +{ + Temp *t = block->function->New<Temp>(); + t->init(e->kind, e->index, e->scope); + cloned = t; +} + +void CloneExpr::visitClosure(Closure *e) +{ + cloned = block->CLOSURE(e->value); +} + +void CloneExpr::visitConvert(Convert *e) +{ + cloned = block->CONVERT(clone(e->expr), e->type); +} + +void CloneExpr::visitUnop(Unop *e) +{ + cloned = block->UNOP(e->op, clone(e->expr)); +} + +void CloneExpr::visitBinop(Binop *e) +{ + cloned = block->BINOP(e->op, clone(e->left), clone(e->right)); +} + +void CloneExpr::visitCall(Call *e) +{ + cloned = block->CALL(clone(e->base), clone(e->args)); +} + +void CloneExpr::visitNew(New *e) +{ + cloned = block->NEW(clone(e->base), clone(e->args)); +} + +void CloneExpr::visitSubscript(Subscript *e) +{ + cloned = block->SUBSCRIPT(clone(e->base), clone(e->index)); +} + +void CloneExpr::visitMember(Member *e) +{ + cloned = block->MEMBER(clone(e->base), e->name); +} + +} // end of namespace IR +} // end of namespace QQmlJS + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4jsir_p.h b/src/qml/qml/v4/qv4jsir_p.h new file mode 100644 index 0000000000..659d9870b1 --- /dev/null +++ b/src/qml/qml/v4/qv4jsir_p.h @@ -0,0 +1,890 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4JSIR_P_H +#define QV4JSIR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4global_p.h" +#include <private/qqmljsmemorypool_p.h> +#include <private/qqmljsastfwd_p.h> + +#include <QtCore/QVector> +#include <QtCore/QString> +#include <QtCore/QBitArray> +#include <QtCore/qurl.h> + +#ifdef CONST +#undef CONST +#endif + +QT_BEGIN_NAMESPACE + +class QTextStream; +class QQmlType; + +namespace QV4 { +struct ExecutionContext; +} + +namespace QQmlJS { + +inline bool isNegative(double d) +{ + uchar *dch = (uchar *)&d; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return (dch[0] & 0x80); + else + return (dch[7] & 0x80); + +} + +namespace V4IR { + +struct BasicBlock; +struct Function; +struct Module; + +struct Stmt; +struct Expr; + +// expressions +struct Const; +struct String; +struct RegExp; +struct Name; +struct Temp; +struct Closure; +struct Convert; +struct Unop; +struct Binop; +struct Call; +struct New; +struct Subscript; +struct Member; + +// statements +struct Exp; +struct Move; +struct Jump; +struct CJump; +struct Ret; +struct Try; +struct Phi; + +enum AluOp { + OpInvalid = 0, + + OpIfTrue, + OpNot, + OpUMinus, + OpUPlus, + OpCompl, + OpIncrement, + OpDecrement, + + OpBitAnd, + OpBitOr, + OpBitXor, + + OpAdd, + OpSub, + OpMul, + OpDiv, + OpMod, + + OpLShift, + OpRShift, + OpURShift, + + OpGt, + OpLt, + OpGe, + OpLe, + OpEqual, + OpNotEqual, + OpStrictEqual, + OpStrictNotEqual, + + OpInstanceof, + OpIn, + + OpAnd, + OpOr, + + LastAluOp = OpOr +}; +AluOp binaryOperator(int op); +const char *opname(V4IR::AluOp op); + +enum Type { + UnknownType = 0, + + MissingType = 1 << 0, + UndefinedType = 1 << 1, + NullType = 1 << 2, + BoolType = 1 << 3, + + SInt32Type = 1 << 4, + UInt32Type = 1 << 5, + DoubleType = 1 << 6, + NumberType = SInt32Type | UInt32Type | DoubleType, + + StringType = 1 << 7, + ObjectType = 1 << 8 +}; +QString typeName(Type t); + +struct ExprVisitor { + virtual ~ExprVisitor() {} + virtual void visitConst(Const *) = 0; + virtual void visitString(String *) = 0; + virtual void visitRegExp(RegExp *) = 0; + virtual void visitName(Name *) = 0; + virtual void visitTemp(Temp *) = 0; + virtual void visitClosure(Closure *) = 0; + virtual void visitConvert(Convert *) = 0; + virtual void visitUnop(Unop *) = 0; + virtual void visitBinop(Binop *) = 0; + virtual void visitCall(Call *) = 0; + virtual void visitNew(New *) = 0; + virtual void visitSubscript(Subscript *) = 0; + virtual void visitMember(Member *) = 0; +}; + +struct StmtVisitor { + virtual ~StmtVisitor() {} + virtual void visitExp(Exp *) = 0; + virtual void visitMove(Move *) = 0; + virtual void visitJump(Jump *) = 0; + virtual void visitCJump(CJump *) = 0; + virtual void visitRet(Ret *) = 0; + virtual void visitTry(Try *) = 0; + virtual void visitPhi(Phi *) = 0; +}; + +struct Expr { + Type type; + + Expr(): type(UnknownType) {} + virtual ~Expr() {} + virtual void accept(ExprVisitor *) = 0; + virtual bool isLValue() { return false; } + virtual Const *asConst() { return 0; } + virtual String *asString() { return 0; } + virtual RegExp *asRegExp() { return 0; } + virtual Name *asName() { return 0; } + virtual Temp *asTemp() { return 0; } + virtual Closure *asClosure() { return 0; } + virtual Convert *asConvert() { return 0; } + virtual Unop *asUnop() { return 0; } + virtual Binop *asBinop() { return 0; } + virtual Call *asCall() { return 0; } + virtual New *asNew() { return 0; } + virtual Subscript *asSubscript() { return 0; } + virtual Member *asMember() { return 0; } + virtual void dump(QTextStream &out) const = 0; +}; + +struct ExprList { + Expr *expr; + ExprList *next; + + void init(Expr *expr, ExprList *next = 0) + { + this->expr = expr; + this->next = next; + } +}; + +struct Const: Expr { + double value; + + void init(Type type, double value) + { + this->type = type; + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitConst(this); } + virtual Const *asConst() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct String: Expr { + const QString *value; + + void init(const QString *value) + { + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitString(this); } + virtual String *asString() { return this; } + + virtual void dump(QTextStream &out) const; + static QString escape(const QString &s); +}; + +struct RegExp: Expr { + // needs to be compatible with the flags in the lexer, and in RegExpObject + enum Flags { + RegExp_Global = 0x01, + RegExp_IgnoreCase = 0x02, + RegExp_Multiline = 0x04 + }; + + const QString *value; + int flags; + + void init(const QString *value, int flags) + { + this->value = value; + this->flags = flags; + } + + virtual void accept(ExprVisitor *v) { v->visitRegExp(this); } + virtual RegExp *asRegExp() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Name: Expr { + enum Builtin { + builtin_invalid, + builtin_typeof, + builtin_delete, + builtin_postincrement, + builtin_postdecrement, + builtin_throw, + builtin_finish_try, + builtin_foreach_iterator_object, + builtin_foreach_next_property_name, + builtin_push_with_scope, + builtin_pop_scope, + builtin_declare_vars, + builtin_define_property, + builtin_define_array, + builtin_define_getter_setter, + builtin_define_object_literal + }; + + const QString *id; + Builtin builtin; + bool global; + quint32 line; + quint32 column; + + void initGlobal(const QString *id, quint32 line, quint32 column); + void init(const QString *id, quint32 line, quint32 column); + void init(Builtin builtin, quint32 line, quint32 column); + + virtual void accept(ExprVisitor *v) { v->visitName(this); } + virtual bool isLValue() { return true; } + virtual Name *asName() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Temp: Expr { + enum Kind { + Formal = 0, + ScopedFormal, + Local, + ScopedLocal, + VirtualRegister, + PhysicalRegister, + StackSlot + }; + + unsigned index; + unsigned scope : 29; // how many scopes outside the current one? + unsigned kind : 3; + + void init(unsigned kind, unsigned index, unsigned scope) + { + Q_ASSERT((kind == ScopedLocal && scope != 0) || + (kind == ScopedFormal && scope != 0) || + (scope == 0)); + + this->kind = kind; + this->index = index; + this->scope = scope; + } + + virtual void accept(ExprVisitor *v) { v->visitTemp(this); } + virtual bool isLValue() { return true; } + virtual Temp *asTemp() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +inline bool operator==(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW +{ return t1.index == t2.index && t1.scope == t2.scope && t1.kind == t2.kind; } + +inline uint qHash(const Temp &t, uint seed = 0) Q_DECL_NOTHROW +{ return t.index ^ (t.kind | (t.scope << 3)) ^ seed; } + +bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW; + +struct Closure: Expr { + Function *value; + + void init(Function *value) + { + this->value = value; + } + + virtual void accept(ExprVisitor *v) { v->visitClosure(this); } + virtual Closure *asClosure() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Convert: Expr { + Expr *expr; + + void init(Expr *expr, Type type) + { + this->expr = expr; + this->type = type; + } + + virtual void accept(ExprVisitor *v) { v->visitConvert(this); } + virtual Convert *asConvert() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Unop: Expr { + AluOp op; + Expr *expr; + + void init(AluOp op, Expr *expr) + { + this->op = op; + this->expr = expr; + } + + virtual void accept(ExprVisitor *v) { v->visitUnop(this); } + virtual Unop *asUnop() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Binop: Expr { + AluOp op; + Expr *left; // Temp or Const + Expr *right; // Temp or Const + + void init(AluOp op, Expr *left, Expr *right) + { + this->op = op; + this->left = left; + this->right = right; + } + + virtual void accept(ExprVisitor *v) { v->visitBinop(this); } + virtual Binop *asBinop() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Call: Expr { + Expr *base; // Name, Member, Temp + ExprList *args; // List of Temps + + void init(Expr *base, ExprList *args) + { + this->base = base; + this->args = args; + } + + Expr *onlyArgument() const { + if (args && ! args->next) + return args->expr; + return 0; + } + + virtual void accept(ExprVisitor *v) { v->visitCall(this); } + virtual Call *asCall() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct New: Expr { + Expr *base; // Name, Member, Temp + ExprList *args; // List of Temps + + void init(Expr *base, ExprList *args) + { + this->base = base; + this->args = args; + } + + Expr *onlyArgument() const { + if (args && ! args->next) + return args->expr; + return 0; + } + + virtual void accept(ExprVisitor *v) { v->visitNew(this); } + virtual New *asNew() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Subscript: Expr { + Expr *base; + Expr *index; + + void init(Expr *base, Expr *index) + { + this->base = base; + this->index = index; + } + + virtual void accept(ExprVisitor *v) { v->visitSubscript(this); } + virtual bool isLValue() { return true; } + virtual Subscript *asSubscript() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Member: Expr { + Expr *base; + const QString *name; + + void init(Expr *base, const QString *name) + { + this->base = base; + this->name = name; + } + + virtual void accept(ExprVisitor *v) { v->visitMember(this); } + virtual bool isLValue() { return true; } + virtual Member *asMember() { return this; } + + virtual void dump(QTextStream &out) const; +}; + +struct Stmt { + enum Mode { + HIR, + MIR + }; + + struct Data { + QVector<unsigned> uses; + QVector<unsigned> defs; + QBitArray liveIn; + QBitArray liveOut; + }; + + Data *d; + int id; + AST::SourceLocation location; + + Stmt(): d(0), id(-1) {} + virtual ~Stmt() { Q_UNREACHABLE(); } + virtual Stmt *asTerminator() { return 0; } + + virtual void accept(StmtVisitor *) = 0; + virtual Exp *asExp() { return 0; } + virtual Move *asMove() { return 0; } + virtual Jump *asJump() { return 0; } + virtual CJump *asCJump() { return 0; } + virtual Ret *asRet() { return 0; } + virtual Try *asTry() { return 0; } + virtual Phi *asPhi() { return 0; } + virtual void dump(QTextStream &out, Mode mode = HIR) = 0; + + void destroyData() { + delete d; + d = 0; + } +}; + +struct Exp: Stmt { + Expr *expr; + + void init(Expr *expr) + { + this->expr = expr; + } + + virtual void accept(StmtVisitor *v) { v->visitExp(this); } + virtual Exp *asExp() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Move: Stmt { + Expr *target; // LHS - Temp, Name, Member or Subscript + Expr *source; + AluOp op; + + void init(Expr *target, Expr *source, AluOp op) + { + this->target = target; + this->source = source; + this->op = op; + } + + virtual void accept(StmtVisitor *v) { v->visitMove(this); } + virtual Move *asMove() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Jump: Stmt { + BasicBlock *target; + + void init(BasicBlock *target) + { + this->target = target; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitJump(this); } + virtual Jump *asJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct CJump: Stmt { + Expr *cond; // Temp, Binop + BasicBlock *iftrue; + BasicBlock *iffalse; + + void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse) + { + this->cond = cond; + this->iftrue = iftrue; + this->iffalse = iffalse; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitCJump(this); } + virtual CJump *asCJump() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct Ret: Stmt { + Expr *expr; + + void init(Expr *expr) + { + this->expr = expr; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitRet(this); } + virtual Ret *asRet() { return this; } + + virtual void dump(QTextStream &out, Mode); +}; + +struct Try: Stmt { + BasicBlock *tryBlock; + BasicBlock *catchBlock; + QString exceptionVarName; + Temp *exceptionVar; // place to store the caught exception, for use when re-throwing + + void init(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar) + { + this->tryBlock = tryBlock; + this->catchBlock = catchBlock; + this->exceptionVarName = exceptionVarName; + this->exceptionVar = exceptionVar; + } + + virtual Stmt *asTerminator() { return this; } + + virtual void accept(StmtVisitor *v) { v->visitTry(this); } + virtual Try *asTry() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct Phi: Stmt { + Temp *targetTemp; + QVector<Expr *> incoming; + + virtual void accept(StmtVisitor *v) { v->visitPhi(this); } + virtual Phi *asPhi() { return this; } + + virtual void dump(QTextStream &out, Mode mode); +}; + +struct Q_QML_EXPORT Module { + MemoryPool pool; + QVector<Function *> functions; + Function *rootFunction; + + Function *newFunction(const QString &name, Function *outer); + + Module() : rootFunction(0) {} + ~Module(); +}; + +struct Function { + Module *module; + MemoryPool *pool; + const QString *name; + QVector<BasicBlock *> basicBlocks; + int tempCount; + int maxNumberOfArguments; + QSet<QString> strings; + QList<const QString *> formals; + QList<const QString *> locals; + QVector<Function *> nestedFunctions; + Function *outer; + + QString sourceFile; + + int insideWithOrCatch; + + uint hasDirectEval: 1; + uint usesArgumentsObject : 1; + uint isStrict: 1; + uint isNamedExpression : 1; + uint hasTry: 1; + uint hasWith: 1; + uint unused : 26; + + template <typename _Tp> _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); } + + Function(Module *module, Function *outer, const QString &name) + : module(module) + , pool(&module->pool) + , tempCount(0) + , maxNumberOfArguments(0) + , outer(outer) + , insideWithOrCatch(0) + , hasDirectEval(false) + , usesArgumentsObject(false) + , isStrict(false) + , isNamedExpression(false) + , hasTry(false) + , hasWith(false) + , unused(0) + { this->name = newString(name); } + + ~Function(); + + enum BasicBlockInsertMode { + InsertBlock, + DontInsertBlock + }; + + BasicBlock *newBasicBlock(BasicBlock *containingLoop, BasicBlockInsertMode mode = InsertBlock); + const QString *newString(const QString &text); + + void RECEIVE(const QString &name) { formals.append(newString(name)); } + void LOCAL(const QString &name) { locals.append(newString(name)); } + + inline BasicBlock *insertBasicBlock(BasicBlock *block) { basicBlocks.append(block); return block; } + + void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); + + void removeSharedExpressions(); + + int indexOfArgument(const QStringRef &string) const; + + bool variablesCanEscape() const + { return hasDirectEval || !nestedFunctions.isEmpty(); } +}; + +struct BasicBlock { + Function *function; + QVector<Stmt *> statements; + QVector<BasicBlock *> in; + QVector<BasicBlock *> out; + QBitArray liveIn; + QBitArray liveOut; + int index; + AST::SourceLocation nextLocation; + + BasicBlock(Function *function, BasicBlock *containingLoop) + : function(function) + , index(-1) + , _containingGroup(containingLoop) + , _groupStart(false) + {} + ~BasicBlock() {} + + template <typename Instr> inline Instr i(Instr i) { statements.append(i); return i; } + + inline bool isEmpty() const { + return statements.isEmpty(); + } + + inline Stmt *terminator() const { + if (! statements.isEmpty() && statements.at(statements.size() - 1)->asTerminator() != 0) + return statements.at(statements.size() - 1); + return 0; + } + + inline bool isTerminated() const { + if (terminator() != 0) + return true; + return false; + } + + unsigned newTemp(); + + Temp *TEMP(unsigned kind); + Temp *ARG(unsigned index, unsigned scope); + Temp *LOCAL(unsigned index, unsigned scope); + + Expr *CONST(Type type, double value); + Expr *STRING(const QString *value); + Expr *REGEXP(const QString *value, int flags); + + Name *NAME(const QString &id, quint32 line, quint32 column); + Name *NAME(Name::Builtin builtin, quint32 line, quint32 column); + + Name *GLOBALNAME(const QString &id, quint32 line, quint32 column); + + Closure *CLOSURE(Function *function); + + Expr *CONVERT(Expr *expr, Type type); + Expr *UNOP(AluOp op, Expr *expr); + Expr *BINOP(AluOp op, Expr *left, Expr *right); + Expr *CALL(Expr *base, ExprList *args = 0); + Expr *NEW(Expr *base, ExprList *args = 0); + Expr *SUBSCRIPT(Expr *base, Expr *index); + Expr *MEMBER(Expr *base, const QString *name); + + Stmt *EXP(Expr *expr); + + Stmt *MOVE(Expr *target, Expr *source, AluOp op = V4IR::OpInvalid); + + Stmt *JUMP(BasicBlock *target); + Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); + Stmt *RET(Temp *expr); + Stmt *TRY(BasicBlock *tryBlock, BasicBlock *catchBlock, const QString &exceptionVarName, Temp *exceptionVar); + + void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); + + void appendStatement(Stmt *statement); + + BasicBlock *containingGroup() const + { return _containingGroup; } + + bool isGroupStart() const + { return _groupStart; } + + void markAsGroupStart() + { _groupStart = true; } + +private: + BasicBlock *_containingGroup; + bool _groupStart; +}; + +class CloneExpr: protected V4IR::ExprVisitor +{ +public: + explicit CloneExpr(V4IR::BasicBlock *block = 0); + + void setBasicBlock(V4IR::BasicBlock *block); + + template <typename _Expr> + _Expr *operator()(_Expr *expr) + { + return clone(expr); + } + + template <typename _Expr> + _Expr *clone(_Expr *expr) + { + Expr *c = expr; + qSwap(cloned, c); + expr->accept(this); + qSwap(cloned, c); + return static_cast<_Expr *>(c); + } + +protected: + V4IR::ExprList *clone(V4IR::ExprList *list); + + virtual void visitConst(Const *); + virtual void visitString(String *); + virtual void visitRegExp(RegExp *); + virtual void visitName(Name *); + virtual void visitTemp(Temp *); + virtual void visitClosure(Closure *); + virtual void visitConvert(Convert *); + virtual void visitUnop(Unop *); + virtual void visitBinop(Binop *); + virtual void visitCall(Call *); + virtual void visitNew(New *); + virtual void visitSubscript(Subscript *); + virtual void visitMember(Member *); + +private: + V4IR::BasicBlock *block; + V4IR::Expr *cloned; +}; + +} // end of namespace IR + +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +#endif // QV4IR_P_H diff --git a/src/qml/qml/v4/qv4jsonobject.cpp b/src/qml/qml/v4/qv4jsonobject.cpp new file mode 100644 index 0000000000..782c388e5a --- /dev/null +++ b/src/qml/qml/v4/qv4jsonobject.cpp @@ -0,0 +1,1040 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qv4jsonobject_p.h> +#include <qv4objectproto_p.h> +#include <qv4numberobject_p.h> +#include <qv4stringobject_p.h> +#include <qv4booleanobject_p.h> +#include <qv4objectiterator_p.h> +#include <qjsondocument.h> +#include <qstack.h> +#include <qstringlist.h> + +#include <wtf/MathExtras.h> + +using namespace QV4; + +//#define PARSER_DEBUG +#ifdef PARSER_DEBUG +static int indent = 0; +#define BEGIN qDebug() << QByteArray(4*indent++, ' ').constData() +#define END --indent +#define DEBUG qDebug() << QByteArray(4*indent, ' ').constData() +#else +#define BEGIN if (1) ; else qDebug() +#define END do {} while (0) +#define DEBUG if (1) ; else qDebug() +#endif + + +class JsonParser +{ +public: + JsonParser(ExecutionContext *context, const QChar *json, int length); + + Value parse(QJsonParseError *error); + +private: + inline bool eatSpace(); + inline QChar nextToken(); + + Value parseObject(); + Value parseArray(); + bool parseMember(Object *o); + bool parseString(QString *string); + bool parseValue(Value *val); + bool parseNumber(Value *val); + + ExecutionContext *context; + const QChar *head; + const QChar *json; + const QChar *end; + + int nestingLevel; + QJsonParseError::ParseError lastError; +}; + +static const int nestingLimit = 1024; + + +JsonParser::JsonParser(ExecutionContext *context, const QChar *json, int length) + : context(context), head(json), json(json), nestingLevel(0), lastError(QJsonParseError::NoError) +{ + end = json + length; +} + + + +/* + +begin-array = ws %x5B ws ; [ left square bracket + +begin-object = ws %x7B ws ; { left curly bracket + +end-array = ws %x5D ws ; ] right square bracket + +end-object = ws %x7D ws ; } right curly bracket + +name-separator = ws %x3A ws ; : colon + +value-separator = ws %x2C ws ; , comma + +Insignificant whitespace is allowed before or after any of the six +structural characters. + +ws = *( + %x20 / ; Space + %x09 / ; Horizontal tab + %x0A / ; Line feed or New line + %x0D ; Carriage return + ) + +*/ + +enum { + Space = 0x20, + Tab = 0x09, + LineFeed = 0x0a, + Return = 0x0d, + BeginArray = 0x5b, + BeginObject = 0x7b, + EndArray = 0x5d, + EndObject = 0x7d, + NameSeparator = 0x3a, + ValueSeparator = 0x2c, + Quote = 0x22 +}; + +bool JsonParser::eatSpace() +{ + while (json < end) { + if (*json > Space) + break; + if (*json != Space && + *json != Tab && + *json != LineFeed && + *json != Return) + break; + ++json; + } + return (json < end); +} + +QChar JsonParser::nextToken() +{ + if (!eatSpace()) + return 0; + QChar token = *json++; + switch (token.unicode()) { + case BeginArray: + case BeginObject: + case NameSeparator: + case ValueSeparator: + case EndArray: + case EndObject: + eatSpace(); + case Quote: + break; + default: + token = 0; + break; + } + return token; +} + +/* + JSON-text = object / array +*/ +Value JsonParser::parse(QJsonParseError *error) +{ +#ifdef PARSER_DEBUG + indent = 0; + qDebug() << ">>>>> parser begin"; +#endif + + eatSpace(); + + Value v; + if (!parseValue(&v)) { +#ifdef PARSER_DEBUG + qDebug() << ">>>>> parser error"; +#endif + if (lastError == QJsonParseError::NoError) + lastError = QJsonParseError::IllegalValue; + error->offset = json - head; + error->error = lastError; + return Value::undefinedValue(); + } + + // some input left... + if (eatSpace()) { + lastError = QJsonParseError::IllegalValue; + error->offset = json - head; + error->error = lastError; + return Value::undefinedValue(); + } + + END; + error->offset = 0; + error->error = QJsonParseError::NoError; + return v; +} + +/* + object = begin-object [ member *( value-separator member ) ] + end-object +*/ + +Value JsonParser::parseObject() +{ + if (++nestingLevel > nestingLimit) { + lastError = QJsonParseError::DeepNesting; + return Value::undefinedValue(); + } + + BEGIN << "parseObject pos=" << json; + + Object *o = context->engine->newObject(); + Value objectVal = Value::fromObject(o); + + QChar token = nextToken(); + while (token == Quote) { + if (!parseMember(o)) + return Value::undefinedValue(); + token = nextToken(); + if (token != ValueSeparator) + break; + token = nextToken(); + if (token == EndObject) { + lastError = QJsonParseError::MissingObject; + return Value::undefinedValue(); + } + } + + DEBUG << "end token=" << token; + if (token != EndObject) { + lastError = QJsonParseError::UnterminatedObject; + return Value::undefinedValue(); + } + + END; + + --nestingLevel; + return objectVal; +} + +/* + member = string name-separator value +*/ +bool JsonParser::parseMember(Object *o) +{ + BEGIN << "parseMember"; + + QString key; + if (!parseString(&key)) + return false; + QChar token = nextToken(); + if (token != NameSeparator) { + lastError = QJsonParseError::MissingNameSeparator; + return false; + } + Value val; + if (!parseValue(&val)) + return false; + + Property *p = o->insertMember(context->engine->newIdentifier(key), Attr_Data); + p->value = val; + + END; + return true; +} + +/* + array = begin-array [ value *( value-separator value ) ] end-array +*/ +Value JsonParser::parseArray() +{ + BEGIN << "parseArray"; + ArrayObject *array = context->engine->newArrayObject(); + + if (++nestingLevel > nestingLimit) { + lastError = QJsonParseError::DeepNesting; + return Value::undefinedValue(); + } + + if (!eatSpace()) { + lastError = QJsonParseError::UnterminatedArray; + return Value::undefinedValue(); + } + if (*json == EndArray) { + nextToken(); + } else { + uint index = 0; + while (1) { + Value val; + if (!parseValue(&val)) + return Value::undefinedValue(); + array->arraySet(index, val); + QChar token = nextToken(); + if (token == EndArray) + break; + else if (token != ValueSeparator) { + if (!eatSpace()) + lastError = QJsonParseError::UnterminatedArray; + else + lastError = QJsonParseError::MissingValueSeparator; + return Value::undefinedValue(); + } + ++index; + } + } + + DEBUG << "size =" << array->arrayLength(); + END; + + --nestingLevel; + return Value::fromObject(array); +} + +/* +value = false / null / true / object / array / number / string + +*/ + +bool JsonParser::parseValue(Value *val) +{ + BEGIN << "parse Value" << *json; + + switch ((json++)->unicode()) { + case 'n': + if (end - json < 3) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'u' && + *json++ == 'l' && + *json++ == 'l') { + *val = Value::nullValue(); + DEBUG << "value: null"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case 't': + if (end - json < 3) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'r' && + *json++ == 'u' && + *json++ == 'e') { + *val = Value::fromBoolean(true); + DEBUG << "value: true"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case 'f': + if (end - json < 4) { + lastError = QJsonParseError::IllegalValue; + return false; + } + if (*json++ == 'a' && + *json++ == 'l' && + *json++ == 's' && + *json++ == 'e') { + *val = Value::fromBoolean(false); + DEBUG << "value: false"; + END; + return true; + } + lastError = QJsonParseError::IllegalValue; + return false; + case Quote: { + QString value; + if (!parseString(&value)) + return false; + DEBUG << "value: string"; + END; + *val = Value::fromString(context, value); + return true; + } + case BeginArray: { + *val = parseArray(); + if (val->isUndefined()) + return false; + DEBUG << "value: array"; + END; + return true; + } + case BeginObject: { + *val = parseObject(); + if (val->isUndefined()) + return false; + DEBUG << "value: object"; + END; + return true; + } + case EndArray: + lastError = QJsonParseError::MissingObject; + return false; + default: + --json; + if (!parseNumber(val)) + return false; + DEBUG << "value: number"; + END; + } + + return true; +} + + + + + +/* + number = [ minus ] int [ frac ] [ exp ] + decimal-point = %x2E ; . + digit1-9 = %x31-39 ; 1-9 + e = %x65 / %x45 ; e E + exp = e [ minus / plus ] 1*DIGIT + frac = decimal-point 1*DIGIT + int = zero / ( digit1-9 *DIGIT ) + minus = %x2D ; - + plus = %x2B ; + + zero = %x30 ; 0 + +*/ + +bool JsonParser::parseNumber(Value *val) +{ + BEGIN << "parseNumber" << *json; + + const QChar *start = json; + bool isInt = true; + + // minus + if (json < end && *json == '-') + ++json; + + // int = zero / ( digit1-9 *DIGIT ) + if (json < end && *json == '0') { + ++json; + } else { + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + // frac = decimal-point 1*DIGIT + if (json < end && *json == '.') { + isInt = false; + ++json; + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + // exp = e [ minus / plus ] 1*DIGIT + if (json < end && (*json == 'e' || *json == 'E')) { + isInt = false; + ++json; + if (json < end && (*json == '-' || *json == '+')) + ++json; + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + QString number(start, json - start); + DEBUG << "numberstring" << number; + + if (isInt) { + bool ok; + int n = number.toInt(&ok); + if (ok && n < (1<<25) && n > -(1<<25)) { + *val = Value::fromInt32(n); + END; + return true; + } + } + + bool ok; + double d; + d = number.toDouble(&ok); + + if (!ok) { + lastError = QJsonParseError::IllegalNumber; + return false; + } + + * val = Value::fromDouble(d); + + END; + return true; +} + +/* + + string = quotation-mark *char quotation-mark + + char = unescaped / + escape ( + %x22 / ; " quotation mark U+0022 + %x5C / ; \ reverse solidus U+005C + %x2F / ; / solidus U+002F + %x62 / ; b backspace U+0008 + %x66 / ; f form feed U+000C + %x6E / ; n line feed U+000A + %x72 / ; r carriage return U+000D + %x74 / ; t tab U+0009 + %x75 4HEXDIG ) ; uXXXX U+XXXX + + escape = %x5C ; \ + + quotation-mark = %x22 ; " + + unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + */ +static inline bool addHexDigit(QChar digit, uint *result) +{ + ushort d = digit.unicode(); + *result <<= 4; + if (d >= '0' && d <= '9') + *result |= (d - '0'); + else if (d >= 'a' && d <= 'f') + *result |= (d - 'a') + 10; + else if (d >= 'A' && d <= 'F') + *result |= (d - 'A') + 10; + else + return false; + return true; +} + +static inline bool scanEscapeSequence(const QChar *&json, const QChar *end, uint *ch) +{ + ++json; + if (json >= end) + return false; + + DEBUG << "scan escape"; + uint escaped = (json++)->unicode(); + switch (escaped) { + case '"': + *ch = '"'; break; + case '\\': + *ch = '\\'; break; + case '/': + *ch = '/'; break; + case 'b': + *ch = 0x8; break; + case 'f': + *ch = 0xc; break; + case 'n': + *ch = 0xa; break; + case 'r': + *ch = 0xd; break; + case 't': + *ch = 0x9; break; + case 'u': { + *ch = 0; + if (json > end - 4) + return false; + for (int i = 0; i < 4; ++i) { + if (!addHexDigit(*json, ch)) + return false; + ++json; + } + if (*ch <= 0x1f) + return false; + return true; + } + default: + return false; + } + return true; +} + + +bool JsonParser::parseString(QString *string) +{ + BEGIN << "parse string stringPos=" << json; + + while (json < end) { + if (*json == '"') + break; + else if (*json == '\\') { + uint ch = 0; + if (!scanEscapeSequence(json, end, &ch)) { + lastError = QJsonParseError::IllegalEscapeSequence; + return false; + } + qDebug() << "scanEscape" << hex << ch; + if (QChar::requiresSurrogates(ch)) { + *string += QChar::highSurrogate(ch); + *string += QChar::lowSurrogate(ch); + } else { + *string += QChar(ch); + } + } else { + if (json->unicode() <= 0x1f) { + lastError = QJsonParseError::IllegalEscapeSequence; + return false; + } + *string += *json; + ++json; + } + } + ++json; + + if (json > end) { + lastError = QJsonParseError::UnterminatedString; + return false; + } + + END; + return true; +} + + +struct Stringify +{ + ExecutionContext *ctx; + FunctionObject *replacerFunction; + QVector<String *> propertyList; + QString gap; + QString indent; + + QStack<Object *> stack; + + Stringify(ExecutionContext *ctx) : ctx(ctx), replacerFunction(0) {} + + QString Str(const QString &key, Value value); + QString JA(ArrayObject *a); + QString JO(Object *o); + + QString makeMember(const QString &key, Value v); +}; + +static QString quote(const QString &str) +{ + QString product = "\""; + for (int i = 0; i < str.length(); ++i) { + QChar c = str.at(i); + switch (c.unicode()) { + case '"': + product += "\\\""; + break; + case '\\': + product += "\\\\"; + break; + case '\b': + product += "\\b"; + break; + case '\f': + product += "\\f"; + break; + case '\n': + product += "\\n"; + break; + case '\r': + product += "\\r"; + break; + case '\t': + product += "\\t"; + break; + default: + if (c.unicode() <= 0x1f) { + product += "\\u00"; + product += c.unicode() > 0xf ? '1' : '0'; + product += "0123456789abcdef"[c.unicode() & 0xf]; + } else { + product += c; + } + } + } + product += '"'; + return product; +} + +QString Stringify::Str(const QString &key, Value value) +{ + QString result; + + if (Object *o = value.asObject()) { + FunctionObject *toJSON = o->get(ctx->engine->newString(QStringLiteral("toJSON"))).asFunctionObject(); + if (toJSON) { + Value arg = Value::fromString(ctx, key); + value = toJSON->call(value, &arg, 1); + } + } + + if (replacerFunction) { + Object *holder = ctx->engine->newObject(); + Value holderValue = Value::fromObject(holder); + holder->put(ctx, QString(), value); + Value args[2]; + args[0] = Value::fromString(ctx, key); + args[1] = value; + value = replacerFunction->call(holderValue, args, 2); + } + + if (Object *o = value.asObject()) { + if (NumberObject *n = o->asNumberObject()) + value = n->value; + else if (StringObject *so = o->asStringObject()) + value = so->value; + else if (BooleanObject *b =o->asBooleanObject()) + value = b->value; + } + + if (value.isNull()) + return QStringLiteral("null"); + if (value.isBoolean()) + return value.booleanValue() ? QStringLiteral("true") : QStringLiteral("false"); + if (value.isString()) + return quote(value.stringValue()->toQString()); + + if (value.isNumber()) { + double d = value.toNumber(); + return std::isfinite(d) ? value.toString(ctx)->toQString() : QStringLiteral("null"); + } + + if (Object *o = value.asObject()) { + if (!o->asFunctionObject()) { + if (o->asArrayObject()) + return JA(static_cast<ArrayObject *>(o)); + else + return JO(o); + } + } + + return QString(); +} + +QString Stringify::makeMember(const QString &key, Value v) +{ + QString strP = Str(key, v); + if (!strP.isEmpty()) { + QString member = quote(key) + ':'; + if (!gap.isEmpty()) + member += ' '; + member += strP; + return member; + } + return QString(); +} + +QString Stringify::JO(Object *o) +{ + if (stack.contains(o)) + ctx->throwTypeError(); + + QString result; + stack.push(o); + QString stepback = indent; + indent += gap; + + QStringList partial; + if (propertyList.isEmpty()) { + ObjectIterator it(o, ObjectIterator::EnumerableOnly); + + while (1) { + Value v; + Value name = it.nextPropertyNameAsString(&v); + if (name.isNull()) + break; + QString key = name.toQString(); + QString member = makeMember(key, v); + if (!member.isEmpty()) + partial += member; + } + } else { + for (int i = 0; i < propertyList.size(); ++i) { + bool exists; + Value v = o->get(propertyList.at(i), &exists); + if (!exists) + continue; + QString member = makeMember(propertyList.at(i)->toQString(), v); + if (!member.isEmpty()) + partial += member; + } + } + + if (partial.isEmpty()) { + result = QStringLiteral("{}"); + } else if (gap.isEmpty()) { + result = "{" + partial.join(",") + "}"; + } else { + QString separator = ",\n" + indent; + result = "{\n" + indent + partial.join(separator) + "\n" + stepback + "}"; + } + + indent = stepback; + stack.pop(); + return result; +} + +QString Stringify::JA(ArrayObject *a) +{ + if (stack.contains(a)) + ctx->throwTypeError(); + + QString result; + stack.push(a); + QString stepback = indent; + indent += gap; + + QStringList partial; + uint len = a->arrayLength(); + for (uint i = 0; i < len; ++i) { + bool exists; + Value v = a->getIndexed(i, &exists); + if (!exists) { + partial += QStringLiteral("null"); + continue; + } + QString strP = Str(QString::number(i), v); + if (!strP.isEmpty()) + partial += strP; + else + partial += QStringLiteral("null"); + } + + if (partial.isEmpty()) { + result = QStringLiteral("[]"); + } else if (gap.isEmpty()) { + result = "[" + partial.join(",") + "]"; + } else { + QString separator = ",\n" + indent; + result = "[\n" + indent + partial.join(separator) + "\n" + stepback + "]"; + } + + indent = stepback; + stack.pop(); + return result; +} + + +JsonObject::JsonObject(ExecutionContext *context) + : Object(context->engine) +{ + type = Type_JSONObject; + prototype = context->engine->objectPrototype; + + defineDefaultProperty(context, QStringLiteral("parse"), method_parse, 2); + defineDefaultProperty(context, QStringLiteral("stringify"), method_stringify, 3); +} + + +Value JsonObject::method_parse(SimpleCallContext *ctx) +{ + QString jtext = ctx->argument(0).toString(ctx)->toQString(); + + DEBUG << "parsing source = " << jtext; + JsonParser parser(ctx, jtext.constData(), jtext.length()); + QJsonParseError error; + Value result = parser.parse(&error); + if (error.error != QJsonParseError::NoError) { + DEBUG << "parse error" << error.errorString(); + ctx->throwSyntaxError(0); + } + + return result; +} + +Value JsonObject::method_stringify(SimpleCallContext *ctx) +{ + Stringify stringify(ctx); + + Object *o = ctx->argument(1).asObject(); + if (o) { + stringify.replacerFunction = o->asFunctionObject(); + if (o->isArrayObject()) { + uint arrayLen = o->arrayLength(); + for (uint i = 0; i < arrayLen; ++i) { + Value v = o->getIndexed(i); + if (v.asNumberObject() || v.asStringObject() || v.isNumber()) + v = __qmljs_to_string(v, ctx); + if (v.isString()) { + String *s = v.stringValue(); + if (!stringify.propertyList.contains(s)) + stringify.propertyList.append(s); + } + } + } + } + + Value s = ctx->argument(2); + if (NumberObject *n = s.asNumberObject()) + s = n->value; + else if (StringObject *so = s.asStringObject()) + s = so->value; + + if (s.isNumber()) { + stringify.gap = QString(qMin(10, (int)s.toInteger()), ' '); + } else if (s.isString()) { + stringify.gap = s.stringValue()->toQString().left(10); + } + + + QString result = stringify.Str(QString(), ctx->argument(0)); + if (result.isEmpty()) + return Value::undefinedValue(); + return Value::fromString(ctx, result); +} + + + +QV4::Value JsonObject::fromJsonValue(ExecutionEngine *engine, const QJsonValue &value) +{ + if (value.isString()) + return Value::fromString(engine->current, value.toString()); + else if (value.isDouble()) + return Value::fromDouble(value.toDouble()); + else if (value.isBool()) + return Value::fromBoolean(value.toBool()); + else if (value.isArray()) + return fromJsonArray(engine, value.toArray()); + else if (value.isObject()) + return fromJsonObject(engine, value.toObject()); + else if (value.isNull()) + return Value::nullValue(); + else + return Value::undefinedValue(); +} + +QJsonValue JsonObject::toJsonValue(const QV4::Value &value, + V4ObjectSet &visitedObjects) +{ + if (String *s = value.asString()) + return QJsonValue(s->toQString()); + else if (value.isNumber()) + return QJsonValue(value.toNumber()); + else if (value.isBoolean()) + return QJsonValue((bool)value.booleanValue()); + else if (ArrayObject *a = value.asArrayObject()) + return toJsonArray(a, visitedObjects); + else if (Object *o = value.asObject()) + return toJsonObject(o, visitedObjects); + else if (value.isNull()) + return QJsonValue(QJsonValue::Null); + else + return QJsonValue(QJsonValue::Undefined); +} + +QV4::Value JsonObject::fromJsonObject(ExecutionEngine *engine, const QJsonObject &object) +{ + Object *o = engine->newObject(); + for (QJsonObject::const_iterator it = object.begin(); it != object.end(); ++it) + o->put(engine->newString(it.key()), fromJsonValue(engine, it.value())); + return Value::fromObject(o); +} + +QJsonObject JsonObject::toJsonObject(QV4::Object *o, V4ObjectSet &visitedObjects) +{ + QJsonObject result; + if (!o || o->asFunctionObject()) + return result; + + if (visitedObjects.contains(o)) { + // Avoid recursion. + // For compatibility with QVariant{List,Map} conversion, we return an + // empty object (and no error is thrown). + return result; + } + + visitedObjects.insert(o); + + ObjectIterator it(o, ObjectIterator::EnumerableOnly); + while (1) { + Value v; + Value name = it.nextPropertyNameAsString(&v); + if (name.isNull()) + break; + + QString key = name.toQString(); + if (!v.asFunctionObject()) + result.insert(key, toJsonValue(v, visitedObjects)); + } + + visitedObjects.remove(o); + + return result; +} + +QV4::Value JsonObject::fromJsonArray(ExecutionEngine *engine, const QJsonArray &array) +{ + int size = array.size(); + ArrayObject *a = engine->newArrayObject(); + a->arrayReserve(size); + a->arrayDataLen = size; + for (int i = 0; i < size; i++) + a->arrayData[i].value = fromJsonValue(engine, array.at(i)); + a->setArrayLengthUnchecked(size); + return Value::fromObject(a); +} + +QJsonArray JsonObject::toJsonArray(ArrayObject *a, V4ObjectSet &visitedObjects) +{ + QJsonArray result; + if (!a) + return result; + + if (visitedObjects.contains(a)) { + // Avoid recursion. + // For compatibility with QVariant{List,Map} conversion, we return an + // empty array (and no error is thrown). + return result; + } + + visitedObjects.insert(a); + + quint32 length = a->arrayLength(); + for (quint32 i = 0; i < length; ++i) { + Value v = a->getIndexed(i); + result.append(toJsonValue(v.asFunctionObject() ? QV4::Value::nullValue() : v, visitedObjects)); + } + + visitedObjects.remove(a); + + return result; +} diff --git a/src/qml/qml/v4/qv4jsonobject_p.h b/src/qml/qml/v4/qv4jsonobject_p.h new file mode 100644 index 0000000000..ccd99d5488 --- /dev/null +++ b/src/qml/qml/v4/qv4jsonobject_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4JSONOBJECTS_H +#define QV4SJONOBJECTS_H + +#include "qv4object_p.h" +#include <qjsonarray.h> +#include <qjsonobject.h> +#include <qjsonvalue.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct JsonObject : Object { +private: + typedef QSet<QV4::Object *> V4ObjectSet; +public: + JsonObject(ExecutionContext *context); + + static Value method_parse(SimpleCallContext *ctx); + static Value method_stringify(SimpleCallContext *ctx); + + static QV4::Value fromJsonValue(ExecutionEngine *engine, const QJsonValue &value); + static QV4::Value fromJsonObject(ExecutionEngine *engine, const QJsonObject &object); + static QV4::Value fromJsonArray(ExecutionEngine *engine, const QJsonArray &array); + + static inline QJsonValue toJsonValue(const QV4::Value &value) + { V4ObjectSet visitedObjects; return toJsonValue(value, visitedObjects); } + static inline QJsonObject toJsonObject(QV4::Object *o) + { V4ObjectSet visitedObjects; return toJsonObject(o, visitedObjects); } + static inline QJsonArray toJsonArray(QV4::ArrayObject *a) + { V4ObjectSet visitedObjects; return toJsonArray(a, visitedObjects); } + +private: + static QJsonValue toJsonValue(const QV4::Value &value, V4ObjectSet &visitedObjects); + static QJsonObject toJsonObject(QV4::Object *o, V4ObjectSet &visitedObjects); + static QJsonArray toJsonArray(QV4::ArrayObject *a, V4ObjectSet &visitedObjects); + +}; + +} + +QT_END_NAMESPACE + +#endif + diff --git a/src/qml/qml/v4/qv4lookup.cpp b/src/qml/qml/v4/qv4lookup.cpp new file mode 100644 index 0000000000..b5ea877bd4 --- /dev/null +++ b/src/qml/qml/v4/qv4lookup.cpp @@ -0,0 +1,365 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qv4lookup_p.h" +#include "qv4functionobject_p.h" + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +Property *Lookup::lookup(Object *obj, PropertyAttributes *attrs) +{ + int i = 0; + while (i < level && obj && obj->internalClass == classList[i]) { + obj = obj->prototype; + ++i; + } + + if (index != UINT_MAX && obj->internalClass == classList[i]) { + *attrs = obj->internalClass->propertyData.at(index); + return obj->memberData + index; + } + + while (i < Size && obj) { + classList[i] = obj->internalClass; + + index = obj->internalClass->find(name); + if (index != UINT_MAX) { + level = i; + *attrs = obj->internalClass->propertyData.at(index); + return obj->memberData + index; + } + + obj = obj->prototype; + ++i; + } + level = i; + + while (obj) { + index = obj->internalClass->find(name); + if (index != UINT_MAX) { + *attrs = obj->internalClass->propertyData.at(index); + return obj->memberData + index; + } + + obj = obj->prototype; + } + return 0; +} + + +void Lookup::getterGeneric(QV4::Lookup *l, QV4::Value *result, const QV4::Value &object) +{ + if (Object *o = object.asObject()) { + o->getLookup(l, result); + return; + } + + Value res; + if (Managed *m = object.asManaged()) { + res = m->get(l->name); + } else { + ExecutionContext *ctx = l->name->engine()->current; + Object *o = __qmljs_convert_to_object(ctx, object); + res = o->get(l->name); + } + if (result) + *result = res; +} + +void Lookup::getter0(Lookup *l, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass) { + if (result) + *result = o->memberData[l->index].value; + return; + } + } + l->getter = getterGeneric; + getterGeneric(l, result, object); +} + +void Lookup::getter1(Lookup *l, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass && + l->classList[1] == o->prototype->internalClass) { + if (result) + *result = o->prototype->memberData[l->index].value; + return; + } + } + l->getter = getterGeneric; + getterGeneric(l, result, object); +} + +void Lookup::getter2(Lookup *l, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass) { + o = o->prototype; + if (l->classList[1] == o->internalClass) { + o = o->prototype; + if (l->classList[2] == o->internalClass) { + if (result) + *result = o->memberData[l->index].value; + return; + } + } + } + } + l->getter = getterGeneric; + getterGeneric(l, result, object); +} + +void Lookup::getterAccessor0(Lookup *l, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass) { + Value res; + FunctionObject *getter = o->memberData[l->index].getter(); + if (!getter) + res = Value::undefinedValue(); + else + res = getter->call(object, 0, 0); + if (result) + *result = res; + return; + } + } + l->getter = getterGeneric; + getterGeneric(l, result, object); +} + +void Lookup::getterAccessor1(Lookup *l, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass && + l->classList[1] == o->prototype->internalClass) { + Value res; + FunctionObject *getter = o->prototype->memberData[l->index].getter(); + if (!getter) + res = Value::undefinedValue(); + else + res = getter->call(object, 0, 0); + if (result) + *result = res; + return; + } + } + l->getter = getterGeneric; + getterGeneric(l, result, object); +} + +void Lookup::getterAccessor2(Lookup *l, Value *result, const Value &object) +{ + if (Object *o = object.asObject()) { + if (l->classList[0] == o->internalClass) { + o = o->prototype; + if (l->classList[1] == o->internalClass) { + o = o->prototype; + if (l->classList[2] == o->internalClass) { + Value res; + FunctionObject *getter = o->memberData[l->index].getter(); + if (!getter) + res = Value::undefinedValue(); + else + res = getter->call(object, 0, 0); + if (result) + *result = res; + return; + } + } + } + } + l->getter = getterGeneric; + getterGeneric(l, result, object); +} + + +void Lookup::globalGetterGeneric(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + PropertyAttributes attrs; + Property *p = l->lookup(o, &attrs); + if (p) { + if (attrs.isData()) { + if (l->level == 0) + l->globalGetter = globalGetter0; + else if (l->level == 1) + l->globalGetter = globalGetter1; + else if (l->level == 2) + l->globalGetter = globalGetter2; + *result = p->value; + return; + } else { + if (l->level == 0) + l->globalGetter = globalGetterAccessor0; + else if (l->level == 1) + l->globalGetter = globalGetterAccessor1; + else if (l->level == 2) + l->globalGetter = globalGetterAccessor2; + Value res = o->getValue(p, attrs); + if (result) + *result = res; + return; + } + } + ctx->throwReferenceError(Value::fromString(l->name)); +} + +void Lookup::globalGetter0(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass) { + *result = o->memberData[l->index].value; + return; + } + l->globalGetter = globalGetterGeneric; + globalGetterGeneric(l, ctx, result); +} + +void Lookup::globalGetter1(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass && + l->classList[1] == o->prototype->internalClass) { + *result = o->prototype->memberData[l->index].value; + return; + } + l->globalGetter = globalGetterGeneric; + globalGetterGeneric(l, ctx, result); +} + +void Lookup::globalGetter2(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass) { + o = o->prototype; + if (l->classList[1] == o->internalClass) { + o = o->prototype; + if (l->classList[2] == o->internalClass) { + *result = o->prototype->memberData[l->index].value; + return; + } + } + } + l->globalGetter = globalGetterGeneric; + globalGetterGeneric(l, ctx, result); +} + +void Lookup::globalGetterAccessor0(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass) { + FunctionObject *getter = o->memberData[l->index].getter(); + if (!getter) + *result = Value::undefinedValue(); + else + *result = getter->call(Value::undefinedValue(), 0, 0); + return; + } + l->globalGetter = globalGetterGeneric; + globalGetterGeneric(l, ctx, result); +} + +void Lookup::globalGetterAccessor1(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass && + l->classList[1] == o->prototype->internalClass) { + FunctionObject *getter = o->prototype->memberData[l->index].getter(); + if (!getter) + *result = Value::undefinedValue(); + else + *result = getter->call(Value::undefinedValue(), 0, 0); + return; + } + l->globalGetter = globalGetterGeneric; + globalGetterGeneric(l, ctx, result); +} + +void Lookup::globalGetterAccessor2(Lookup *l, ExecutionContext *ctx, Value *result) +{ + Object *o = ctx->engine->globalObject; + if (l->classList[0] == o->internalClass) { + o = o->prototype; + if (l->classList[1] == o->internalClass) { + o = o->prototype; + if (l->classList[2] == o->internalClass) { + FunctionObject *getter = o->memberData[l->index].getter(); + if (!getter) + *result = Value::undefinedValue(); + else + *result = getter->call(Value::undefinedValue(), 0, 0); + return; + } + } + } + l->globalGetter = globalGetterGeneric; + globalGetterGeneric(l, ctx, result); +} + +void Lookup::setterGeneric(Lookup *l, const Value &object, const Value &value) +{ + Object *o = object.asObject(); + if (!o) { + o = __qmljs_convert_to_object(l->name->engine()->current, object); + o->put(l->name, value); + return; + } + o->setLookup(l, value); +} + +void Lookup::setter0(Lookup *l, const Value &object, const Value &value) +{ + Object *o = object.asObject(); + if (o && o->internalClass == l->classList[0]) { + o->memberData[l->index].value = value; + return; + } + + l->setter = setterGeneric; + setterGeneric(l, object, value); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4lookup_p.h b/src/qml/qml/v4/qv4lookup_p.h new file mode 100644 index 0000000000..e77552826a --- /dev/null +++ b/src/qml/qml/v4/qv4lookup_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4LOOKUP_H +#define QV4LOOKUP_H + +#include "qv4global_p.h" +#include "qv4runtime_p.h" +#include "qv4engine_p.h" +#include "qv4context_p.h" +#include "qv4object_p.h" +#include "qv4internalclass_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Lookup { + enum { Size = 3 }; + union { + void (*getter)(Lookup *l, Value *result, const Value &object); + void (*globalGetter)(Lookup *l, ExecutionContext *ctx, Value *result); + void (*setter)(Lookup *l, const Value &object, const Value &v); + }; + InternalClass *classList[Size]; + int level; + uint index; + String *name; + + static void getterGeneric(Lookup *l, Value *result, const Value &object); + static void getter0(Lookup *l, Value *result, const Value &object); + static void getter1(Lookup *l, Value *result, const Value &object); + static void getter2(Lookup *l, Value *result, const Value &object); + static void getterAccessor0(Lookup *l, Value *result, const Value &object); + static void getterAccessor1(Lookup *l, Value *result, const Value &object); + static void getterAccessor2(Lookup *l, Value *result, const Value &object); + + static void globalGetterGeneric(Lookup *l, ExecutionContext *ctx, Value *result); + static void globalGetter0(Lookup *l, ExecutionContext *ctx, Value *result); + static void globalGetter1(Lookup *l, ExecutionContext *ctx, Value *result); + static void globalGetter2(Lookup *l, ExecutionContext *ctx, Value *result); + static void globalGetterAccessor0(Lookup *l, ExecutionContext *ctx, Value *result); + static void globalGetterAccessor1(Lookup *l, ExecutionContext *ctx, Value *result); + static void globalGetterAccessor2(Lookup *l, ExecutionContext *ctx, Value *result); + + static void setterGeneric(Lookup *l, const Value &object, const Value &value); + static void setter0(Lookup *l, const Value &object, const Value &value); + + Property *lookup(Object *obj, PropertyAttributes *attrs); + +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4/qv4managed.cpp b/src/qml/qml/v4/qv4managed.cpp new file mode 100644 index 0000000000..19adb354e3 --- /dev/null +++ b/src/qml/qml/v4/qv4managed.cpp @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4managed_p.h" +#include "qv4mm_p.h" +#include "qv4errorobject_p.h" + +using namespace QV4; + +const ManagedVTable Managed::static_vtbl = +{ + call, + construct, + 0 /*markObjects*/, + destroy, + 0 /*collectDeletables*/, + hasInstance, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + isEqualTo, + 0, + "Managed", +}; + + +void *Managed::operator new(size_t size, MemoryManager *mm) +{ + assert(mm); + + return mm->allocManaged(size); +} + +void Managed::operator delete(void *ptr) +{ + if (!ptr) + return; + + Managed *m = static_cast<Managed *>(ptr); + m->vtbl = 0; + m->_data = 0; + m->~Managed(); +} + +void Managed::operator delete(void *ptr, MemoryManager *mm) +{ + Q_UNUSED(mm); + + operator delete(ptr); +} + +ExecutionEngine *Managed::engine() const +{ + return internalClass ? internalClass->engine : 0; +} + +QString Managed::className() const +{ + const char *s = 0; + switch (Type(type)) { + case Type_Invalid: + case Type_String: + return QString(); + case Type_Object: + s = "Object"; + break; + case Type_ArrayObject: + s = "Array"; + break; + case Type_FunctionObject: + s = "Function"; + break; + case Type_BooleanObject: + s = "Boolean"; + break; + case Type_NumberObject: + s = "Number"; + break; + case Type_StringObject: + s = "String"; + break; + case Type_DateObject: + s = "Date"; + break; + case Type_RegExpObject: + s = "RegExp"; + break; + case Type_ErrorObject: + switch (ErrorObject::ErrorType(subtype)) { + case ErrorObject::Error: + s = "Error"; + break; + case ErrorObject::EvalError: + s = "EvalError"; + break; + case ErrorObject::RangeError: + s = "RangeError"; + break; + case ErrorObject::ReferenceError: + s = "ReferenceError"; + break; + case ErrorObject::SyntaxError: + s = "SyntaxError"; + break; + case ErrorObject::TypeError: + s = "TypeError"; + break; + case ErrorObject::URIError: + s = "URIError"; + break; + } + break; + case Type_ArgumentsObject: + s = "Arguments"; + break; + case Type_JSONObject: + s = "JSON"; + break; + case Type_MathObject: + s = "Math"; + break; + case Type_ForeachIteratorObject: + s = "__ForeachIterator"; + break; + } + return QString::fromLatin1(s); +} + +bool Managed::hasInstance(Managed *m, const Value &) +{ + m->engine()->current->throwTypeError(); +} + +Value Managed::construct(Managed *m, Value *, int) +{ + m->engine()->current->throwTypeError(); +} + +Value Managed::call(Managed *m, const Value &, Value *, int) +{ + m->engine()->current->throwTypeError(); +} + +void Managed::getLookup(Managed *m, Lookup *, Value *) +{ + m->engine()->current->throwTypeError(); +} + +void Managed::setLookup(Managed *m, Lookup *, const Value &) +{ + m->engine()->current->throwTypeError(); +} + +bool Managed::isEqualTo(Managed *, Managed *) +{ + return false; +} + +Value Managed::get(String *name, bool *hasProperty) +{ + return vtbl->get(this, name, hasProperty); +} + +Value Managed::getIndexed(uint index, bool *hasProperty) +{ + return vtbl->getIndexed(this, index, hasProperty); +} diff --git a/src/qml/qml/v4/qv4managed_p.h b/src/qml/qml/v4/qv4managed_p.h new file mode 100644 index 0000000000..5e3c142bb8 --- /dev/null +++ b/src/qml/qml/v4/qv4managed_p.h @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_MANAGED_H +#define QMLJS_MANAGED_H + +#include <QtCore/QString> +#include <QtCore/QVector> +#include <QtCore/QDebug> +#include "qv4global_p.h" +#include "qv4value_def_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +#define Q_MANAGED_CHECK \ + template <typename T> inline void qt_check_for_QMANAGED_macro(const T &_q_argument) const \ + { int i = qYouForgotTheQ_MANAGED_Macro(this, &_q_argument); i = i + 1; } + +template <typename T> +inline int qYouForgotTheQ_MANAGED_Macro(T, T) { return 0; } + +template <typename T1, typename T2> +inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} + +#define Q_MANAGED \ + public: \ + Q_MANAGED_CHECK \ + static const QV4::ManagedVTable static_vtbl; + +struct GCDeletable +{ + GCDeletable() : next(0), lastCall(false) {} + virtual ~GCDeletable() {} + GCDeletable *next; + bool lastCall; +}; + +struct ManagedVTable +{ + Value (*call)(Managed *, const Value &thisObject, Value *args, int argc); + Value (*construct)(Managed *, Value *args, int argc); + void (*markObjects)(Managed *); + void (*destroy)(Managed *); + void (*collectDeletables)(Managed *, GCDeletable **deletable); + bool (*hasInstance)(Managed *, const Value &value); + Value (*get)(Managed *, String *name, bool *hasProperty); + Value (*getIndexed)(Managed *, uint index, bool *hasProperty); + void (*put)(Managed *, String *name, const Value &value); + void (*putIndexed)(Managed *, uint index, const Value &value); + PropertyAttributes (*query)(const Managed *, String *name); + PropertyAttributes (*queryIndexed)(const Managed *, uint index); + bool (*deleteProperty)(Managed *m, String *name); + bool (*deleteIndexedProperty)(Managed *m, uint index); + void (*getLookup)(Managed *m, Lookup *l, Value *result); + void (*setLookup)(Managed *m, Lookup *l, const Value &v); + bool (*isEqualTo)(Managed *m, Managed *other); + Property *(*advanceIterator)(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes); + const char *className; +}; + +#define DEFINE_MANAGED_VTABLE(classname) \ +const QV4::ManagedVTable classname::static_vtbl = \ +{ \ + call, \ + construct, \ + markObjects, \ + destroy, \ + 0, \ + hasInstance, \ + get, \ + getIndexed, \ + put, \ + putIndexed, \ + query, \ + queryIndexed, \ + deleteProperty, \ + deleteIndexedProperty, \ + getLookup, \ + setLookup, \ + isEqualTo, \ + advanceIterator, \ + #classname \ +} + +#define DEFINE_MANAGED_VTABLE_WITH_DELETABLES(classname) \ +const QV4::ManagedVTable classname::static_vtbl = \ +{ \ + call, \ + construct, \ + markObjects, \ + destroy, \ + collectDeletables, \ + hasInstance, \ + get, \ + getIndexed, \ + put, \ + putIndexed, \ + query, \ + queryIndexed, \ + deleteProperty, \ + deleteIndexedProperty, \ + getLookup, \ + setLookup, \ + isEqualTo, \ + advanceIterator, \ + #classname \ +} + +struct Q_QML_EXPORT Managed +{ +private: + void *operator new(size_t); + Managed(const Managed &other); + void operator = (const Managed &other); + +protected: + Managed(InternalClass *internal) + : _data(0), vtbl(&static_vtbl), internalClass(internal) + { inUse = 1; extensible = 1; } + +public: + void *operator new(size_t size, MemoryManager *mm); + void operator delete(void *ptr); + void operator delete(void *ptr, MemoryManager *mm); + + inline void mark() { + if (markBit) + return; + markBit = 1; + if (vtbl->markObjects) + vtbl->markObjects(this); + } + + enum Type { + Type_Invalid, + Type_String, + Type_Object, + Type_ArrayObject, + Type_FunctionObject, + Type_BooleanObject, + Type_NumberObject, + Type_StringObject, + Type_DateObject, + Type_RegExpObject, + Type_ErrorObject, + Type_ArgumentsObject, + Type_JSONObject, + Type_MathObject, + Type_ForeachIteratorObject, + Type_RegExp, + + Type_QmlSequence + }; + + ExecutionEngine *engine() const; + + template <typename T> + T *as() { +#if !defined(QT_NO_QOBJECT_CHECK) + reinterpret_cast<T *>(this)->qt_check_for_QMANAGED_macro(*reinterpret_cast<T *>(this)); +#endif + return vtbl == &T::static_vtbl ? static_cast<T *>(this) : 0; + } + template <typename T> + const T *as() const { +#if !defined(QT_NO_QOBJECT_CHECK) + reinterpret_cast<T *>(this)->qt_check_for_QMANAGED_macro(*reinterpret_cast<T *>(const_cast<Managed *>(this))); +#endif + return vtbl == &T::static_vtbl ? static_cast<const T *>(this) : 0; + } + + ArrayObject *asArrayObject() { return type == Type_ArrayObject ? reinterpret_cast<ArrayObject *>(this) : 0; } + FunctionObject *asFunctionObject() { return type == Type_FunctionObject ? reinterpret_cast<FunctionObject *>(this) : 0; } + BooleanObject *asBooleanObject() { return type == Type_BooleanObject ? reinterpret_cast<BooleanObject *>(this) : 0; } + NumberObject *asNumberObject() { return type == Type_NumberObject ? reinterpret_cast<NumberObject *>(this) : 0; } + StringObject *asStringObject() { return type == Type_StringObject ? reinterpret_cast<StringObject *>(this) : 0; } + DateObject *asDateObject() { return type == Type_DateObject ? reinterpret_cast<DateObject *>(this) : 0; } + ErrorObject *asErrorObject() { return type == Type_ErrorObject ? reinterpret_cast<ErrorObject *>(this) : 0; } + ArgumentsObject *asArgumentsObject() { return type == Type_ArgumentsObject ? reinterpret_cast<ArgumentsObject *>(this) : 0; } + + bool isListType() const { return type == Type_QmlSequence; } + + bool isArrayObject() const { return type == Type_ArrayObject; } + bool isStringObject() const { return type == Type_StringObject; } + + QString className() const; + + Managed **nextFreeRef() { + return reinterpret_cast<Managed **>(this); + } + Managed *nextFree() { + return *reinterpret_cast<Managed **>(this); + } + void setNextFree(Managed *m) { + *reinterpret_cast<Managed **>(this) = m; + } + + inline bool hasInstance(const Value &v) { + return vtbl->hasInstance(this, v); + } + Value construct(Value *args, int argc); + Value call(const Value &thisObject, Value *args, int argc); + Value get(String *name, bool *hasProperty = 0); + Value getIndexed(uint index, bool *hasProperty = 0); + void put(String *name, const Value &value) + { vtbl->put(this, name, value); } + void putIndexed(uint index, const Value &value) + { vtbl->putIndexed(this, index, value); } + PropertyAttributes query(String *name) const + { return vtbl->query(this, name); } + PropertyAttributes queryIndexed(uint index) const + { return vtbl->queryIndexed(this, index); } + + bool deleteProperty(String *name) + { return vtbl->deleteProperty(this, name); } + bool deleteIndexedProperty(uint index) + { return vtbl->deleteIndexedProperty(this, index); } + void getLookup(Lookup *l, Value *result) + { vtbl->getLookup(this, l, result); } + void setLookup(Lookup *l, const Value &v) + { vtbl->setLookup(this, l, v); } + + bool isEqualTo(Managed *other) + { return vtbl->isEqualTo(this, other); } + Property *advanceIterator(ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes) + { return vtbl->advanceIterator(this, it, name, index, attributes); } + + static void destroy(Managed *that) { that->_data = 0; } + static bool hasInstance(Managed *that, const Value &value); + static Value construct(Managed *m, Value *, int); + static Value call(Managed *m, const Value &, Value *, int); + static void getLookup(Managed *m, Lookup *, Value *); + static void setLookup(Managed *m, Lookup *l, const Value &v); + static bool isEqualTo(Managed *m, Managed *other); + + uint internalType() const { + return type; + } + + union { + uint _data; + struct { + uint markBit : 1; + uint inUse : 1; + uint extensible : 1; // used by Object + uint isNonStrictArgumentsObject : 1; + uint isBuiltinFunction : 1; // used by FunctionObject + uint needsActivation : 1; // used by FunctionObject + uint usesArgumentsObject : 1; // used by FunctionObject + uint strictMode : 1; // used by FunctionObject + uint type : 8; + mutable uint subtype : 3; + uint bindingKeyFlag : 1; + uint unused : 12; + }; + }; + +protected: + + static const ManagedVTable static_vtbl; + + const ManagedVTable *vtbl; +public: + InternalClass *internalClass; + +private: + friend class MemoryManager; + friend struct Identifiers; + friend struct ObjectIterator; +}; + +// ### Not a good placement +template<typename T> +inline T *Value::as() const { Managed *m = isObject() ? managed() : 0; return m ? m->as<T>() : 0; } + + +} + + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4/qv4math_p.h b/src/qml/qml/v4/qv4math_p.h new file mode 100644 index 0000000000..a3a3715545 --- /dev/null +++ b/src/qml/qml/v4/qv4math_p.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_MATH_H +#define QMLJS_MATH_H + +#include <qglobal.h> + +#ifndef QMLJS_LLVM_RUNTIME +# include <QtCore/qnumeric.h> +#endif // QMLJS_LLVM_RUNTIME +#include <cmath> + +#if defined(Q_CC_GNU) +#define QMLJS_READONLY __attribute((const)) +#else +#define QMLJS_READONLY +#endif + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +#if !defined(QMLJS_LLVM_RUNTIME) && defined(Q_CC_GNU) && defined(Q_PROCESSOR_X86) + +static inline QMLJS_READONLY Value add_int32(int a, int b) +{ + quint8 overflow = 0; + int aa = a; + + asm ("addl %2, %1\n" + "seto %0" + : "=q" (overflow), "=r" (aa) + : "r" (b), "1" (aa) + : "cc" + ); + if (!overflow) + return Value::fromInt32(aa); + return Value::fromDouble((double)a + (double)b); +} + +static inline QMLJS_READONLY Value sub_int32(int a, int b) +{ + quint8 overflow = 0; + int aa = a; + + asm ("subl %2, %1\n" + "seto %0" + : "=q" (overflow), "=r" (aa) + : "r" (b), "1" (aa) + : "cc" + ); + if (!overflow) + return Value::fromInt32(aa); + return Value::fromDouble((double)a - (double)b); +} + +static inline QMLJS_READONLY Value mul_int32(int a, int b) +{ + quint8 overflow = 0; + int aa = a; + + asm ("imul %2, %1\n" + "setc %0" + : "=q" (overflow), "=r" (aa) + : "r" (b), "1" (aa) + : "cc" + ); + if (!overflow) + return Value::fromInt32(aa); + return Value::fromDouble((double)a * (double)b); +} + +#else + +static inline QMLJS_READONLY Value add_int32(int a, int b) +{ + qint64 result = a + b; + if (result > INT_MAX || result < INT_MIN) + return Value::fromDouble(result); + return Value::fromInt32(static_cast<int>(result)); +} + +static inline QMLJS_READONLY Value sub_int32(int a, int b) +{ + qint64 result = a - b; + if (result > INT_MAX || result < INT_MIN) + return Value::fromDouble(result); + return Value::fromInt32(static_cast<int>(result)); +} + +static inline QMLJS_READONLY Value mul_int32(int a, int b) +{ + qint64 result = a * b; + if (result > INT_MAX || result < INT_MIN) + return Value::fromDouble(result); + return Value::fromInt32(static_cast<int>(result)); +} + +#endif // defined(QMLJS_INLINE_MATH) + +} + +QT_END_NAMESPACE + +#ifdef QMLJS_READONLY +#undef QMLJS_READONLY +#endif + +#endif // QMLJS_MATH_H diff --git a/src/qml/qml/v4/qv4mathobject.cpp b/src/qml/qml/v4/qv4mathobject.cpp new file mode 100644 index 0000000000..7aa56f51bd --- /dev/null +++ b/src/qml/qml/v4/qv4mathobject.cpp @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4mathobject_p.h" +#include "qv4objectproto_p.h" + +#include <cmath> +#include <qmath.h> +#include <qnumeric.h> + +using namespace QV4; + +static const double qt_PI = 2.0 * ::asin(1.0); + +MathObject::MathObject(ExecutionContext *ctx) + : Object(ctx->engine) +{ + type = Type_MathObject; + prototype = ctx->engine->objectPrototype; + + defineReadonlyProperty(ctx->engine, QStringLiteral("E"), Value::fromDouble(::exp(1.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LN2"), Value::fromDouble(::log(2.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LN10"), Value::fromDouble(::log(10.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LOG2E"), Value::fromDouble(1.0/::log(2.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("LOG10E"), Value::fromDouble(1.0/::log(10.0))); + defineReadonlyProperty(ctx->engine, QStringLiteral("PI"), Value::fromDouble(qt_PI)); + defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT1_2"), Value::fromDouble(::sqrt(0.5))); + defineReadonlyProperty(ctx->engine, QStringLiteral("SQRT2"), Value::fromDouble(::sqrt(2.0))); + + defineDefaultProperty(ctx, QStringLiteral("abs"), method_abs, 1); + defineDefaultProperty(ctx, QStringLiteral("acos"), method_acos, 1); + defineDefaultProperty(ctx, QStringLiteral("asin"), method_asin, 0); + defineDefaultProperty(ctx, QStringLiteral("atan"), method_atan, 1); + defineDefaultProperty(ctx, QStringLiteral("atan2"), method_atan2, 2); + defineDefaultProperty(ctx, QStringLiteral("ceil"), method_ceil, 1); + defineDefaultProperty(ctx, QStringLiteral("cos"), method_cos, 1); + defineDefaultProperty(ctx, QStringLiteral("exp"), method_exp, 1); + defineDefaultProperty(ctx, QStringLiteral("floor"), method_floor, 1); + defineDefaultProperty(ctx, QStringLiteral("log"), method_log, 1); + defineDefaultProperty(ctx, QStringLiteral("max"), method_max, 2); + defineDefaultProperty(ctx, QStringLiteral("min"), method_min, 2); + defineDefaultProperty(ctx, QStringLiteral("pow"), method_pow, 2); + defineDefaultProperty(ctx, QStringLiteral("random"), method_random, 0); + defineDefaultProperty(ctx, QStringLiteral("round"), method_round, 1); + defineDefaultProperty(ctx, QStringLiteral("sin"), method_sin, 1); + defineDefaultProperty(ctx, QStringLiteral("sqrt"), method_sqrt, 1); + defineDefaultProperty(ctx, QStringLiteral("tan"), method_tan, 1); +} + +/* copies the sign from y to x and returns the result */ +static double copySign(double x, double y) +{ + uchar *xch = (uchar *)&x; + uchar *ych = (uchar *)&y; + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + xch[0] = (xch[0] & 0x7f) | (ych[0] & 0x80); + else + xch[7] = (xch[7] & 0x7f) | (ych[7] & 0x80); + return x; +} + +Value MathObject::method_abs(SimpleCallContext *context) +{ + if (!context->argumentCount) + return Value::fromDouble(qSNaN()); + + if (context->arguments[0].isInteger()) { + int i = context->arguments[0].integerValue(); + return Value::fromInt32(i < 0 ? - i : i); + } + + double v = context->arguments[0].toNumber(); + if (v == 0) // 0 | -0 + return Value::fromDouble(0); + + return Value::fromDouble(v < 0 ? -v : v); +} + +Value MathObject::method_acos(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : 2; + if (v > 1) + return Value::fromDouble(qSNaN()); + + return Value::fromDouble(::acos(v)); +} + +Value MathObject::method_asin(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : 2; + if (v > 1) + return Value::fromDouble(qSNaN()); + else + return Value::fromDouble(::asin(v)); +} + +Value MathObject::method_atan(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + if (v == 0.0) + return Value::fromDouble(v); + else + return Value::fromDouble(::atan(v)); +} + +Value MathObject::method_atan2(SimpleCallContext *context) +{ + double v1 = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + double v2 = context->argumentCount > 1 ? context->arguments[1].toNumber() : qSNaN(); + + if ((v1 < 0) && qIsFinite(v1) && qIsInf(v2) && (copySign(1.0, v2) == 1.0)) + return Value::fromDouble(copySign(0, -1.0)); + + if ((v1 == 0.0) && (v2 == 0.0)) { + if ((copySign(1.0, v1) == 1.0) && (copySign(1.0, v2) == -1.0)) { + return Value::fromDouble(qt_PI); + } else if ((copySign(1.0, v1) == -1.0) && (copySign(1.0, v2) == -1.0)) { + return Value::fromDouble(-qt_PI); + } + } + return Value::fromDouble(::atan2(v1, v2)); +} + +Value MathObject::method_ceil(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + if (v < 0.0 && v > -1.0) + return Value::fromDouble(copySign(0, -1.0)); + else + return Value::fromDouble(::ceil(v)); +} + +Value MathObject::method_cos(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + return Value::fromDouble(::cos(v)); +} + +Value MathObject::method_exp(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + if (qIsInf(v)) { + if (copySign(1.0, v) == -1.0) + return Value::fromDouble(0); + else + return Value::fromDouble(qInf()); + } else { + return Value::fromDouble(::exp(v)); + } +} + +Value MathObject::method_floor(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + return Value::fromDouble(::floor(v)); +} + +Value MathObject::method_log(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + if (v < 0) + return Value::fromDouble(qSNaN()); + else + return Value::fromDouble(::log(v)); +} + +Value MathObject::method_max(SimpleCallContext *context) +{ + double mx = -qInf(); + for (unsigned i = 0; i < context->argumentCount; ++i) { + double x = context->arguments[i].toNumber(); + if (x > mx || std::isnan(x)) + mx = x; + } + return Value::fromDouble(mx); +} + +Value MathObject::method_min(SimpleCallContext *context) +{ + double mx = qInf(); + for (unsigned i = 0; i < context->argumentCount; ++i) { + double x = context->arguments[i].toNumber(); + if ((x == 0 && mx == x && copySign(1.0, x) == -1.0) + || (x < mx) || std::isnan(x)) { + mx = x; + } + } + return Value::fromDouble(mx); +} + +Value MathObject::method_pow(SimpleCallContext *context) +{ + double x = context->argumentCount > 0 ? context->arguments[0].toNumber() : qSNaN(); + double y = context->argumentCount > 1 ? context->arguments[1].toNumber() : qSNaN(); + + if (std::isnan(y)) + return Value::fromDouble(qSNaN()); + + if (y == 0) { + return Value::fromDouble(1); + } else if (((x == 1) || (x == -1)) && std::isinf(y)) { + return Value::fromDouble(qSNaN()); + } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) { + return Value::fromDouble(qInf()); + } else if ((x == 0) && copySign(1.0, x) == -1.0) { + if (y < 0) { + if (::fmod(-y, 2.0) == 1.0) + return Value::fromDouble(-qInf()); + else + return Value::fromDouble(qInf()); + } else if (y > 0) { + if (::fmod(y, 2.0) == 1.0) + return Value::fromDouble(copySign(0, -1.0)); + else + return Value::fromDouble(0); + } + } + +#ifdef Q_OS_AIX + else if (qIsInf(x) && copySign(1.0, x) == -1.0) { + if (y > 0) { + if (::fmod(y, 2.0) == 1.0) + return Value::fromDouble(-qInf()); + else + return Value::fromDouble(qInf()); + } else if (y < 0) { + if (::fmod(-y, 2.0) == 1.0) + return Value::fromDouble(copySign(0, -1.0)); + else + return Value::fromDouble(0); + } + } +#endif + else { + return Value::fromDouble(::pow(x, y)); + } + // ### + return Value::fromDouble(qSNaN()); +} + +Value MathObject::method_random(SimpleCallContext *) +{ + return Value::fromDouble(qrand() / (double) RAND_MAX); +} + +Value MathObject::method_round(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + v = copySign(::floor(v + 0.5), v); + return Value::fromDouble(v); +} + +Value MathObject::method_sin(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + return Value::fromDouble(::sin(v)); +} + +Value MathObject::method_sqrt(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + return Value::fromDouble(::sqrt(v)); +} + +Value MathObject::method_tan(SimpleCallContext *context) +{ + double v = context->argumentCount ? context->arguments[0].toNumber() : qSNaN(); + if (v == 0.0) + return Value::fromDouble(v); + else + return Value::fromDouble(::tan(v)); +} + diff --git a/src/qml/qml/v4/qv4mathobject_p.h b/src/qml/qml/v4/qv4mathobject_p.h new file mode 100644 index 0000000000..03c36bcc68 --- /dev/null +++ b/src/qml/qml/v4/qv4mathobject_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4MATHOBJECT_H +#define QV$MATHOBJECT_H + +#include "qv4object_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct MathObject: Object +{ + MathObject(ExecutionContext *ctx); + + static Value method_abs(SimpleCallContext *context); + static Value method_acos(SimpleCallContext *context); + static Value method_asin(SimpleCallContext *context); + static Value method_atan(SimpleCallContext *context); + static Value method_atan2(SimpleCallContext *context); + static Value method_ceil(SimpleCallContext *context); + static Value method_cos(SimpleCallContext *context); + static Value method_exp(SimpleCallContext *context); + static Value method_floor(SimpleCallContext *context); + static Value method_log(SimpleCallContext *context); + static Value method_max(SimpleCallContext *context); + static Value method_min(SimpleCallContext *context); + static Value method_pow(SimpleCallContext *context); + static Value method_random(SimpleCallContext *context); + static Value method_round(SimpleCallContext *context); + static Value method_sin(SimpleCallContext *context); + static Value method_sqrt(SimpleCallContext *context); + static Value method_tan(SimpleCallContext *context); +}; + +} + +QT_END_NAMESPACE + +#endif // QMLJS_OBJECTS_H diff --git a/src/qml/qml/v4/qv4mm.cpp b/src/qml/qml/v4/qv4mm.cpp new file mode 100644 index 0000000000..0e53a2088f --- /dev/null +++ b/src/qml/qml/v4/qv4mm.cpp @@ -0,0 +1,605 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4engine_p.h" +#include "qv4object_p.h" +#include "qv4objectproto_p.h" +#include "qv4mm_p.h" +#include "qv4qobjectwrapper_p.h" +#include <qqmlengine.h> +#include "PageAllocation.h" +#include "StdLibExtras.h" + +#include <QTime> +#include <QVector> +#include <QVector> +#include <QMap> + +#include <iostream> +#include <cstdlib> +#include "qv4alloca_p.h" + +#ifdef V4_USE_VALGRIND +#include <valgrind/valgrind.h> +#include <valgrind/memcheck.h> +#endif + +#if OS(QNX) +#include <sys/storage.h> // __tls() +#endif + +QT_BEGIN_NAMESPACE + +using namespace QV4; +using namespace WTF; + +static const std::size_t CHUNK_SIZE = 1024*32; + +struct MemoryManager::Data +{ + bool enableGC; + bool gcBlocked; + bool scribble; + bool aggressiveGC; + ExecutionEngine *engine; + quintptr *stackTop; + + enum { MaxItemSize = 512 }; + Managed *smallItems[MaxItemSize/16]; + uint nChunks[MaxItemSize/16]; + uint availableItems[MaxItemSize/16]; + uint allocCount[MaxItemSize/16]; + struct Chunk { + PageAllocation memory; + int chunkSize; + }; + + QVector<Chunk> heapChunks; + QHash<Managed *, uint> protectedObject; + + // statistics: +#ifdef DETAILED_MM_STATS + QVector<unsigned> allocSizeCounters; +#endif // DETAILED_MM_STATS + + Data(bool enableGC) + : enableGC(enableGC) + , gcBlocked(false) + , engine(0) + , stackTop(0) + { + memset(smallItems, 0, sizeof(smallItems)); + memset(nChunks, 0, sizeof(nChunks)); + memset(availableItems, 0, sizeof(availableItems)); + memset(allocCount, 0, sizeof(allocCount)); + scribble = !qgetenv("MM_SCRIBBLE").isEmpty(); + aggressiveGC = !qgetenv("MM_AGGRESSIVE_GC").isEmpty(); + } + + ~Data() + { + for (QVector<Chunk>::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) + i->memory.deallocate(); + } +}; + +#define SCRIBBLE(obj, c, size) \ + if (m_d->scribble) \ + ::memset((void *)(obj + 1), c, size - sizeof(Managed)); + + +namespace QV4 { + +bool operator<(const MemoryManager::Data::Chunk &a, const MemoryManager::Data::Chunk &b) +{ + return a.memory.base() < b.memory.base(); +} + +} // namespace QV4 + +MemoryManager::MemoryManager() + : m_d(new Data(true)) + , m_contextList(0) + , m_persistentValues(0) + , m_weakValues(0) +{ + setEnableGC(true); +#ifdef V4_USE_VALGRIND + VALGRIND_CREATE_MEMPOOL(this, 0, true); +#endif + +#if OS(QNX) + // TLS is at the top of each thread's stack, + // so the stack base for thread is the result of __tls() + m_d->stackTop = reinterpret_cast<quintptr *>( + (((uintptr_t)__tls() + __PAGESIZE - 1) & ~(__PAGESIZE - 1))); +#elif USE(PTHREADS) +# if OS(DARWIN) + void *st = pthread_get_stackaddr_np(pthread_self()); + m_d->stackTop = static_cast<quintptr *>(st); +# else + void* stackBottom = 0; + pthread_attr_t attr; + pthread_getattr_np(pthread_self(), &attr); + size_t stackSize = 0; + pthread_attr_getstack(&attr, &stackBottom, &stackSize); + pthread_attr_destroy(&attr); + + m_d->stackTop = static_cast<quintptr *>(stackBottom) + stackSize/sizeof(quintptr); +# endif +#elif OS(WINDOWS) + PNT_TIB tib = (PNT_TIB)NtCurrentTeb(); + m_d->stackTop = static_cast<quintptr*>(tib->StackBase); +#else +# error "Unsupported platform: no way to get the top-of-stack." +#endif + +} + +Managed *MemoryManager::alloc(std::size_t size) +{ + if (m_d->aggressiveGC) + runGC(); +#ifdef DETAILED_MM_STATS + willAllocate(size); +#endif // DETAILED_MM_STATS + + assert(size >= 16); + assert(size % 16 == 0); + + size_t pos = size >> 4; + ++m_d->allocCount[pos]; + + // fits into a small bucket + assert(size < MemoryManager::Data::MaxItemSize); + + Managed *m = m_d->smallItems[pos]; + if (m) + goto found; + + // try to free up space, otherwise allocate + if (m_d->allocCount[pos] > (m_d->availableItems[pos] >> 1) && !m_d->aggressiveGC) { + runGC(); + m = m_d->smallItems[pos]; + if (m) + goto found; + } + + // no free item available, allocate a new chunk + { + // allocate larger chunks at a time to avoid excessive GC, but cap at 64M chunks + uint shift = ++m_d->nChunks[pos]; + if (shift > 10) + shift = 10; + std::size_t allocSize = CHUNK_SIZE*(1 << shift); + allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize); + Data::Chunk allocation; + allocation.memory = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages); + allocation.chunkSize = size; + m_d->heapChunks.append(allocation); + qSort(m_d->heapChunks); + char *chunk = (char *)allocation.memory.base(); + char *end = chunk + allocation.memory.size() - size; + memset(chunk, 0, allocation.memory.size()); + Managed **last = &m_d->smallItems[pos]; + while (chunk <= end) { + Managed *o = reinterpret_cast<Managed *>(chunk); + o->_data = 0; + *last = o; + last = o->nextFreeRef(); + chunk += size; + } + *last = 0; + m = m_d->smallItems[pos]; + m_d->availableItems[pos] += allocation.memory.size()/size - 1; +#ifdef V4_USE_VALGRIND + VALGRIND_MAKE_MEM_NOACCESS(allocation.memory, allocation.chunkSize); +#endif + } + + found: +#ifdef V4_USE_VALGRIND + VALGRIND_MEMPOOL_ALLOC(this, m, size); +#endif + + m_d->smallItems[pos] = m->nextFree(); + return m; +} + +void MemoryManager::mark() +{ + m_d->engine->markObjects(); + + for (QHash<Managed *, uint>::const_iterator it = m_d->protectedObject.begin(); it != m_d->protectedObject.constEnd(); ++it) + it.key()->mark(); + + PersistentValuePrivate *persistent = m_persistentValues; + while (persistent) { + if (!persistent->refcount) { + PersistentValuePrivate *n = persistent->next; + persistent->removeFromList(); + delete persistent; + persistent = n; + continue; + } + if (Managed *m = persistent->value.asManaged()) + m->mark(); + persistent = persistent->next; + } + + // push all caller saved registers to the stack, so we can find the objects living in these registers +#if COMPILER(MSVC) +# if CPU(X86_64) + HANDLE thread = GetCurrentThread(); + WOW64_CONTEXT ctxt; + /*bool success =*/ Wow64GetThreadContext(thread, &ctxt); +# elif CPU(X86) + HANDLE thread = GetCurrentThread(); + CONTEXT ctxt; + /*bool success =*/ GetThreadContext(thread, &ctxt); +# endif // CPU +#elif COMPILER(CLANG) || COMPILER(GCC) +# if CPU(X86_64) + quintptr regs[5]; + asm( + "mov %%rbp, %0\n" + "mov %%r12, %1\n" + "mov %%r13, %2\n" + "mov %%r14, %3\n" + "mov %%r15, %4\n" + : "=m" (regs[0]), "=m" (regs[1]), "=m" (regs[2]), "=m" (regs[3]), "=m" (regs[4]) + : + : + ); +# endif // CPU +#endif // COMPILER + + collectFromStack(); + + // Preserve QObject ownership rules within JavaScript: A parent with c++ ownership + // keeps all of its children alive in JavaScript. + + // Do this _after_ collectFromStack to ensure that processing the weak + // managed objects in the loop down there doesn't make then end up as leftovers + // on the stack and thus always get collected. + for (PersistentValuePrivate *weak = m_weakValues; weak; weak = weak->next) { + if (!weak->refcount) + continue; + QObjectWrapper *qobjectWrapper = weak->value.as<QObjectWrapper>(); + if (!qobjectWrapper) + continue; + QObject *qobject = qobjectWrapper->object(); + if (!qobject) + continue; + bool keepAlive = QQmlData::keepAliveDuringGarbageCollection(qobject); + + if (!keepAlive) { + if (QObject *parent = qobject->parent()) { + while (parent->parent()) + parent = parent->parent(); + + keepAlive = QQmlData::keepAliveDuringGarbageCollection(parent); + } + } + + if (keepAlive) + qobjectWrapper->mark(); + } +} + +std::size_t MemoryManager::sweep(bool lastSweep) +{ + PersistentValuePrivate *weak = m_weakValues; + while (weak) { + if (!weak->refcount) { + PersistentValuePrivate *n = weak->next; + weak->removeFromList(); + delete weak; + weak = n; + continue; + } + if (Managed *m = weak->value.asManaged()) { + if (!m->markBit) { + weak->value = Value::emptyValue(); + PersistentValuePrivate *n = weak->next; + weak->removeFromList(); + weak = n; + continue; + } + } + weak = weak->next; + } + + if (MultiplyWrappedQObjectMap *multiplyWrappedQObjects = m_d->engine->m_multiplyWrappedQObjects) { + for (MultiplyWrappedQObjectMap::Iterator it = multiplyWrappedQObjects->begin(); it != multiplyWrappedQObjects->end();) { + if (!it.value()->markBit) + it = multiplyWrappedQObjects->erase(it); + else + ++it; + } + } + + std::size_t freedCount = 0; + GCDeletable *deletable = 0; + GCDeletable **firstDeletable = &deletable; + + for (QVector<Data::Chunk>::iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i) + freedCount += sweep(reinterpret_cast<char*>(i->memory.base()), i->memory.size(), i->chunkSize, &deletable); + + ExecutionContext *ctx = m_contextList; + ExecutionContext **n = &m_contextList; + while (ctx) { + ExecutionContext *next = ctx->next; + if (!ctx->marked) { + free(ctx); + *n = next; + } else { + ctx->marked = false; + n = &ctx->next; + } + ctx = next; + } + + deletable = *firstDeletable; + while (deletable) { + GCDeletable *next = deletable->next; + deletable->lastCall = lastSweep; + delete deletable; + deletable = next; + } + + return freedCount; +} + +std::size_t MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t size, GCDeletable **deletable) +{ +// qDebug("chunkStart @ %p, size=%x, pos=%x (%x)", chunkStart, size, size>>4, m_d->smallItems[size >> 4]); + std::size_t freedCount = 0; + + Managed **f = &m_d->smallItems[size >> 4]; + +#ifdef V4_USE_VALGRIND + VALGRIND_DISABLE_ERROR_REPORTING; +#endif + for (char *chunk = chunkStart, *chunkEnd = chunk + chunkSize - size; chunk <= chunkEnd; chunk += size) { + Managed *m = reinterpret_cast<Managed *>(chunk); +// qDebug("chunk @ %p, size = %lu, in use: %s, mark bit: %s", +// chunk, m->size, (m->inUse ? "yes" : "no"), (m->markBit ? "true" : "false")); + + assert((intptr_t) chunk % 16 == 0); + + if (m->inUse) { + if (m->markBit) { + m->markBit = 0; + } else { +// qDebug() << "-- collecting it." << m << *f << m->nextFree(); +#ifdef V4_USE_VALGRIND + VALGRIND_ENABLE_ERROR_REPORTING; +#endif + if (m->vtbl->collectDeletables) + m->vtbl->collectDeletables(m, deletable); + m->vtbl->destroy(m); + + m->setNextFree(*f); +#ifdef V4_USE_VALGRIND + VALGRIND_DISABLE_ERROR_REPORTING; + VALGRIND_MEMPOOL_FREE(this, m); +#endif + *f = m; + SCRIBBLE(m, 0x99, size); + ++freedCount; + } + } + } +#ifdef V4_USE_VALGRIND + VALGRIND_ENABLE_ERROR_REPORTING; +#endif + + return freedCount; +} + +bool MemoryManager::isGCBlocked() const +{ + return m_d->gcBlocked; +} + +void MemoryManager::setGCBlocked(bool blockGC) +{ + m_d->gcBlocked = blockGC; +} + +void MemoryManager::runGC() +{ + if (!m_d->enableGC || m_d->gcBlocked) { +// qDebug() << "Not running GC."; + return; + } + +// QTime t; t.start(); + +// qDebug() << ">>>>>>>>runGC"; + + mark(); +// std::cerr << "GC: marked " << marks +// << " objects in " << t.elapsed() +// << "ms" << std::endl; + +// t.restart(); + /*std::size_t freedCount =*/ sweep(); +// std::cerr << "GC: sweep freed " << freedCount +// << " objects in " << t.elapsed() +// << "ms" << std::endl; + memset(m_d->allocCount, 0, sizeof(m_d->allocCount)); +} + +void MemoryManager::setEnableGC(bool enableGC) +{ + m_d->enableGC = enableGC; +} + +MemoryManager::~MemoryManager() +{ + PersistentValuePrivate *persistent = m_persistentValues; + while (persistent) { + PersistentValuePrivate *n = persistent->next; + persistent->value = Value::undefinedValue(); + persistent->engine = 0; + persistent->prev = 0; + persistent->next = 0; + persistent = n; + } + + sweep(/*lastSweep*/true); +#ifdef V4_USE_VALGRIND + VALGRIND_DESTROY_MEMPOOL(this); +#endif +} + +void MemoryManager::protect(Managed *m) +{ + ++m_d->protectedObject[m]; +} + +void MemoryManager::unprotect(Managed *m) +{ + if (!--m_d->protectedObject[m]) + m_d->protectedObject.remove(m); +} + +static inline void add(QVector<Managed *> &values, const Value &v) +{ + if (Object *o = v.asObject()) + values.append(o); +} + +void MemoryManager::setExecutionEngine(ExecutionEngine *engine) +{ + m_d->engine = engine; +} + +void MemoryManager::dumpStats() const +{ +#ifdef DETAILED_MM_STATS + std::cerr << "=================" << std::endl; + std::cerr << "Allocation stats:" << std::endl; + std::cerr << "Requests for each chunk size:" << std::endl; + for (int i = 0; i < m_d->allocSizeCounters.size(); ++i) { + if (unsigned count = m_d->allocSizeCounters[i]) { + std::cerr << "\t" << (i << 4) << " bytes chunks: " << count << std::endl; + } + } +#endif // DETAILED_MM_STATS +} + +ExecutionEngine *MemoryManager::engine() const +{ + return m_d->engine; +} + +#ifdef DETAILED_MM_STATS +void MemoryManager::willAllocate(std::size_t size) +{ + unsigned alignedSize = (size + 15) >> 4; + QVector<unsigned> &counters = m_d->allocSizeCounters; + if ((unsigned) counters.size() < alignedSize + 1) + counters.resize(alignedSize + 1); + counters[alignedSize]++; +} + +#endif // DETAILED_MM_STATS + +void MemoryManager::collectFromStack() const +{ + quintptr valueOnStack = 0; + + if (!m_d->heapChunks.count()) + return; + + quintptr *current = (&valueOnStack) + 1; +// qDebug() << "collectFromStack";// << top << current << &valueOnStack; + +#if V4_USE_VALGRIND + VALGRIND_MAKE_MEM_DEFINED(current, (m_d->stackTop - current)*sizeof(quintptr)); +#endif + + char** heapChunkBoundaries = (char**)alloca(m_d->heapChunks.count() * 2 * sizeof(char*)); + char** heapChunkBoundariesEnd = heapChunkBoundaries + 2 * m_d->heapChunks.count(); + int i = 0; + for (QVector<Data::Chunk>::Iterator it = m_d->heapChunks.begin(), end = + m_d->heapChunks.end(); it != end; ++it) { + heapChunkBoundaries[i++] = reinterpret_cast<char*>(it->memory.base()) - 1; + heapChunkBoundaries[i++] = reinterpret_cast<char*>(it->memory.base()) + it->memory.size() - it->chunkSize; + } + assert(i == m_d->heapChunks.count() * 2); + + for (; current < m_d->stackTop; ++current) { + char* genericPtr = +#if QT_POINTER_SIZE == 8 + reinterpret_cast<char *>((*current) & ~(quint64(Value::Type_Mask) << Value::Tag_Shift)); +#else + reinterpret_cast<char *>(*current); +#endif + + if (genericPtr < *heapChunkBoundaries || genericPtr > *(heapChunkBoundariesEnd - 1)) + continue; + int index = qLowerBound(heapChunkBoundaries, heapChunkBoundariesEnd, genericPtr) - heapChunkBoundaries; + // An odd index means the pointer is _before_ the end of a heap chunk and therefore valid. + assert(index >= 0 && index < m_d->heapChunks.count() * 2); + if (index & 1) { + int size = m_d->heapChunks.at(index >> 1).chunkSize; + Managed *m = reinterpret_cast<Managed *>(genericPtr); +// qDebug() << " inside" << size; + + if (((quintptr)m - (quintptr)heapChunkBoundaries[index-1] - 1 ) % size) + // wrongly aligned value, skip it + continue; + + if (!m->inUse) + // Skip pointers to already freed objects, they are bogus as well + continue; + +// qDebug() << " marking"; + m->mark(); + } + } +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4mm_p.h b/src/qml/qml/v4/qv4mm_p.h new file mode 100644 index 0000000000..f72d23dc9a --- /dev/null +++ b/src/qml/qml/v4/qv4mm_p.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4GC_H +#define QV4GC_H + +#include "qv4global_p.h" +#include "qv4context_p.h" + +#include <QScopedPointer> + +//#define DETAILED_MM_STATS + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ExecutionEngine; +struct ExecutionContext; +struct Managed; +struct GCDeletable; + +class Q_QML_EXPORT MemoryManager +{ + MemoryManager(const MemoryManager &); + MemoryManager &operator=(const MemoryManager&); + +public: + struct Data; + + class GCBlocker + { + public: + GCBlocker(MemoryManager *mm) + : mm(mm) + , wasBlocked(mm->isGCBlocked()) + { + mm->setGCBlocked(true); + } + + ~GCBlocker() + { + mm->setGCBlocked(wasBlocked); + } + + private: + MemoryManager *mm; + bool wasBlocked; + }; + +public: + MemoryManager(); + ~MemoryManager(); + + void protect(Managed *m); + void unprotect(Managed *m); + + // TODO: this is only for 64bit (and x86 with SSE/AVX), so exend it for other architectures to be slightly more efficient (meaning, align on 8-byte boundaries). + // Note: all occurances of "16" in alloc/dealloc are also due to the alignment. + static inline std::size_t align(std::size_t size) + { return (size + 15) & ~0xf; } + + inline Managed *allocManaged(std::size_t size) + { + size = align(size); + Managed *o = alloc(size); + return o; + } + + ExecutionContext *allocContext(uint size); + + bool isGCBlocked() const; + void setGCBlocked(bool blockGC); + void runGC(); + + void setEnableGC(bool enableGC); + void setExecutionEngine(ExecutionEngine *engine); + + void dumpStats() const; + +protected: + /// expects size to be aligned + // TODO: try to inline + Managed *alloc(std::size_t size); + + ExecutionEngine *engine() const; + +#ifdef DETAILED_MM_STATS + void willAllocate(std::size_t size); +#endif // DETAILED_MM_STATS + +private: + void collectFromStack() const; + void mark(); + std::size_t sweep(bool lastSweep = false); + std::size_t sweep(char *chunkStart, std::size_t chunkSize, size_t size, GCDeletable **deletable); + +protected: + QScopedPointer<Data> m_d; + ExecutionContext *m_contextList; +public: + PersistentValuePrivate *m_persistentValues; + PersistentValuePrivate *m_weakValues; +}; + +inline ExecutionContext *MemoryManager::allocContext(uint size) +{ + ExecutionContext *newContext = (ExecutionContext *)malloc(size); + newContext->next = m_contextList; + m_contextList = newContext; + return newContext; +} + + +} + +QT_END_NAMESPACE + +#endif // QV4GC_H diff --git a/src/qml/qml/v4/qv4numberobject.cpp b/src/qml/qml/v4/qv4numberobject.cpp new file mode 100644 index 0000000000..266fa792dc --- /dev/null +++ b/src/qml/qml/v4/qv4numberobject.cpp @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4numberobject_p.h" +#include <QtCore/qnumeric.h> +#include <QtCore/qmath.h> +#include <QtCore/QDebug> +#include <cassert> +#include <double-conversion.h> + +using namespace QV4; + +DEFINE_MANAGED_VTABLE(NumberCtor); + +NumberCtor::NumberCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Number"))) +{ + vtbl = &static_vtbl; +} + +Value NumberCtor::construct(Managed *m, Value *args, int argc) +{ + double d = argc ? args[0].toNumber() : 0.; + return Value::fromObject(m->engine()->newNumberObject(Value::fromDouble(d))); +} + +Value NumberCtor::call(Managed *, const Value &, Value *argv, int argc) +{ + double d = argc ? argv[0].toNumber() : 0.; + return Value::fromDouble(d); +} + +void NumberPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NaN"), Value::fromDouble(qSNaN())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("NEGATIVE_INFINITY"), Value::fromDouble(-qInf())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("POSITIVE_INFINITY"), Value::fromDouble(qInf())); + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MAX_VALUE"), Value::fromDouble(1.7976931348623158e+308)); + +#ifdef __INTEL_COMPILER +# pragma warning( push ) +# pragma warning(disable: 239) +#endif + ctor.objectValue()->defineReadonlyProperty(ctx->engine, QStringLiteral("MIN_VALUE"), Value::fromDouble(5e-324)); +#ifdef __INTEL_COMPILER +# pragma warning( pop ) +#endif + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf); + defineDefaultProperty(ctx, QStringLiteral("toFixed"), method_toFixed, 1); + defineDefaultProperty(ctx, QStringLiteral("toExponential"), method_toExponential); + defineDefaultProperty(ctx, QStringLiteral("toPrecision"), method_toPrecision); +} + +Value NumberPrototype::method_toString(SimpleCallContext *ctx) +{ + double num; + if (ctx->thisObject.isNumber()) { + num = ctx->thisObject.asDouble(); + } else { + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + num = thisObject->value.asDouble(); + } + + Value arg = ctx->argument(0); + if (!arg.isUndefined()) { + int radix = arg.toInt32(); + if (radix < 2 || radix > 36) { + ctx->throwError(QString::fromLatin1("Number.prototype.toString: %0 is not a valid radix") + .arg(radix)); + return Value::undefinedValue(); + } + + if (std::isnan(num)) { + return Value::fromString(ctx, QStringLiteral("NaN")); + } else if (qIsInf(num)) { + return Value::fromString(ctx, QLatin1String(num < 0 ? "-Infinity" : "Infinity")); + } + + if (radix != 10) { + QString str; + bool negative = false; + if (num < 0) { + negative = true; + num = -num; + } + double frac = num - ::floor(num); + num = Value::toInteger(num); + do { + char c = (char)::fmod(num, radix); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.prepend(QLatin1Char(c)); + num = ::floor(num / radix); + } while (num != 0); + if (frac != 0) { + str.append(QLatin1Char('.')); + do { + frac = frac * radix; + char c = (char)::floor(frac); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + str.append(QLatin1Char(c)); + frac = frac - ::floor(frac); + } while (frac != 0); + } + if (negative) + str.prepend(QLatin1Char('-')); + return Value::fromString(ctx, str); + } + } + + String *str = Value::fromDouble(num).toString(ctx); + return Value::fromString(str); +} + +Value NumberPrototype::method_toLocaleString(SimpleCallContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + String *str = thisObject->value.toString(ctx); + return Value::fromString(str); +} + +Value NumberPrototype::method_valueOf(SimpleCallContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + return thisObject->value; +} + +Value NumberPrototype::method_toFixed(SimpleCallContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + double fdigits = 0; + + if (ctx->argumentCount > 0) + fdigits = ctx->argument(0).toInteger(); + + if (std::isnan(fdigits)) + fdigits = 0; + + if (fdigits < 0 || fdigits > 20) + ctx->throwRangeError(ctx->thisObject); + + double v = thisObject->value.asDouble(); + QString str; + if (std::isnan(v)) + str = QString::fromLatin1("NaN"); + else if (qIsInf(v)) + str = QString::fromLatin1(v < 0 ? "-Infinity" : "Infinity"); + else if (v < 1.e21) + str = QString::number(v, 'f', int (fdigits)); + else + return __qmljs_string_from_number(ctx, v); + return Value::fromString(ctx, str); +} + +Value NumberPrototype::method_toExponential(SimpleCallContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + Value fraction = ctx->argument(0); + int fdigits = -1; + + if (!fraction.isUndefined()) { + int fdigits = ctx->argument(0).toInt32(); + if (fdigits < 0 || fdigits > 20) { + String *error = ctx->engine->newString(QStringLiteral("Number.prototype.toExponential: fractionDigits out of range")); + ctx->throwRangeError(Value::fromString(error)); + } + } + + char str[100]; + double_conversion::StringBuilder builder(str, sizeof(str)); + double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToExponential(thisObject->value.asDouble(), fdigits, &builder); + QString result = QString::fromLatin1(builder.Finalize()); + + return Value::fromString(ctx, result); +} + +Value NumberPrototype::method_toPrecision(SimpleCallContext *ctx) +{ + NumberObject *thisObject = ctx->thisObject.asNumberObject(); + if (!thisObject) + ctx->throwTypeError(); + + Value prec = ctx->argument(0); + if (prec.isUndefined()) + return __qmljs_to_string(thisObject->value, ctx); + + double precision = prec.toInt32(); + if (precision < 1 || precision > 21) { + String *error = ctx->engine->newString(QStringLiteral("Number.prototype.toPrecision: precision out of range")); + ctx->throwRangeError(Value::fromString(error)); + } + + char str[100]; + double_conversion::StringBuilder builder(str, sizeof(str)); + double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToPrecision(thisObject->value.asDouble(), precision, &builder); + QString result = QString::fromLatin1(builder.Finalize()); + + return Value::fromString(ctx, result); +} diff --git a/src/qml/qml/v4/qv4numberobject_p.h b/src/qml/qml/v4/qv4numberobject_p.h new file mode 100644 index 0000000000..0c06451c98 --- /dev/null +++ b/src/qml/qml/v4/qv4numberobject_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4NUMBEROBJECT_H +#define QV4NUMBEROBJECT_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct NumberCtor: FunctionObject +{ + NumberCtor(ExecutionContext *scope); + + static Value construct(Managed *that, Value *args, int argc); + static Value call(Managed *, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct NumberPrototype: NumberObject +{ + NumberPrototype(ExecutionEngine *engine): NumberObject(engine, Value::fromDouble(0)) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_toString(SimpleCallContext *ctx); + static Value method_toLocaleString(SimpleCallContext *ctx); + static Value method_valueOf(SimpleCallContext *ctx); + static Value method_toFixed(SimpleCallContext *ctx); + static Value method_toExponential(SimpleCallContext *ctx); + static Value method_toPrecision(SimpleCallContext *ctx); +}; + + +} + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4/qv4object.cpp b/src/qml/qml/v4/qv4object.cpp new file mode 100644 index 0000000000..16b57871af --- /dev/null +++ b/src/qml/qml/v4/qv4object.cpp @@ -0,0 +1,1383 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4object_p.h" +#include "qv4jsir_p.h" +#include "qv4isel_p.h" +#include "qv4objectproto_p.h" +#include "qv4stringobject_p.h" +#include "qv4argumentsobject_p.h" +#include "qv4mm_p.h" +#include "qv4lookup_p.h" + +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsast_p.h> +#include <qv4jsir_p.h> +#include <qv4codegen_p.h> +#include "private/qlocale_tools_p.h" + +#include <QtCore/qmath.h> +#include <QtCore/QDebug> +#include <cassert> +#include <typeinfo> +#include <iostream> +#include <stdint.h> +#include "qv4alloca_p.h" + +using namespace QV4; + +DEFINE_MANAGED_VTABLE(Object); + +Object::Object(ExecutionEngine *engine) + : Managed(engine->emptyClass) + , prototype(0) + , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) + , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0) +{ + vtbl = &static_vtbl; + type = Type_Object; + memset(memberData, 0, sizeof(Property)*memberDataAlloc); +} + +Object::Object(ExecutionContext *context) + : Managed(context->engine->emptyClass) + , prototype(0) + , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) + , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0) +{ + vtbl = &static_vtbl; + type = Type_Object; + memset(memberData, 0, sizeof(Property)*memberDataAlloc); +} + +Object::Object(ExecutionEngine *engine, InternalClass *internalClass) + : Managed(internalClass) + , prototype(0) + , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) + , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0) +{ + vtbl = &static_vtbl; + type = Type_Object; + + if (internalClass->size >= memberDataAlloc) { + memberDataAlloc = internalClass->size; + memberData = new Property[memberDataAlloc]; + } + memset(memberData, 0, sizeof(Property)*memberDataAlloc); +} + +Object::~Object() +{ + if (memberData != inlineProperties) + delete [] memberData; + delete [] (arrayData - (sparseArray ? 0 : arrayOffset)); + if (arrayAttributes) + delete [] (arrayAttributes - (sparseArray ? 0 : arrayOffset)); + delete sparseArray; + _data = 0; +} + +void Object::destroy(Managed *that) +{ + static_cast<Object *>(that)->~Object(); +} + +void Object::put(ExecutionContext *ctx, const QString &name, const Value &value) +{ + put(ctx->engine->newString(name), value); +} + +Value Object::getValue(const Value &thisObject, const Property *p, PropertyAttributes attrs) +{ + if (!attrs.isAccessor()) + return p->value; + FunctionObject *getter = p->getter(); + if (!getter) + return Value::undefinedValue(); + + return getter->call(thisObject, 0, 0); +} + +void Object::putValue(Property *pd, PropertyAttributes attrs, const Value &value) +{ + if (attrs.isAccessor()) { + if (pd->set) { + Value args[1]; + args[0] = value; + pd->set->call(Value::fromObject(this), args, 1); + return; + } + goto reject; + } + + if (!attrs.isWritable()) + goto reject; + + pd->value = value; + return; + + reject: + if (engine()->current->strictMode) + engine()->current->throwTypeError(); + +} + +void Object::inplaceBinOp(ExecutionContext *ctx, BinOp op, String *name, const Value &rhs) +{ + Value v = get(name); + Value result; + op(ctx, &result, v, rhs); + put(name, result); +} + +void Object::inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value &index, const Value &rhs) +{ + uint idx = index.asArrayIndex(); + if (idx < UINT_MAX) { + bool hasProperty = false; + Value v = getIndexed(idx, &hasProperty); + Value result; + op(ctx, &result, v, rhs); + putIndexed(idx, result); + return; + } + String *name = index.toString(ctx); + assert(name); + inplaceBinOp(ctx, op, name, rhs); +} + +void Object::defineDefaultProperty(String *name, Value value) +{ + Property *pd = insertMember(name, Attr_Data|Attr_NotEnumerable); + pd->value = value; +} + +void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value value) +{ + defineDefaultProperty(context->engine->newIdentifier(name), value); +} + +void Object::defineDefaultProperty(ExecutionEngine *engine, const QString &name, Value value) +{ + defineDefaultProperty(engine->newIdentifier(name), value); +} + +void Object::defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(SimpleCallContext *), int argumentCount) +{ + Q_UNUSED(argumentCount); + String *s = context->engine->newIdentifier(name); + FunctionObject* function = context->engine->newBuiltinFunction(context, s, code); + function->defineReadonlyProperty(context->engine->id_length, Value::fromInt32(argumentCount)); + defineDefaultProperty(s, Value::fromObject(function)); +} + +void Object::defineDefaultProperty(ExecutionEngine *engine, const QString &name, Value (*code)(SimpleCallContext *), int argumentCount) +{ + Q_UNUSED(argumentCount); + String *s = engine->newIdentifier(name); + FunctionObject* function = engine->newBuiltinFunction(engine->rootContext, s, code); + function->defineReadonlyProperty(engine->id_length, Value::fromInt32(argumentCount)); + defineDefaultProperty(s, Value::fromObject(function)); +} + +void Object::defineAccessorProperty(ExecutionEngine *engine, const QString &name, + Value (*getter)(SimpleCallContext *), Value (*setter)(SimpleCallContext *)) +{ + String *s = engine->newString(name); + defineAccessorProperty(s, getter, setter); +} + +void Object::defineAccessorProperty(String *name, Value (*getter)(SimpleCallContext *), Value (*setter)(SimpleCallContext *)) +{ + ExecutionEngine *v4 = engine(); + Property *p = insertMember(name, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); + if (getter) + p->setGetter(v4->newBuiltinFunction(v4->rootContext, name, getter)); + if (setter) + p->setSetter(v4->newBuiltinFunction(v4->rootContext, name, setter)); +} + +void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value) +{ + defineReadonlyProperty(engine->newIdentifier(name), value); +} + +void Object::defineReadonlyProperty(String *name, Value value) +{ + Property *pd = insertMember(name, Attr_ReadOnly); + pd->value = value; +} + +void Object::markObjects(Managed *that) +{ + Object *o = static_cast<Object *>(that); + if (o->prototype) + o->prototype->mark(); + + for (int i = 0; i < o->internalClass->size; ++i) { + const Property &pd = o->memberData[i]; + if (o->internalClass->propertyData[i].isData()) { + if (Managed *m = pd.value.asManaged()) + m->mark(); + } else { + if (pd.getter()) + pd.getter()->mark(); + if (pd.setter()) + pd.setter()->mark(); + } + } + o->markArrayObjects(); +} + +Property *Object::insertMember(String *s, PropertyAttributes attributes) +{ + uint idx; + internalClass = internalClass->addMember(s, attributes, &idx); + + if (idx >= memberDataAlloc) { + memberDataAlloc = qMax((uint)8, 2*memberDataAlloc); + Property *newMemberData = new Property[memberDataAlloc]; + memcpy(newMemberData, memberData, sizeof(Property)*idx); + memset(newMemberData + idx, 0, sizeof(Property)*(memberDataAlloc - idx)); + if (memberData != inlineProperties) + delete [] memberData; + memberData = newMemberData; + } + return memberData + idx; +} + +// Section 8.12.1 +Property *Object::__getOwnProperty__(String *name, PropertyAttributes *attrs) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return __getOwnProperty__(idx, attrs); + + uint member = internalClass->find(name); + if (member < UINT_MAX) { + if (attrs) + *attrs = internalClass->propertyData[member]; + return memberData + member; + } + + if (attrs) + *attrs = Attr_Invalid; + return 0; +} + +Property *Object::__getOwnProperty__(uint index, PropertyAttributes *attrs) +{ + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + Property *p = arrayData + pidx; + if (!arrayAttributes || arrayAttributes[pidx].isData()) { + if (attrs) + *attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + return p; + } else if (arrayAttributes[pidx].isAccessor()) { + if (attrs) + *attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Accessor); + return p; + } + } + if (isStringObject()) { + if (attrs) + *attrs = Attr_NotConfigurable|Attr_NotWritable; + return static_cast<StringObject *>(this)->getIndex(index); + } + + if (attrs) + *attrs = Attr_Invalid; + return 0; +} + +// Section 8.12.2 +Property *Object::__getPropertyDescriptor__(String *name, PropertyAttributes *attrs) const +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return __getPropertyDescriptor__(idx); + + + const Object *o = this; + while (o) { + uint idx = o->internalClass->find(name); + if (idx < UINT_MAX) { + if (attrs) + *attrs = o->internalClass->propertyData[idx]; + return o->memberData + idx; + } + + o = o->prototype; + } + if (attrs) + *attrs = Attr_Invalid; + return 0; +} + +Property *Object::__getPropertyDescriptor__(uint index, PropertyAttributes *attrs) const +{ + const Object *o = this; + while (o) { + uint pidx = o->propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + Property *p = o->arrayData + pidx; + if (!o->arrayAttributes || !o->arrayAttributes[pidx].isGeneric()) { + if (attrs) + *attrs = o->arrayAttributes ? o->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + return p; + } + } + if (o->isStringObject()) { + Property *p = static_cast<const StringObject *>(o)->getIndex(index); + if (p) { + if (attrs) + *attrs = (Attr_NotWritable|Attr_NotConfigurable); + return p; + } + } + o = o->prototype; + } + if (attrs) + *attrs = Attr_Invalid; + return 0; +} + +bool Object::__hasProperty__(String *name) const +{ + if (__getPropertyDescriptor__(name)) + return true; + return !query(name).isEmpty(); +} + +Value Object::get(Managed *m, String *name, bool *hasProperty) +{ + return static_cast<Object *>(m)->internalGet(name, hasProperty); +} + +Value Object::getIndexed(Managed *m, uint index, bool *hasProperty) +{ + return static_cast<Object *>(m)->internalGetIndexed(index, hasProperty); +} + +void Object::put(Managed *m, String *name, const Value &value) +{ + static_cast<Object *>(m)->internalPut(name, value); +} + +void Object::putIndexed(Managed *m, uint index, const Value &value) +{ + static_cast<Object *>(m)->internalPutIndexed(index, value); +} + +PropertyAttributes Object::query(const Managed *m, String *name) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return queryIndexed(m, idx); + + const Object *o = static_cast<const Object *>(m); + while (o) { + uint idx = o->internalClass->find(name); + if (idx < UINT_MAX) + return o->internalClass->propertyData[idx]; + + o = o->prototype; + } + return Attr_Invalid; +} + +PropertyAttributes Object::queryIndexed(const Managed *m, uint index) +{ + const Object *o = static_cast<const Object *>(m); + while (o) { + uint pidx = o->propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + if (o->arrayAttributes) + return o->arrayAttributes[pidx]; + return Attr_Data; + } + if (o->isStringObject()) { + Property *p = static_cast<const StringObject *>(o)->getIndex(index); + if (p) + return Attr_Data; + } + o = o->prototype; + } + return Attr_Invalid; +} + +bool Object::deleteProperty(Managed *m, String *name) +{ + return static_cast<Object *>(m)->internalDeleteProperty(name); +} + +bool Object::deleteIndexedProperty(Managed *m, uint index) +{ + return static_cast<Object *>(m)->internalDeleteIndexedProperty(index); +} + +void Object::getLookup(Managed *m, Lookup *l, Value *result) +{ + Object *o = static_cast<Object *>(m); + PropertyAttributes attrs; + Property *p = l->lookup(o, &attrs); + if (p) { + if (attrs.isData()) { + if (l->level == 0) + l->getter = Lookup::getter0; + else if (l->level == 1) + l->getter = Lookup::getter1; + else if (l->level == 2) + l->getter = Lookup::getter2; + if (result) + *result = p->value; + return; + } else { + if (l->level == 0) + l->getter = Lookup::getterAccessor0; + else if (l->level == 1) + l->getter = Lookup::getterAccessor1; + else if (l->level == 2) + l->getter = Lookup::getterAccessor2; + if (result) + *result = p->value; + Value res = o->getValue(p, attrs); + if (result) + *result = res; + return; + } + } else if (result) { + *result = Value::undefinedValue(); + } +} + +void Object::setLookup(Managed *m, Lookup *l, const Value &value) +{ + Object *o = static_cast<Object *>(m); + + uint idx = o->internalClass->find(l->name); + if (!o->isArrayObject() || idx != ArrayObject::LengthPropertyIndex) { + if (idx != UINT_MAX && o->internalClass->propertyData[idx].isData() && o->internalClass->propertyData[idx].isWritable()) { + l->classList[0] = o->internalClass; + l->index = idx; + l->setter = Lookup::setter0; + o->memberData[idx].value = value; + return; + } + + if (idx != UINT_MAX) { + o->putValue(o->memberData + idx, o->internalClass->propertyData[idx], value); + return; + } + } + + o->put(l->name, value); +} + +Property *Object::advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs) +{ + Object *o = static_cast<Object *>(m); + *name = 0; + *index = UINT_MAX; + + if (!it->arrayIndex) + it->arrayNode = o->sparseArrayBegin(); + + // sparse arrays + if (it->arrayNode) { + while (it->arrayNode != o->sparseArrayEnd()) { + int k = it->arrayNode->key(); + uint pidx = it->arrayNode->value; + Property *p = o->arrayData + pidx; + it->arrayNode = it->arrayNode->nextNode(); + PropertyAttributes a = o->arrayAttributes ? o->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { + it->arrayIndex = k + 1; + *index = k; + if (attrs) + *attrs = a; + return p; + } + } + it->arrayNode = 0; + it->arrayIndex = UINT_MAX; + } + // dense arrays + while (it->arrayIndex < o->arrayDataLen) { + uint pidx = o->propertyIndexFromArrayIndex(it->arrayIndex); + Property *p = o->arrayData + pidx; + PropertyAttributes a = o->arrayAttributes ? o->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + ++it->arrayIndex; + if ((!o->arrayAttributes || !o->arrayAttributes[pidx].isGeneric()) + && (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable())) { + *index = it->arrayIndex - 1; + if (attrs) + *attrs = a; + return p; + } + } + + while (it->memberIndex < o->internalClass->size) { + String *n = o->internalClass->nameMap.at(it->memberIndex); + assert(n); + + Property *p = o->memberData + it->memberIndex; + PropertyAttributes a = o->internalClass->propertyData[it->memberIndex]; + ++it->memberIndex; + if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { + *name = n; + if (attrs) + *attrs = a; + return p; + } + } + + return 0; +} + +// Section 8.12.3 +Value Object::internalGet(String *name, bool *hasProperty) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return getIndexed(idx, hasProperty); + + name->makeIdentifier(); + + Object *o = this; + while (o) { + uint idx = o->internalClass->find(name); + if (idx < UINT_MAX) { + if (hasProperty) + *hasProperty = true; + return getValue(o->memberData + idx, o->internalClass->propertyData.at(idx)); + } + + o = o->prototype; + } + + if (hasProperty) + *hasProperty = false; + return Value::undefinedValue(); +} + +Value Object::internalGetIndexed(uint index, bool *hasProperty) +{ + Property *pd = 0; + PropertyAttributes attrs = Attr_Data; + Object *o = this; + while (o) { + uint pidx = o->propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + if (!o->arrayAttributes || !o->arrayAttributes[pidx].isGeneric()) { + pd = o->arrayData + pidx; + if (o->arrayAttributes) + attrs = o->arrayAttributes[pidx]; + break; + } + } + if (o->isStringObject()) { + pd = static_cast<StringObject *>(o)->getIndex(index); + if (pd) { + attrs = (Attr_NotWritable|Attr_NotConfigurable); + break; + } + } + o = o->prototype; + } + + if (pd) { + if (hasProperty) + *hasProperty = true; + return getValue(pd, attrs); + } + + if (hasProperty) + *hasProperty = false; + return Value::undefinedValue(); +} + + +// Section 8.12.5 +void Object::internalPut(String *name, const Value &value) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return putIndexed(idx, value); + + name->makeIdentifier(); + + uint member = internalClass->find(name); + Property *pd = 0; + PropertyAttributes attrs; + if (member < UINT_MAX) { + pd = memberData + member; + attrs = internalClass->propertyData[member]; + } + + // clause 1 + if (pd) { + if (attrs.isAccessor()) { + if (pd->setter()) + goto cont; + goto reject; + } else if (!attrs.isWritable()) + goto reject; + else if (isArrayObject() && name->isEqualTo(engine()->id_length)) { + bool ok; + uint l = value.asArrayLength(&ok); + if (!ok) + engine()->current->throwRangeError(value); + ok = setArrayLength(l); + if (!ok) + goto reject; + } else { + pd->value = value; + } + return; + } else if (!prototype) { + if (!extensible) + goto reject; + } else { + // clause 4 + if ((pd = prototype->__getPropertyDescriptor__(name, &attrs))) { + if (attrs.isAccessor()) { + if (!pd->setter()) + goto reject; + } else if (!extensible || !attrs.isWritable()) { + goto reject; + } + } else if (!extensible) { + goto reject; + } + } + + cont: + + // Clause 5 + if (pd && attrs.isAccessor()) { + assert(pd->setter() != 0); + + Value args[1]; + args[0] = value; + pd->setter()->call(Value::fromObject(this), args, 1); + return; + } + + { + Property *p = insertMember(name, Attr_Data); + p->value = value; + return; + } + + reject: + if (engine()->current->strictMode) { + QString message = QStringLiteral("Cannot assign to read-only property \""); + message += name->toQString(); + message += QLatin1Char('\"'); + engine()->current->throwTypeError(message); + } +} + +void Object::internalPutIndexed(uint index, const Value &value) +{ + Property *pd = 0; + PropertyAttributes attrs; + + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + if (arrayAttributes && arrayAttributes[pidx].isGeneric()) { + pidx = UINT_MAX; + } else { + pd = arrayData + pidx; + attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + } + } + + if (!pd && isStringObject()) { + pd = static_cast<StringObject *>(this)->getIndex(index); + if (pd) + // not writable + goto reject; + } + + // clause 1 + if (pd) { + if (attrs.isAccessor()) { + if (pd->setter()) + goto cont; + goto reject; + } else if (!attrs.isWritable()) + goto reject; + else + pd->value = value; + return; + } else if (!prototype) { + if (!extensible) + goto reject; + } else { + // clause 4 + if ((pd = prototype->__getPropertyDescriptor__(index, &attrs))) { + if (attrs.isAccessor()) { + if (!pd->setter()) + goto reject; + } else if (!extensible || !attrs.isWritable()) { + goto reject; + } + } else if (!extensible) { + goto reject; + } + } + + cont: + + // Clause 5 + if (pd && attrs.isAccessor()) { + assert(pd->setter() != 0); + + Value args[1]; + args[0] = value; + pd->setter()->call(Value::fromObject(this), args, 1); + return; + } + + arraySet(index, value); + return; + + reject: + if (engine()->current->strictMode) + engine()->current->throwTypeError(); +} + +// Section 8.12.7 +bool Object::internalDeleteProperty(String *name) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return deleteIndexedProperty(idx); + + name->makeIdentifier(); + + uint memberIdx = internalClass->find(name); + if (memberIdx != UINT_MAX) { + if (internalClass->propertyData[memberIdx].isConfigurable()) { + internalClass->removeMember(this, name->identifier); + memmove(memberData + memberIdx, memberData + memberIdx + 1, (internalClass->size - memberIdx)*sizeof(Property)); + return true; + } + if (engine()->current->strictMode) + engine()->current->throwTypeError(); + return false; + } + + return true; +} + +bool Object::internalDeleteIndexedProperty(uint index) +{ + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx == UINT_MAX) + return true; + if (arrayAttributes && arrayAttributes[pidx].isGeneric()) + return true; + + if (!arrayAttributes || arrayAttributes[pidx].isConfigurable()) { + arrayData[pidx].value = Value::undefinedValue(); + if (!arrayAttributes) + ensureArrayAttributes(); + arrayAttributes[pidx].clear(); + if (sparseArray) { + arrayData[pidx].value.int_32 = arrayFreeList; + arrayFreeList = pidx; + } + return true; + } + + if (engine()->current->strictMode) + engine()->current->throwTypeError(); + return false; +} + +// Section 8.12.9 +bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Property &p, PropertyAttributes attrs) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return __defineOwnProperty__(ctx, idx, p, attrs); + + name->makeIdentifier(); + + Property *current; + PropertyAttributes *cattrs; + + if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { + assert(ArrayObject::LengthPropertyIndex == internalClass->find(ctx->engine->id_length)); + Property *lp = memberData + ArrayObject::LengthPropertyIndex; + cattrs = internalClass->propertyData.data() + ArrayObject::LengthPropertyIndex; + if (attrs.isEmpty() || p.isSubset(attrs, *lp, *cattrs)) + return true; + if (!cattrs->isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable()) + goto reject; + bool succeeded = true; + if (attrs.type() == PropertyAttributes::Data) { + bool ok; + uint l = p.value.asArrayLength(&ok); + if (!ok) + ctx->throwRangeError(p.value); + succeeded = setArrayLength(l); + } + if (attrs.hasWritable() && !attrs.isWritable()) + cattrs->setWritable(false); + if (!succeeded) + goto reject; + return true; + } + + // Clause 1 + { + uint member = internalClass->find(name); + current = (member < UINT_MAX) ? memberData + member : 0; + cattrs = internalClass->propertyData.data() + member; + } + + if (!current) { + // clause 3 + if (!extensible) + goto reject; + // clause 4 + Property *pd = insertMember(name, attrs); + *pd = p; + pd->fullyPopulated(&attrs); + return true; + } + + return __defineOwnProperty__(ctx, current, name, p, attrs); +reject: + if (ctx->strictMode) + ctx->throwTypeError(); + return false; +} + +bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs) +{ + Property *current = 0; + + // 15.4.5.1, 4b + if (isArrayObject() && index >= arrayLength() && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) + goto reject; + + if (isNonStrictArgumentsObject) + return static_cast<ArgumentsObject *>(this)->defineOwnProperty(ctx, index, p, attrs); + + // Clause 1 + { + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX && (!arrayAttributes || !arrayAttributes[pidx].isGeneric())) + current = arrayData + pidx; + if (!current && isStringObject()) + current = static_cast<StringObject *>(this)->getIndex(index); + } + + if (!current) { + // clause 3 + if (!extensible) + goto reject; + // clause 4 + Property *pd = arrayInsert(index, attrs); + *pd = p; + pd->fullyPopulated(&attrs); + return true; + } + + return __defineOwnProperty__(ctx, current, 0 /*member*/, p, attrs); +reject: + if (ctx->strictMode) + ctx->throwTypeError(); + return false; +} + +bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, String *member, const Property &p, PropertyAttributes attrs) +{ + // clause 5 + if (attrs.isEmpty()) + return true; + + PropertyAttributes cattrs = Attr_Data; + if (member) + cattrs = internalClass->propertyData[current - memberData]; + else if (arrayAttributes) + cattrs = arrayAttributes[current - arrayData]; + + // clause 6 + if (p.isSubset(attrs, *current, cattrs)) + return true; + + // clause 7 + if (!cattrs.isConfigurable()) { + if (attrs.isConfigurable()) + goto reject; + if (attrs.hasEnumerable() && attrs.isEnumerable() != cattrs.isEnumerable()) + goto reject; + } + + // clause 8 + if (attrs.isGeneric()) + goto accept; + + // clause 9 + if (cattrs.isData() != attrs.isData()) { + // 9a + if (!cattrs.isConfigurable()) + goto reject; + if (cattrs.isData()) { + // 9b + cattrs.setType(PropertyAttributes::Accessor); + cattrs.clearWritable(); + current->setGetter(0); + current->setSetter(0); + } else { + // 9c + cattrs.setType(PropertyAttributes::Data); + cattrs.setWritable(false); + current->value = Value::undefinedValue(); + } + } else if (cattrs.isData() && attrs.isData()) { // clause 10 + if (!cattrs.isConfigurable() && !cattrs.isWritable()) { + if (attrs.isWritable() || !current->value.sameValue(p.value)) + goto reject; + } + } else { // clause 10 + assert(cattrs.isAccessor() && attrs.isAccessor()); + if (!cattrs.isConfigurable()) { + if (p.getter() && !(current->getter() == p.getter() || (!current->getter() && (quintptr)p.getter() == 0x1))) + goto reject; + if (p.setter() && !(current->setter() == p.setter() || (!current->setter() && (quintptr)p.setter() == 0x1))) + goto reject; + } + } + + accept: + + current->merge(cattrs, p, attrs); + if (member) { + internalClass = internalClass->changeMember(member, cattrs); + } else { + if (cattrs != Attr_Data) + ensureArrayAttributes(); + if (arrayAttributes) + arrayAttributes[current - arrayData] = cattrs; + } + return true; + reject: + if (ctx->strictMode) + ctx->throwTypeError(); + return false; +} + + +bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, const Property &p, PropertyAttributes attrs) +{ + return __defineOwnProperty__(ctx, ctx->engine->newString(name), p, attrs); +} + + +void Object::copyArrayData(Object *other) +{ + arrayReserve(other->arrayDataLen); + arrayDataLen = other->arrayDataLen; + memcpy(arrayData, other->arrayData, arrayDataLen*sizeof(Property)); + arrayOffset = 0; + if (other->sparseArray) { + sparseArray = new SparseArray(*other->sparseArray); + arrayFreeList = other->arrayFreeList; + } + if (isArrayObject()) + setArrayLengthUnchecked(other->arrayLength()); +} + + +Value Object::arrayIndexOf(Value v, uint fromIndex, uint endIndex, ExecutionContext *ctx, Object *o) +{ + bool protoHasArray = false; + Object *p = o; + while ((p = p->prototype)) + if (p->arrayDataLen) + protoHasArray = true; + + if (protoHasArray || o->arrayAttributes) { + // lets be safe and slow + for (uint i = fromIndex; i < endIndex; ++i) { + bool exists; + Value value = o->getIndexed(i, &exists); + if (exists && __qmljs_strict_equal(value, v)) + return Value::fromDouble(i); + } + } else if (sparseArray) { + for (SparseArrayNode *n = sparseArray->lowerBound(fromIndex); n != sparseArray->end() && n->key() < endIndex; n = n->nextNode()) { + Value value = o->getValue(arrayData + n->value, arrayAttributes ? arrayAttributes[n->value] : Attr_Data); + if (__qmljs_strict_equal(value, v)) + return Value::fromDouble(n->key()); + } + } else { + if ((int) endIndex > arrayDataLen) + endIndex = arrayDataLen; + Property *pd = arrayData; + Property *end = pd + endIndex; + pd += fromIndex; + while (pd < end) { + if (!arrayAttributes || !arrayAttributes[pd - arrayData].isGeneric()) { + Value value = o->getValue(pd, arrayAttributes ? arrayAttributes[pd - arrayData] : Attr_Data); + if (__qmljs_strict_equal(value, v)) + return Value::fromDouble(pd - arrayData); + } + ++pd; + } + } + return Value::fromInt32(-1); +} + +void Object::arrayConcat(const ArrayObject *other) +{ + int newLen = arrayDataLen + other->arrayLength(); + if (other->sparseArray) + initSparse(); + // ### copy attributes as well! + if (sparseArray) { + if (other->sparseArray) { + for (const SparseArrayNode *it = other->sparseArray->begin(); it != other->sparseArray->end(); it = it->nextNode()) + arraySet(arrayDataLen + it->key(), other->arrayData + it->value); + } else { + int oldSize = arrayDataLen; + arrayReserve(oldSize + other->arrayLength()); + memcpy(arrayData + oldSize, other->arrayData, other->arrayLength()*sizeof(Property)); + if (arrayAttributes) + std::fill(arrayAttributes + oldSize, arrayAttributes + oldSize + other->arrayLength(), PropertyAttributes(Attr_Data)); + for (uint i = 0; i < other->arrayLength(); ++i) { + SparseArrayNode *n = sparseArray->insert(arrayDataLen + i); + n->value = oldSize + i; + } + } + } else { + int oldSize = arrayLength(); + arrayReserve(oldSize + other->arrayDataLen); + if (oldSize > arrayDataLen) { + ensureArrayAttributes(); + std::fill(arrayAttributes + arrayDataLen, arrayAttributes + oldSize, PropertyAttributes()); + } + arrayDataLen = oldSize + other->arrayDataLen; + if (other->arrayAttributes) { + for (int i = 0; i < arrayDataLen; ++i) { + bool exists; + arrayData[oldSize + i].value = const_cast<ArrayObject *>(other)->getIndexed(i, &exists); + if (arrayAttributes) + arrayAttributes[oldSize + i] = Attr_Data; + if (!exists) { + ensureArrayAttributes(); + arrayAttributes[oldSize + i].clear(); + } + } + } else { + memcpy(arrayData + oldSize, other->arrayData, other->arrayDataLen*sizeof(Property)); + if (arrayAttributes) + std::fill(arrayAttributes + oldSize, arrayAttributes + oldSize + other->arrayDataLen, PropertyAttributes(Attr_Data)); + } + } + setArrayLengthUnchecked(newLen); +} + +void Object::arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint len) +{ + if (!arrayDataLen) + return; + + if (sparseArray) { + context->throwUnimplemented("Object::sort unimplemented for sparse arrays"); + return; + } + + if (len > arrayDataLen) + len = arrayDataLen; + + // The spec says the sorting goes through a series of get,put and delete operations. + // this implies that the attributes don't get sorted around. + // behavior of accessor properties is implementation defined. We simply turn them all + // into data properties and then sort. This is in line with the sentence above. + if (arrayAttributes) { + for (uint i = 0; i < len; i++) { + if (arrayAttributes[i].isGeneric()) { + while (--len > i) + if (!arrayAttributes[len].isGeneric()) + break; + arrayData[i].value = getValue(arrayData + len, arrayAttributes[len]); + arrayAttributes[i] = Attr_Data; + arrayAttributes[len].clear(); + } else if (arrayAttributes[i].isAccessor()) { + arrayData[i].value = getValue(arrayData + i, arrayAttributes[i]); + arrayAttributes[i] = Attr_Data; + } + } + } + + ArrayElementLessThan lessThan(context, thisObject, comparefn); + + Property *begin = arrayData; + // We deliberately choose qSort over std::sort here, because with + // MSVC in debug builds, std::sort has an ASSERT() that verifies + // that the return values of lessThan are perfectly consistent + // and aborts otherwise. We do not want JavaScript to easily crash + // the entire application and therefore choose qSort, which doesn't + // have this property. + qSort(begin, begin + len, lessThan); +} + + +void Object::initSparse() +{ + if (!sparseArray) { + sparseArray = new SparseArray; + for (int i = 0; i < arrayDataLen; ++i) { + if (!arrayAttributes || !arrayAttributes[i].isGeneric()) { + SparseArrayNode *n = sparseArray->insert(i); + n->value = i + arrayOffset; + } + } + + uint off = arrayOffset; + if (!arrayOffset) { + arrayFreeList = arrayDataLen; + } else { + arrayFreeList = 0; + arrayData -= off; + arrayAlloc += off; + int o = off; + for (int i = 0; i < o - 1; ++i) { + arrayData[i].value = Value::fromInt32(i + 1); + } + arrayData[o - 1].value = Value::fromInt32(arrayDataLen + off); + } + for (int i = arrayDataLen + off; i < arrayAlloc; ++i) { + arrayData[i].value = Value::fromInt32(i + 1); + } + } +} + +void Object::arrayReserve(uint n) +{ + if (n < 8) + n = 8; + if (n >= arrayAlloc) { + uint off; + if (sparseArray) { + assert(arrayFreeList == arrayAlloc); + // ### FIXME + arrayDataLen = arrayAlloc; + off = 0; + } else { + off = arrayOffset; + } + arrayAlloc = qMax(n, 2*arrayAlloc); + Property *newArrayData = new Property[arrayAlloc]; + if (arrayData) { + memcpy(newArrayData, arrayData, sizeof(Property)*arrayDataLen); + delete [] (arrayData - off); + } + arrayData = newArrayData; + if (sparseArray) { + for (uint i = arrayFreeList; i < arrayAlloc; ++i) { + arrayData[i].value = Value::emptyValue(); + arrayData[i].value = Value::fromInt32(i + 1); + } + } else { + arrayOffset = 0; + } + + if (arrayAttributes) { + PropertyAttributes *newAttrs = new PropertyAttributes[arrayAlloc]; + memcpy(newAttrs, arrayAttributes, sizeof(PropertyAttributes)*arrayDataLen); + delete [] (arrayAttributes - off); + + arrayAttributes = newAttrs; + if (sparseArray) { + for (uint i = arrayFreeList; i < arrayAlloc; ++i) + arrayAttributes[i] = Attr_Invalid; + } + } + } +} + +void Object::ensureArrayAttributes() +{ + if (arrayAttributes) + return; + + arrayAttributes = new PropertyAttributes[arrayAlloc]; + for (uint i = 0; i < arrayDataLen; ++i) + arrayAttributes[i] = Attr_Data; + for (uint i = arrayDataLen; i < arrayAlloc; ++i) + arrayAttributes[i] = Attr_Invalid; +} + + +bool Object::setArrayLength(uint newLen) { + assert(isArrayObject()); + const Property *lengthProperty = memberData + ArrayObject::LengthPropertyIndex; + if (lengthProperty && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) + return false; + uint oldLen = arrayLength(); + bool ok = true; + if (newLen < oldLen) { + if (sparseArray) { + SparseArrayNode *begin = sparseArray->lowerBound(newLen); + if (begin != sparseArray->end()) { + SparseArrayNode *it = sparseArray->end()->previousNode(); + while (1) { + Property &pd = arrayData[it->value]; + if (arrayAttributes) { + if (!arrayAttributes[it->value].isConfigurable()) { + ok = false; + newLen = it->key() + 1; + break; + } else { + arrayAttributes[it->value].clear(); + } + } + pd.value.tag = Value::_Empty_Type; + pd.value.int_32 = arrayFreeList; + arrayFreeList = it->value; + bool brk = (it == begin); + SparseArrayNode *prev = it->previousNode(); + sparseArray->erase(it); + if (brk) + break; + it = prev; + } + } + } else { + Property *it = arrayData + arrayDataLen; + const Property *begin = arrayData + newLen; + while (--it >= begin) { + if (arrayAttributes) { + if (!arrayAttributes[it - arrayData].isEmpty() && !arrayAttributes[it - arrayData].isConfigurable()) { + ok = false; + newLen = it - arrayData + 1; + break; + } else { + arrayAttributes[it - arrayData].clear(); + } + it->value = Value::emptyValue(); + } + } + arrayDataLen = newLen; + } + } else { + if (newLen >= 0x100000) + initSparse(); + } + setArrayLengthUnchecked(newLen); + return ok; +} + +void Object::markArrayObjects() const +{ + for (uint i = 0; i < arrayDataLen; ++i) { + const Property &pd = arrayData[i]; + if (!arrayAttributes || arrayAttributes[i].isData()) { + if (Managed *m = pd.value.asManaged()) + m->mark(); + } else if (arrayAttributes[i].isAccessor()) { + if (pd.getter()) + pd.getter()->mark(); + if (pd.setter()) + pd.setter()->mark(); + } + } +} + +ArrayObject::ArrayObject(ExecutionEngine *engine, const QStringList &list) + : Object(engine) +{ + init(engine); + + // Converts a QStringList to JS. + // The result is a new Array object with length equal to the length + // of the QStringList, and the elements being the QStringList's + // elements converted to JS Strings. + int len = list.count(); + arrayReserve(len); + for (int ii = 0; ii < len; ++ii) + arrayData[ii].value = Value::fromString(engine->newString(list.at(ii))); + setArrayLengthUnchecked(len); +} + +void ArrayObject::init(ExecutionEngine *engine) +{ + type = Type_ArrayObject; + internalClass = engine->arrayClass; + + memberData = new Property[4]; + memberData[LengthPropertyIndex].value = Value::fromInt32(0); +} + +QStringList ArrayObject::toQStringList() const +{ + QStringList result; + + QV4::ExecutionEngine *engine = internalClass->engine; + + uint32_t length = arrayLength(); + for (uint32_t i = 0; i < length; ++i) + result.append(const_cast<ArrayObject *>(this)->getIndexed(i).toString(engine->current)->toQString()); + return result; +} + + +DEFINE_MANAGED_VTABLE(ForEachIteratorObject); + +void ForEachIteratorObject::markObjects(Managed *that) +{ + ForEachIteratorObject *o = static_cast<ForEachIteratorObject *>(that); + Object::markObjects(that); + if (o->it.object) + o->it.object->mark(); +} diff --git a/src/qml/qml/v4/qv4object_p.h b/src/qml/qml/v4/qv4object_p.h new file mode 100644 index 0000000000..a6f1c04abd --- /dev/null +++ b/src/qml/qml/v4/qv4object_p.h @@ -0,0 +1,432 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_OBJECTS_H +#define QMLJS_OBJECTS_H + +#include "qv4global_p.h" +#include "qv4runtime_p.h" +#include "qv4engine_p.h" +#include "qv4context_p.h" +#include "qv4sparsearray_p.h" +#include "qv4string_p.h" +#include "qv4codegen_p.h" +#include "qv4isel_p.h" +#include "qv4managed_p.h" +#include "qv4property_p.h" +#include "qv4internalclass_p.h" +#include "qv4objectiterator_p.h" + +#include <QtCore/QString> +#include <QtCore/QHash> +#include <QtCore/QScopedPointer> +#include <cstdio> +#include <cassert> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Function; +struct Lookup; +struct Object; +struct ObjectIterator; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct ArrayObject; +struct DateObject; +struct FunctionObject; +struct RegExpObject; +struct ErrorObject; +struct ArgumentsObject; +struct ExecutionContext; +struct CallContext; +struct ExecutionEngine; +class MemoryManager; + +struct ObjectPrototype; +struct StringPrototype; +struct NumberPrototype; +struct BooleanPrototype; +struct ArrayPrototype; +struct FunctionPrototype; +struct DatePrototype; +struct RegExpPrototype; +struct ErrorPrototype; +struct EvalErrorPrototype; +struct RangeErrorPrototype; +struct ReferenceErrorPrototype; +struct SyntaxErrorPrototype; +struct TypeErrorPrototype; +struct URIErrorPrototype; + +typedef Value (*PropertyEnumeratorFunction)(Object *object); +typedef PropertyAttributes (*PropertyQueryFunction)(const Object *object, String *name); + +struct Q_QML_EXPORT Object: Managed { + Object *prototype; + uint memberDataAlloc; + Property *memberData; + + union { + uint arrayFreeList; + uint arrayOffset; + }; + uint arrayDataLen; + uint arrayAlloc; + PropertyAttributes *arrayAttributes; + Property *arrayData; + SparseArray *sparseArray; + + enum { + InlinePropertySize = 4 + }; + Property inlineProperties[InlinePropertySize]; + + Object(ExecutionEngine *engine); + Object(ExecutionContext *context); + Object(ExecutionEngine *engine, InternalClass *internalClass); + ~Object(); + + Property *__getOwnProperty__(String *name, PropertyAttributes *attrs = 0); + Property *__getOwnProperty__(uint index, PropertyAttributes *attrs = 0); + + Property *__getPropertyDescriptor__(String *name, PropertyAttributes *attrs = 0) const; + Property *__getPropertyDescriptor__(uint index, PropertyAttributes *attrs = 0) const; + + bool __hasProperty__(String *name) const; + bool __hasProperty__(uint index) const { + return __getPropertyDescriptor__(index); + } + + bool __defineOwnProperty__(ExecutionContext *ctx, Property *current, String *member, const Property &p, PropertyAttributes attrs); + bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const Property &p, PropertyAttributes attrs); + bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs); + bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const Property &p, PropertyAttributes attrs); + + // + // helpers + // + void put(ExecutionContext *ctx, const QString &name, const Value &value); + + static Value getValue(const Value &thisObject, const Property *p, PropertyAttributes attrs); + Value getValue(const Property *p, PropertyAttributes attrs) const { + return getValue(Value::fromObject(const_cast<Object *>(this)), p, attrs); + } + + void putValue(Property *pd, PropertyAttributes attrs, const Value &value); + + void inplaceBinOp(ExecutionContext *ctx, BinOp op, String *name, const Value &rhs); + void inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value &index, const Value &rhs); + + /* The spec default: Writable: true, Enumerable: false, Configurable: true */ + void defineDefaultProperty(String *name, Value value); + void defineDefaultProperty(ExecutionContext *context, const QString &name, Value value); + void defineDefaultProperty(ExecutionEngine *engine, const QString &name, Value value); + void defineDefaultProperty(ExecutionContext *context, const QString &name, Value (*code)(SimpleCallContext *), int count = 0); + void defineDefaultProperty(ExecutionEngine *engine, const QString &name, Value (*code)(SimpleCallContext *), int count = 0); + void defineAccessorProperty(ExecutionEngine *engine, const QString &name, Value (*getter)(SimpleCallContext *), Value (*setter)(SimpleCallContext *)); + void defineAccessorProperty(String *name, Value (*getter)(SimpleCallContext *), Value (*setter)(SimpleCallContext *)); + /* Fixed: Writable: false, Enumerable: false, Configurable: false */ + void defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value); + void defineReadonlyProperty(String *name, Value value); + + Property *insertMember(String *s, PropertyAttributes attributes); + + inline ExecutionEngine *engine() const { return internalClass->engine; } + + // Array handling + + uint allocArrayValue() { + uint idx = arrayFreeList; + if (arrayAlloc <= arrayFreeList) + arrayReserve(arrayAlloc + 1); + arrayFreeList = arrayData[arrayFreeList].value.uint_32; + if (arrayAttributes) + arrayAttributes[idx].setType(PropertyAttributes::Data); + return idx; + } + + uint allocArrayValue(Value v) { + uint idx = allocArrayValue(); + Property *pd = &arrayData[idx]; + pd->value = v; + return idx; + } + void freeArrayValue(int idx) { + Property &pd = arrayData[idx]; + pd.value.tag = Value::_Empty_Type; + pd.value.int_32 = arrayFreeList; + arrayFreeList = idx; + if (arrayAttributes) + arrayAttributes[idx].clear(); + } + + void getArrayHeadRoom() { + assert(!sparseArray && !arrayOffset); + arrayOffset = qMax(arrayDataLen >> 2, (uint)16); + Property *newArray = new Property[arrayOffset + arrayAlloc]; + memcpy(newArray + arrayOffset, arrayData, arrayDataLen*sizeof(Property)); + delete [] arrayData; + arrayData = newArray + arrayOffset; + if (arrayAttributes) { + PropertyAttributes *newAttrs = new PropertyAttributes[arrayOffset + arrayAlloc]; + memcpy(newAttrs + arrayOffset, arrayAttributes, arrayDataLen*sizeof(PropertyAttributes)); + delete [] arrayAttributes; + arrayAttributes = newAttrs + arrayOffset; + } + } + +public: + void copyArrayData(Object *other); + void initSparse(); + + uint arrayLength() const; + bool setArrayLength(uint newLen); + + void setArrayLengthUnchecked(uint l); + + Property *arrayInsert(uint index, PropertyAttributes attributes = Attr_Data) { + + Property *pd; + if (!sparseArray && (index < 0x1000 || index < arrayDataLen + (arrayDataLen >> 2))) { + if (index >= arrayAlloc) + arrayReserve(index + 1); + if (index >= arrayDataLen) { + ensureArrayAttributes(); + for (uint i = arrayDataLen; i < index; ++i) + arrayAttributes[i].clear(); + arrayDataLen = index + 1; + } + pd = arrayData + index; + } else { + initSparse(); + SparseArrayNode *n = sparseArray->insert(index); + if (n->value == UINT_MAX) + n->value = allocArrayValue(); + pd = arrayData + n->value; + } + if (index >= arrayLength()) + setArrayLengthUnchecked(index + 1); + if (arrayAttributes || attributes != Attr_Data) { + if (!arrayAttributes) + ensureArrayAttributes(); + attributes.resolve(); + arrayAttributes[pd - arrayData] = attributes; + } + return pd; + } + + void arraySet(uint index, const Property *pd) { + *arrayInsert(index) = *pd; + } + + void arraySet(uint index, Value value) { + Property *pd = arrayInsert(index); + pd->value = value; + } + + uint propertyIndexFromArrayIndex(uint index) const + { + if (!sparseArray) { + if (index >= arrayDataLen) + return UINT_MAX; + return index; + } else { + SparseArrayNode *n = sparseArray->findNode(index); + if (!n) + return UINT_MAX; + return n->value; + } + } + + Property *arrayAt(uint index) const { + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx == UINT_MAX) + return 0; + return arrayData + pidx; + } + + Property *nonSparseArrayAt(uint index) const { + if (sparseArray) + return 0; + if (index >= arrayDataLen) + return 0; + return arrayData + index; + } + + void markArrayObjects() const; + + void push_back(Value v) { + uint idx = arrayLength(); + if (!sparseArray) { + if (idx >= arrayAlloc) + arrayReserve(idx + 1); + arrayData[idx].value = v; + arrayDataLen = idx + 1; + } else { + uint idx = allocArrayValue(v); + sparseArray->push_back(idx, arrayLength()); + } + setArrayLengthUnchecked(idx + 1); + } + + SparseArrayNode *sparseArrayBegin() { return sparseArray ? sparseArray->begin() : 0; } + SparseArrayNode *sparseArrayEnd() { return sparseArray ? sparseArray->end() : 0; } + + void arrayConcat(const ArrayObject *other); + void arraySort(ExecutionContext *context, Object *thisObject, const Value &comparefn, uint arrayDataLen); + Value arrayIndexOf(Value v, uint fromIndex, uint arrayDataLen, ExecutionContext *ctx, Object *o); + + void arrayReserve(uint n); + void ensureArrayAttributes(); + + inline Value get(String *name, bool *hasProperty = 0) + { return vtbl->get(this, name, hasProperty); } + inline Value getIndexed(uint idx, bool *hasProperty = 0) + { return vtbl->getIndexed(this, idx, hasProperty); } + inline void put(String *name, const Value &v) + { vtbl->put(this, name, v); } + inline void putIndexed(uint idx, const Value &v) + { vtbl->putIndexed(this, idx, v); } + using Managed::get; + using Managed::getIndexed; + using Managed::put; + using Managed::putIndexed; + using Managed::query; + using Managed::queryIndexed; + using Managed::deleteProperty; + using Managed::deleteIndexedProperty; + using Managed::getLookup; + using Managed::setLookup; + using Managed::advanceIterator; +protected: + static const ManagedVTable static_vtbl; + static void destroy(Managed *that); + static void markObjects(Managed *that); + static Value get(Managed *m, String *name, bool *hasProperty); + static Value getIndexed(Managed *m, uint index, bool *hasProperty); + static void put(Managed *m, String *name, const Value &value); + static void putIndexed(Managed *m, uint index, const Value &value); + static PropertyAttributes query(const Managed *m, String *name); + static PropertyAttributes queryIndexed(const Managed *m, uint index); + static bool deleteProperty(Managed *m, String *name); + static bool deleteIndexedProperty(Managed *m, uint index); + static void getLookup(Managed *m, Lookup *l, Value *result); + static void setLookup(Managed *m, Lookup *l, const Value &v); + static Property *advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes); + + +private: + Value internalGet(String *name, bool *hasProperty); + Value internalGetIndexed(uint index, bool *hasProperty); + void internalPut(String *name, const Value &value); + void internalPutIndexed(uint index, const Value &value); + bool internalDeleteProperty(String *name); + bool internalDeleteIndexedProperty(uint index); + + friend struct ObjectIterator; + friend struct ObjectPrototype; +}; + +struct ForEachIteratorObject: Object { + Q_MANAGED + ObjectIterator it; + ForEachIteratorObject(ExecutionContext *ctx, Object *o) + : Object(ctx->engine), it(o, ObjectIterator::EnumerableOnly|ObjectIterator::WithProtoChain) { + vtbl = &static_vtbl; + type = Type_ForeachIteratorObject; + } + + Value nextPropertyName() { return it.nextPropertyNameAsString(); } + +protected: + static void markObjects(Managed *that); +}; + +struct BooleanObject: Object { + Value value; + BooleanObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_BooleanObject; } +}; + +struct NumberObject: Object { + Value value; + NumberObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_NumberObject; } +}; + +struct ArrayObject: Object { + enum { + LengthPropertyIndex = 0 + }; + + ArrayObject(ExecutionEngine *engine) : Object(engine) { init(engine); } + ArrayObject(ExecutionEngine *engine, const QStringList &list); + void init(ExecutionEngine *engine); + + QStringList toQStringList() const; +}; + +inline uint Object::arrayLength() const +{ + if (isArrayObject()) { + // length is always the first property of an array + Value v = memberData[ArrayObject::LengthPropertyIndex].value; + if (v.isInteger()) + return v.integerValue(); + return Value::toUInt32(v.doubleValue()); + } + return 0; +} + +inline void Object::setArrayLengthUnchecked(uint l) +{ + if (isArrayObject()) { + // length is always the first property of an array + Property &lengthProperty = memberData[ArrayObject::LengthPropertyIndex]; + lengthProperty.value = Value::fromUInt32(l); + } +} + +} + +QT_END_NAMESPACE + +#endif // QMLJS_OBJECTS_H diff --git a/src/qml/qml/v4/qv4objectiterator.cpp b/src/qml/qml/v4/qv4objectiterator.cpp new file mode 100644 index 0000000000..a89bfdb797 --- /dev/null +++ b/src/qml/qml/v4/qv4objectiterator.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qv4objectiterator_p.h" +#include "qv4object_p.h" +#include "qv4stringobject_p.h" +#include "qv4identifier_p.h" + +using namespace QV4; + +ObjectIterator::ObjectIterator(Object *o, uint flags) + : object(o) + , current(o) + , arrayNode(0) + , arrayIndex(0) + , memberIndex(0) + , flags(flags) +{ + tmpDynamicProperty.value = Value::undefinedValue(); +} + +Property *ObjectIterator::next(String **name, uint *index, PropertyAttributes *attrs) +{ + Property *p = 0; + *name = 0; + *index = UINT_MAX; + while (1) { + if (!current) + break; + + while (p = current->advanceIterator(this, name, index, attrs)) { + // check the property is not already defined earlier in the proto chain + if (current != object) { + Property *pp; + if (*name) { + pp = object->__getPropertyDescriptor__(*name); + } else { + assert (*index != UINT_MAX); + pp = object->__getPropertyDescriptor__(*index); + } + if (pp != p) + continue; + } + return p; + } + + if (flags & WithProtoChain) + current = current->prototype; + else + current = 0; + + arrayIndex = 0; + memberIndex = 0; + } + return 0; +} + +Value ObjectIterator::nextPropertyName(Value *value) +{ + PropertyAttributes attrs; + uint index; + String *name; + Property *p = next(&name, &index, &attrs); + if (!p) + return Value::nullValue(); + + if (value) + *value = object->getValue(p, attrs); + + if (name) + return Value::fromString(name); + assert(index < UINT_MAX); + return Value::fromDouble(index); +} + +Value ObjectIterator::nextPropertyNameAsString(Value *value) +{ + PropertyAttributes attrs; + uint index; + String *name; + Property *p = next(&name, &index, &attrs); + if (!p) + return Value::nullValue(); + + if (value) + *value = object->getValue(p, attrs); + + if (name) + return Value::fromString(name); + assert(index < UINT_MAX); + return Value::fromString(object->engine()->newString(QString::number(index))); +} diff --git a/src/qml/qml/v4/qv4objectiterator_p.h b/src/qml/qml/v4/qv4objectiterator_p.h new file mode 100644 index 0000000000..95439397f5 --- /dev/null +++ b/src/qml/qml/v4/qv4objectiterator_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4OBJECTITERATOR_H +#define QV4OBJECTITERATOR_H + +#include "qv4global_p.h" +#include "qv4property_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct SparseArrayNode; +struct Object; +struct ArrayObject; +struct PropertyAttributes; +struct ExecutionContext; +struct Property; +struct String; +struct InternalClass; + +struct Q_QML_EXPORT ObjectIterator +{ + enum Flags { + NoFlags = 0, + EnumerableOnly = 0x1, + WithProtoChain = 0x2, + }; + + Object *object; + Object *current; + SparseArrayNode *arrayNode; + uint arrayIndex; + uint memberIndex; + uint flags; + + Property tmpDynamicProperty; + + ObjectIterator(Object *o, uint flags); + Property *next(String **name, uint *index, PropertyAttributes *attributes = 0); + Value nextPropertyName(Value *value = 0); + Value nextPropertyNameAsString(Value *value = 0); +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4/qv4objectproto.cpp b/src/qml/qml/v4/qv4objectproto.cpp new file mode 100644 index 0000000000..462c9ca81e --- /dev/null +++ b/src/qml/qml/v4/qv4objectproto.cpp @@ -0,0 +1,624 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4objectproto_p.h" +#include "qv4mm_p.h" +#include <QtCore/qnumeric.h> +#include <QtCore/qmath.h> +#include <QtCore/QDateTime> +#include <QtCore/QStringList> +#include <QtCore/QDebug> +#include <cassert> + +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsast_p.h> +#include <qv4jsir_p.h> +#include <qv4codegen_p.h> +#include <qv4isel_masm_p.h> + +#ifndef Q_OS_WIN +# include <time.h> +# ifndef Q_OS_VXWORKS +# include <sys/time.h> +# else +# include "qplatformdefs.h" +# endif +#else +# include <windows.h> +#endif + +using namespace QV4; + + +DEFINE_MANAGED_VTABLE(ObjectCtor); + +ObjectCtor::ObjectCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("Object"))) +{ + vtbl = &static_vtbl; +} + +Value ObjectCtor::construct(Managed *that, Value *args, int argc) +{ + ObjectCtor *ctor = static_cast<ObjectCtor *>(that); + ExecutionEngine *v4 = that->engine(); + if (!argc || args[0].isUndefined() || args[0].isNull()) { + Object *obj = v4->newObject(); + Value proto = ctor->get(v4->id_prototype); + if (proto.isObject()) + obj->prototype = proto.objectValue(); + return Value::fromObject(obj); + } + return __qmljs_to_object(v4->current, args[0]); +} + +Value ObjectCtor::call(Managed *m, const Value &/*thisObject*/, Value *args, int argc) +{ + if (!argc || args[0].isUndefined() || args[0].isNull()) + return Value::fromObject(m->engine()->newObject()); + return __qmljs_to_object(m->engine()->current, args[0]); +} + +void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("create"), method_create, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperty"), method_defineProperty, 3); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperties"), method_defineProperties, 2); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("seal"), method_seal, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("freeze"), method_freeze, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isSealed"), method_isSealed, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isFrozen"), method_isFrozen, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isExtensible"), method_isExtensible, 1); + ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("keys"), method_keys, 1); + + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); + defineDefaultProperty(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 1); + defineDefaultProperty(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 1); + defineDefaultProperty(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 1); + defineDefaultProperty(ctx, QStringLiteral("__defineGetter__"), method_defineGetter, 2); + defineDefaultProperty(ctx, QStringLiteral("__defineSetter__"), method_defineSetter, 2); + + ExecutionEngine *v4 = ctx->engine; + Property *p = insertMember(v4->id___proto__, Attr_Accessor|Attr_NotEnumerable); + p->setGetter(v4->newBuiltinFunction(v4->rootContext, v4->id___proto__, method_get_proto)); + p->setSetter(v4->newBuiltinFunction(v4->rootContext, v4->id___proto__, method_set_proto)); +} + +Value ObjectPrototype::method_getPrototypeOf(SimpleCallContext *ctx) +{ + Value o = ctx->argument(0); + if (! o.isObject()) + ctx->throwTypeError(); + + Object *p = o.objectValue()->prototype; + return p ? Value::fromObject(p) : Value::nullValue(); +} + +Value ObjectPrototype::method_getOwnPropertyDescriptor(SimpleCallContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + String *name = ctx->argument(1).toString(ctx); + PropertyAttributes attrs; + Property *desc = O.objectValue()->__getOwnProperty__(name, &attrs); + return fromPropertyDescriptor(ctx, desc, attrs); +} + +Value ObjectPrototype::method_getOwnPropertyNames(SimpleCallContext *context) +{ + Object *O = context->argumentCount ? context->arguments[0].asObject() : 0; + if (!O) + context->throwTypeError(); + + ArrayObject *array = getOwnPropertyNames(context->engine, context->arguments[0]); + return Value::fromObject(array); +} + +Value ObjectPrototype::method_create(SimpleCallContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject() && !O.isNull()) + ctx->throwTypeError(); + + Object *newObject = ctx->engine->newObject(); + newObject->prototype = O.asObject(); + + Value objValue = Value::fromObject(newObject); + if (ctx->argumentCount > 1 && !ctx->argument(1).isUndefined()) { + ctx->arguments[0] = objValue; + method_defineProperties(ctx); + } + + return objValue; +} + +Value ObjectPrototype::method_defineProperty(SimpleCallContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + String *name = ctx->argument(1).toString(ctx); + + Value attributes = ctx->argument(2); + Property pd; + PropertyAttributes attrs; + toPropertyDescriptor(ctx, attributes, &pd, &attrs); + + if (!O.objectValue()->__defineOwnProperty__(ctx, name, pd, attrs)) + ctx->throwTypeError(); + + return O; +} + +Value ObjectPrototype::method_defineProperties(SimpleCallContext *ctx) +{ + Value O = ctx->argument(0); + if (!O.isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(1).toObject(ctx); + + ObjectIterator it(o, ObjectIterator::EnumerableOnly); + while (1) { + uint index; + String *name; + PropertyAttributes attrs; + Property *pd = it.next(&name, &index, &attrs); + if (!pd) + break; + Property n; + PropertyAttributes nattrs; + toPropertyDescriptor(ctx, o->getValue(pd, attrs), &n, &nattrs); + bool ok; + if (name) + ok = O.objectValue()->__defineOwnProperty__(ctx, name, n, nattrs); + else + ok = O.objectValue()->__defineOwnProperty__(ctx, index, n, nattrs); + if (!ok) + ctx->throwTypeError(); + } + + return O; +} + +Value ObjectPrototype::method_seal(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; + + o->internalClass = o->internalClass->sealed(); + + o->ensureArrayAttributes(); + for (uint i = 0; i < o->arrayDataLen; ++i) { + if (!o->arrayAttributes[i].isGeneric()) + o->arrayAttributes[i].setConfigurable(false); + } + + return ctx->argument(0); +} + +Value ObjectPrototype::method_freeze(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; + + o->internalClass = o->internalClass->frozen(); + + o->ensureArrayAttributes(); + for (uint i = 0; i < o->arrayDataLen; ++i) { + if (!o->arrayAttributes[i].isGeneric()) + o->arrayAttributes[i].setConfigurable(false); + if (o->arrayAttributes[i].isData()) + o->arrayAttributes[i].setWritable(false); + } + return ctx->argument(0); +} + +Value ObjectPrototype::method_preventExtensions(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + o->extensible = false; + return ctx->argument(0); +} + +Value ObjectPrototype::method_isSealed(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + if (o->extensible) + return Value::fromBoolean(false); + + if (o->internalClass != o->internalClass->sealed()) + return Value::fromBoolean(false); + + if (!o->arrayDataLen) + return Value::fromBoolean(true); + + if (!o->arrayAttributes) + return Value::fromBoolean(false); + + for (uint i = 0; i < o->arrayDataLen; ++i) { + if (!o->arrayAttributes[i].isGeneric()) + if (o->arrayAttributes[i].isConfigurable()) + return Value::fromBoolean(false); + } + + return Value::fromBoolean(true); +} + +Value ObjectPrototype::method_isFrozen(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + if (o->extensible) + return Value::fromBoolean(false); + + if (o->internalClass != o->internalClass->frozen()) + return Value::fromBoolean(false); + + if (!o->arrayDataLen) + return Value::fromBoolean(true); + + if (!o->arrayAttributes) + return Value::fromBoolean(false); + + for (uint i = 0; i < o->arrayDataLen; ++i) { + if (!o->arrayAttributes[i].isGeneric()) + if (o->arrayAttributes[i].isConfigurable() || o->arrayAttributes[i].isWritable()) + return Value::fromBoolean(false); + } + + return Value::fromBoolean(true); +} + +Value ObjectPrototype::method_isExtensible(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + return Value::fromBoolean(o->extensible); +} + +Value ObjectPrototype::method_keys(SimpleCallContext *ctx) +{ + if (!ctx->argument(0).isObject()) + ctx->throwTypeError(); + + Object *o = ctx->argument(0).objectValue(); + + ArrayObject *a = ctx->engine->newArrayObject(); + + ObjectIterator it(o, ObjectIterator::EnumerableOnly); + while (1) { + Value name = it.nextPropertyNameAsString(); + if (name.isNull()) + break; + a->push_back(name); + } + + return Value::fromObject(a); +} + +Value ObjectPrototype::method_toString(SimpleCallContext *ctx) +{ + if (ctx->thisObject.isUndefined()) { + return Value::fromString(ctx, QStringLiteral("[object Undefined]")); + } else if (ctx->thisObject.isNull()) { + return Value::fromString(ctx, QStringLiteral("[object Null]")); + } else { + Value obj = __qmljs_to_object(ctx, ctx->thisObject); + QString className = obj.objectValue()->className(); + return Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(className)); + } +} + +Value ObjectPrototype::method_toLocaleString(SimpleCallContext *ctx) +{ + Object *o = ctx->thisObject.toObject(ctx); + Value ts = o->get(ctx->engine->newString(QStringLiteral("toString"))); + FunctionObject *f = ts.asFunctionObject(); + if (!f) + ctx->throwTypeError(); + return f->call(Value::fromObject(o), 0, 0); +} + +Value ObjectPrototype::method_valueOf(SimpleCallContext *ctx) +{ + return Value::fromObject(ctx->thisObject.toObject(ctx)); +} + +Value ObjectPrototype::method_hasOwnProperty(SimpleCallContext *ctx) +{ + String *P = ctx->argument(0).toString(ctx); + Object *O = ctx->thisObject.toObject(ctx); + bool r = O->__getOwnProperty__(P) != 0; + return Value::fromBoolean(r); +} + +Value ObjectPrototype::method_isPrototypeOf(SimpleCallContext *ctx) +{ + Value V = ctx->argument(0); + if (! V.isObject()) + return Value::fromBoolean(false); + + Object *O = ctx->thisObject.toObject(ctx); + Object *proto = V.objectValue()->prototype; + while (proto) { + if (O == proto) + return Value::fromBoolean(true); + proto = proto->prototype; + } + return Value::fromBoolean(false); +} + +Value ObjectPrototype::method_propertyIsEnumerable(SimpleCallContext *ctx) +{ + String *p = ctx->argument(0).toString(ctx); + + Object *o = ctx->thisObject.toObject(ctx); + PropertyAttributes attrs; + o->__getOwnProperty__(p, &attrs); + return Value::fromBoolean(attrs.isEnumerable()); +} + +Value ObjectPrototype::method_defineGetter(SimpleCallContext *ctx) +{ + if (ctx->argumentCount < 2) + ctx->throwTypeError(); + String *prop = ctx->argument(0).toString(ctx); + + FunctionObject *f = ctx->argument(1).asFunctionObject(); + if (!f) + ctx->throwTypeError(); + + Object *o = ctx->thisObject.asObject(); + if (!o) { + if (!ctx->thisObject.isUndefined()) + return Value::undefinedValue(); + o = ctx->engine->globalObject; + } + + Property pd = Property::fromAccessor(f, 0); + o->__defineOwnProperty__(ctx, prop, pd, Attr_Accessor); + return Value::undefinedValue(); +} + +Value ObjectPrototype::method_defineSetter(SimpleCallContext *ctx) +{ + if (ctx->argumentCount < 2) + ctx->throwTypeError(); + String *prop = ctx->argument(0).toString(ctx); + + FunctionObject *f = ctx->argument(1).asFunctionObject(); + if (!f) + ctx->throwTypeError(); + + Object *o = ctx->thisObject.asObject(); + if (!o) { + if (!ctx->thisObject.isUndefined()) + return Value::undefinedValue(); + o = ctx->engine->globalObject; + } + + Property pd = Property::fromAccessor(0, f); + o->__defineOwnProperty__(ctx, prop, pd, Attr_Accessor); + return Value::undefinedValue(); +} + +Value ObjectPrototype::method_get_proto(SimpleCallContext *ctx) +{ + Object *o = ctx->thisObject.asObject(); + if (!o) + ctx->throwTypeError(); + + return Value::fromObject(o->prototype); +} + +Value ObjectPrototype::method_set_proto(SimpleCallContext *ctx) +{ + Object *o = ctx->thisObject.asObject(); + if (!o) + ctx->throwTypeError(); + + Value proto = ctx->argument(0); + bool ok = false; + if (proto.isNull()) { + o->prototype = 0; + ok = true; + } else if (Object *p = proto.asObject()) { + if (o->prototype == p) { + ok = true; + } else if (o->extensible) { + Object *pp = p; + while (pp) { + if (pp == o) + break; + pp = pp->prototype; + } + if (!pp) { + ok = true; + o->prototype = p; + } + } + } + if (!ok) + ctx->throwTypeError(QStringLiteral("Cyclic __proto__ value")); + return Value::undefinedValue(); +} + +void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Property *desc, PropertyAttributes *attrs) +{ + if (!v.isObject()) + ctx->throwTypeError(); + + Object *o = v.objectValue(); + + attrs->clear(); + desc->setGetter(0); + desc->setSetter(0); + + if (o->__hasProperty__(ctx->engine->id_enumerable)) + attrs->setEnumerable(o->get(ctx->engine->id_enumerable).toBoolean()); + + if (o->__hasProperty__(ctx->engine->id_configurable)) + attrs->setConfigurable(o->get(ctx->engine->id_configurable).toBoolean()); + + if (o->__hasProperty__(ctx->engine->id_get)) { + Value get = o->get(ctx->engine->id_get); + FunctionObject *f = get.asFunctionObject(); + if (f) { + desc->setGetter(f); + } else if (get.isUndefined()) { + desc->setGetter((FunctionObject *)0x1); + } else { + ctx->throwTypeError(); + } + attrs->setType(PropertyAttributes::Accessor); + } + + if (o->__hasProperty__(ctx->engine->id_set)) { + Value set = o->get(ctx->engine->id_set); + FunctionObject *f = set.asFunctionObject(); + if (f) { + desc->setSetter(f); + } else if (set.isUndefined()) { + desc->setSetter((FunctionObject *)0x1); + } else { + ctx->throwTypeError(); + } + attrs->setType(PropertyAttributes::Accessor); + } + + if (o->__hasProperty__(ctx->engine->id_writable)) { + if (attrs->isAccessor()) + ctx->throwTypeError(); + attrs->setWritable(o->get(ctx->engine->id_writable).toBoolean()); + // writable forces it to be a data descriptor + desc->value = Value::undefinedValue(); + } + + if (o->__hasProperty__(ctx->engine->id_value)) { + if (attrs->isAccessor()) + ctx->throwTypeError(); + desc->value = o->get(ctx->engine->id_value); + attrs->setType(PropertyAttributes::Data); + } + + if (attrs->isGeneric()) + desc->value = Value::emptyValue(); +} + + +Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const Property *desc, PropertyAttributes attrs) +{ + if (!desc) + return Value::undefinedValue(); + + ExecutionEngine *engine = ctx->engine; +// Let obj be the result of creating a new object as if by the expression new Object() where Object is the standard built-in constructor with that name. + Object *o = engine->newObject(); + + Property pd; + if (attrs.isData()) { + pd.value = desc->value; + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("value")), pd, Attr_Data); + pd.value = Value::fromBoolean(attrs.isWritable()); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("writable")), pd, Attr_Data); + } else { + pd.value = desc->getter() ? Value::fromObject(desc->getter()) : Value::undefinedValue(); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("get")), pd, Attr_Data); + pd.value = desc->setter() ? Value::fromObject(desc->setter()) : Value::undefinedValue(); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("set")), pd, Attr_Data); + } + pd.value = Value::fromBoolean(attrs.isEnumerable()); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("enumerable")), pd, Attr_Data); + pd.value = Value::fromBoolean(attrs.isConfigurable()); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("configurable")), pd, Attr_Data); + + return Value::fromObject(o); +} + + +ArrayObject *ObjectPrototype::getOwnPropertyNames(ExecutionEngine *v4, const Value &o) +{ + ArrayObject *array = v4->newArrayObject(); + Object *O = o.asObject(); + if (!O) + return array; + + ObjectIterator it(O, ObjectIterator::NoFlags); + while (1) { + Value name = it.nextPropertyNameAsString(); + if (name.isNull()) + break; + array->push_back(name); + } + return array; +} diff --git a/src/qml/qml/v4/qv4objectproto_p.h b/src/qml/qml/v4/qv4objectproto_p.h new file mode 100644 index 0000000000..ca2e77ca42 --- /dev/null +++ b/src/qml/qml/v4/qv4objectproto_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ECMAOBJECTS_P_H +#define QV4ECMAOBJECTS_P_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ObjectCtor: FunctionObject +{ + ObjectCtor(ExecutionContext *scope); + + static Value construct(Managed *that, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct ObjectPrototype: Object +{ + ObjectPrototype(ExecutionEngine *engine) : Object(engine) {} + + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_getPrototypeOf(SimpleCallContext *ctx); + static Value method_getOwnPropertyDescriptor(SimpleCallContext *ctx); + static Value method_getOwnPropertyNames(SimpleCallContext *context); + static Value method_create(SimpleCallContext *ctx); + static Value method_defineProperty(SimpleCallContext *ctx); + static Value method_defineProperties(SimpleCallContext *ctx); + static Value method_seal(SimpleCallContext *ctx); + static Value method_freeze(SimpleCallContext *ctx); + static Value method_preventExtensions(SimpleCallContext *ctx); + static Value method_isSealed(SimpleCallContext *ctx); + static Value method_isFrozen(SimpleCallContext *ctx); + static Value method_isExtensible(SimpleCallContext *ctx); + static Value method_keys(SimpleCallContext *ctx); + + static Value method_toString(SimpleCallContext *ctx); + static Value method_toLocaleString(SimpleCallContext *ctx); + static Value method_valueOf(SimpleCallContext *ctx); + static Value method_hasOwnProperty(SimpleCallContext *ctx); + static Value method_isPrototypeOf(SimpleCallContext *ctx); + static Value method_propertyIsEnumerable(SimpleCallContext *ctx); + + static Value method_defineGetter(SimpleCallContext *ctx); + static Value method_defineSetter(SimpleCallContext *ctx); + + static Value method_get_proto(SimpleCallContext *ctx); + static Value method_set_proto(SimpleCallContext *ctx); + + static void toPropertyDescriptor(ExecutionContext *ctx, Value v, Property *desc, PropertyAttributes *attrs); + static Value fromPropertyDescriptor(ExecutionContext *ctx, const Property *desc, PropertyAttributes attrs); + + static ArrayObject *getOwnPropertyNames(ExecutionEngine *v4, const Value &o); +}; + + +} + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4/qv4property_p.h b/src/qml/qml/v4/qv4property_p.h new file mode 100644 index 0000000000..024ad3c720 --- /dev/null +++ b/src/qml/qml/v4/qv4property_p.h @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4PROPERTYDESCRIPTOR_H +#define QV4PROPERTYDESCRIPTOR_H + +#include "qv4global_p.h" +#include "qv4value_p.h" +#include "qv4internalclass_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct FunctionObject; + +struct Property { + union { + Value value; + struct { + FunctionObject *get; + FunctionObject *set; + }; + }; + + // Section 8.10 + inline void fullyPopulated(PropertyAttributes *attrs) { + if (!attrs->hasType()) { + value = Value::undefinedValue(); + } + if (attrs->type() == PropertyAttributes::Accessor) { + attrs->clearWritable(); + if (get == (FunctionObject *)0x1) + get = 0; + if (set == (FunctionObject *)0x1) + set = 0; + } + attrs->resolve(); + } + + static inline Property fromValue(Value v) { + Property pd; + pd.value = v; + return pd; + } + static inline Property fromAccessor(FunctionObject *getter, FunctionObject *setter) { + Property pd; + pd.get = getter; + pd.set = setter; + return pd; + } + + static Property genericDescriptor() { + Property pd; + pd.value = Value::emptyValue(); + return pd; + } + + inline bool isSubset(const PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) const; + inline void merge(PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs); + + inline FunctionObject *getter() const { return get; } + inline FunctionObject *setter() const { return set; } + inline void setGetter(FunctionObject *g) { get = g; } + inline void setSetter(FunctionObject *s) { set = s; } +}; + +inline bool Property::isSubset(const PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) const +{ + if (attrs.type() != PropertyAttributes::Generic && attrs.type() != otherAttrs.type()) + return false; + if (attrs.hasEnumerable() && attrs.isEnumerable() != otherAttrs.isEnumerable()) + return false; + if (attrs.hasConfigurable() && attrs.isConfigurable() != otherAttrs.isConfigurable()) + return false; + if (attrs.hasWritable() && attrs.isWritable() != otherAttrs.isWritable()) + return false; + if (attrs.type() == PropertyAttributes::Data && !value.sameValue(other.value)) + return false; + if (attrs.type() == PropertyAttributes::Accessor) { + if (get != other.get) + return false; + if (set != other.set) + return false; + } + return true; +} + +inline void Property::merge(PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) +{ + if (otherAttrs.hasEnumerable()) + attrs.setEnumerable(otherAttrs.isEnumerable()); + if (otherAttrs.hasConfigurable()) + attrs.setConfigurable(otherAttrs.isConfigurable()); + if (otherAttrs.hasWritable()) + attrs.setWritable(otherAttrs.isWritable()); + if (otherAttrs.type() == PropertyAttributes::Accessor) { + attrs.setType(PropertyAttributes::Accessor); + if (other.get) + get = (other.get == (FunctionObject *)0x1) ? 0 : other.get; + if (other.set) + set = (other.set == (FunctionObject *)0x1) ? 0 : other.set; + } else if (otherAttrs.type() == PropertyAttributes::Data){ + attrs.setType(PropertyAttributes::Data); + value = other.value; + } +} + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4/qv4qmlextensions.cpp b/src/qml/qml/v4/qv4qmlextensions.cpp new file mode 100644 index 0000000000..a55330ff67 --- /dev/null +++ b/src/qml/qml/v4/qv4qmlextensions.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4qmlextensions_p.h" +#include "qv4object_p.h" + +using namespace QV4; + +void QmlExtensions::markObjects() +{ + if (valueTypeWrapperPrototype) + valueTypeWrapperPrototype->mark(); +} diff --git a/src/qml/qml/v4/qv4qmlextensions_p.h b/src/qml/qml/v4/qv4qmlextensions_p.h new file mode 100644 index 0000000000..cf9e287efe --- /dev/null +++ b/src/qml/qml/v4/qv4qmlextensions_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4QMLEXTENSIONS_P_H +#define QV4QMLEXTENSIONS_P_H + +#include <qtqmlglobal.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { +struct Object; + +struct Q_QML_EXPORT QmlExtensions +{ + QmlExtensions() + : valueTypeWrapperPrototype(0) + {} + + Object *valueTypeWrapperPrototype; + + void markObjects(); +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4/qv4qobjectwrapper.cpp b/src/qml/qml/v4/qv4qobjectwrapper.cpp new file mode 100644 index 0000000000..f79675845b --- /dev/null +++ b/src/qml/qml/v4/qv4qobjectwrapper.cpp @@ -0,0 +1,1792 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4qobjectwrapper_p.h" + +#include <private/qqmlpropertycache_p.h> +#include <private/qqmlengine_p.h> +#include <private/qqmlvmemetaobject_p.h> +#include <private/qqmlbinding_p.h> +#include <private/qjsvalue_p.h> +#include <private/qqmlaccessors_p.h> +#include <private/qqmlexpression_p.h> +#include <private/qqmlglobal_p.h> +#include <private/qqmltypewrapper_p.h> +#include <private/qqmlvaluetypewrapper_p.h> +#include <private/qqmlcontextwrapper_p.h> +#include <private/qqmllistwrapper_p.h> +#include <private/qv8engine_p.h> + +#include <private/qv4functionobject_p.h> +#include <private/qv4runtime_p.h> +#include <private/qv4variantobject_p.h> +#include <private/qv4sequenceobject_p.h> +#include <private/qv4objectproto_p.h> +#include <private/qv4jsonobject_p.h> +#include <private/qv4regexpobject_p.h> + +#include <QtQml/qjsvalue.h> +#include <QtCore/qjsonarray.h> +#include <QtCore/qjsonobject.h> +#include <QtCore/qjsonvalue.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qtimer.h> +#include <QtCore/qatomic.h> + +QT_BEGIN_NAMESPACE + +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) +# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 +// The code in this file does not violate strict aliasing, but GCC thinks it does +// so turn off the warnings for us to have a clean build +# pragma GCC diagnostic ignored "-Wstrict-aliasing" +# endif +#endif + + +using namespace QV4; + +static QPair<QObject *, int> extractQtMethod(QV4::FunctionObject *function) +{ + if (function && function->subtype == QV4::FunctionObject::WrappedQtMethod) { + QObjectMethod *method = static_cast<QObjectMethod*>(function); + return qMakePair(method->object(), method->methodIndex()); + } + + return qMakePair((QObject *)0, -1); +} + +static QPair<QObject *, int> extractQtSignal(const Value &value) +{ + if (QV4::FunctionObject *function = value.asFunctionObject()) + return extractQtMethod(function); + + if (QV4::QmlSignalHandler *handler = value.as<QV4::QmlSignalHandler>()) + return qMakePair(handler->object(), handler->signalIndex()); + + return qMakePair((QObject *)0, -1); +} + + +struct ReadAccessor { + static inline void Indirect(QObject *object, const QQmlPropertyData &property, + void *output, QQmlNotifier **n) + { + Q_ASSERT(n == 0); + Q_UNUSED(n); + + void *args[] = { output, 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); + } + + static inline void Direct(QObject *object, const QQmlPropertyData &property, + void *output, QQmlNotifier **n) + { + Q_ASSERT(n == 0); + Q_UNUSED(n); + + void *args[] = { output, 0 }; + object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args); + } + + static inline void Accessor(QObject *object, const QQmlPropertyData &property, + void *output, QQmlNotifier **n) + { + Q_ASSERT(property.accessors); + + property.accessors->read(object, property.accessorData, output); + if (n) property.accessors->notifier(object, property.accessorData, n); + } +}; + +static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, int v) +{ return QV4::Value::fromInt32(v); } +static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, uint v) +{ return QV4::Value::fromUInt32(v); } +static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, bool v) +{ return QV4::Value::fromBoolean(v); } +static inline QV4::Value valueToHandle(QV4::ExecutionEngine *e, const QString &v) +{ return QV4::Value::fromString(e, v); } +static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, float v) +{ return QV4::Value::fromDouble(v); } +static inline QV4::Value valueToHandle(QV4::ExecutionEngine *, double v) +{ return QV4::Value::fromDouble(v); } +static inline QV4::Value valueToHandle(QV4::ExecutionEngine *e, QObject *v) +{ return QV4::QObjectWrapper::wrap(e, v); } + +// Load value properties +template<void (*ReadFunction)(QObject *, const QQmlPropertyData &, + void *, QQmlNotifier **)> +static QV4::Value LoadProperty(QV8Engine *engine, QObject *object, + const QQmlPropertyData &property, + QQmlNotifier **notifier) +{ + Q_ASSERT(!property.isFunction()); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + + if (property.isQObject()) { + QObject *rv = 0; + ReadFunction(object, property, &rv, notifier); + return QV4::QObjectWrapper::wrap(v4, rv); + } else if (property.isQList()) { + return QmlListWrapper::create(engine, object, property.coreIndex, property.propType); + } else if (property.propType == QMetaType::QReal) { + qreal v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(v4, v); + } else if (property.propType == QMetaType::Int || property.isEnum()) { + int v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(v4, v); + } else if (property.propType == QMetaType::Bool) { + bool v = false; + ReadFunction(object, property, &v, notifier); + return valueToHandle(v4, v); + } else if (property.propType == QMetaType::QString) { + QString v; + ReadFunction(object, property, &v, notifier); + return valueToHandle(v4, v); + } else if (property.propType == QMetaType::UInt) { + uint v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(v4, v); + } else if (property.propType == QMetaType::Float) { + float v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(v4, v); + } else if (property.propType == QMetaType::Double) { + double v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(v4, v); + } else if (property.isV4Handle()) { + QQmlV4Handle handle; + ReadFunction(object, property, &handle, notifier); + return handle.toValue(); + } else if (property.propType == qMetaTypeId<QJSValue>()) { + QJSValue v; + ReadFunction(object, property, &v, notifier); + return QJSValuePrivate::get(v)->getValue(v4); + } else if (property.isQVariant()) { + QVariant v; + ReadFunction(object, property, &v, notifier); + + if (QQmlValueTypeFactory::isValueType(v.userType())) { + if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(v.userType())) + return QV4::QmlValueTypeWrapper::create(engine, object, property.coreIndex, valueType); // VariantReference value-type. + } + + return engine->fromVariant(v); + } else if (QQmlValueTypeFactory::isValueType(property.propType)) { + Q_ASSERT(notifier == 0); + + if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(property.propType)) + return QV4::QmlValueTypeWrapper::create(engine, object, property.coreIndex, valueType); + } else { + Q_ASSERT(notifier == 0); + + // see if it's a sequence type + bool succeeded = false; + QV4::Value retn = QV4::SequencePrototype::newSequence(v4, property.propType, object, property.coreIndex, &succeeded); + if (succeeded) + return retn; + } + + if (property.propType == QMetaType::UnknownType) { + QMetaProperty p = object->metaObject()->property(property.coreIndex); + qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property " + "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name()); + return QV4::Value::undefinedValue(); + } else { + QVariant v(property.propType, (void *)0); + ReadFunction(object, property, v.data(), notifier); + return engine->fromVariant(v); + } +} + +QObjectWrapper::QObjectWrapper(ExecutionEngine *engine, QObject *object) + : Object(engine) + , m_object(object) +{ + vtbl = &static_vtbl; + prototype = engine->objectPrototype; + + m_destroy = engine->newIdentifier(QStringLiteral("destroy")); + m_toString = engine->newIdentifier(QStringLiteral("toString")); +} + +void QObjectWrapper::initializeBindings(ExecutionEngine *engine) +{ + engine->functionPrototype->defineDefaultProperty(engine, QStringLiteral("connect"), method_connect); + engine->functionPrototype->defineDefaultProperty(engine, QStringLiteral("disconnect"), method_disconnect); +} + +QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const +{ + QQmlData *ddata = QQmlData::get(m_object, false); + if (!ddata) + return 0; + QQmlPropertyData *result = 0; + if (ddata && ddata->propertyCache) + result = ddata->propertyCache->property(name, m_object, qmlContext); + if (!result) + result = QQmlPropertyCache::property(engine->v8Engine->engine(), m_object, name, qmlContext, *local); + return result; +} + +Value QObjectWrapper::getQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, bool includeImports) +{ + if (QQmlData::wasDeleted(m_object)) { + if (hasProperty) + *hasProperty = false; + return QV4::Value::undefinedValue(); + } + + if (name->isEqualTo(m_destroy) || name->isEqualTo(m_toString)) { + int index = name->isEqualTo(m_destroy) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod; + QV4::Value method = QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, index); + if (hasProperty) + *hasProperty = true; + return method; + } + + QQmlPropertyData local; + QQmlPropertyData *result = findProperty(ctx->engine, qmlContext, name, revisionMode, &local); + + if (!result) { + if (includeImports && name->startsWithUpper()) { + // Check for attached properties + if (qmlContext && qmlContext->imports) { + QQmlTypeNameCache::Result r = qmlContext->imports->query(name); + + if (hasProperty) + *hasProperty = true; + + if (r.isValid()) { + if (r.scriptIndex != -1) { + return QV4::Value::undefinedValue(); + } else if (r.type) { + return QmlTypeWrapper::create(ctx->engine->v8Engine, m_object, r.type, QmlTypeWrapper::ExcludeEnums); + } else if (r.importNamespace) { + return QmlTypeWrapper::create(ctx->engine->v8Engine, m_object, qmlContext->imports, r.importNamespace, QmlTypeWrapper::ExcludeEnums); + } + Q_ASSERT(!"Unreachable"); + } + } + } + return QV4::Object::get(this, name, hasProperty); + } + + QQmlData::flushPendingBinding(m_object, result->coreIndex); + QQmlData *ddata = QQmlData::get(m_object, false); + + if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) { + if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) { + if (hasProperty) + *hasProperty = false; + return QV4::Value::undefinedValue(); + } + } + + if (hasProperty) + *hasProperty = true; + + if (result->isFunction() && !result->isVarProperty()) { + if (result->isVMEFunction()) { + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_object); + Q_ASSERT(vmemo); + return vmemo->vmeMethod(result->coreIndex); + } else if (result->isV4Function()) { + return QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, result->coreIndex, QV4::Value::fromObject(ctx->engine->qmlContextObject())); + } else if (result->isSignalHandler()) { + QV4::QmlSignalHandler *handler = new (ctx->engine->memoryManager) QV4::QmlSignalHandler(ctx->engine, m_object, result->coreIndex); + + QV4::String *connect = ctx->engine->newIdentifier(QStringLiteral("connect")); + QV4::String *disconnect = ctx->engine->newIdentifier(QStringLiteral("disconnect")); + handler->put(connect, ctx->engine->functionPrototype->get(connect)); + handler->put(disconnect, ctx->engine->functionPrototype->get(disconnect)); + + return QV4::Value::fromObject(handler); + } else { + return QV4::QObjectMethod::create(ctx->engine->rootContext, m_object, result->coreIndex); + } + } + + QQmlEnginePrivate *ep = ctx->engine->v8Engine->engine() ? QQmlEnginePrivate::get(ctx->engine->v8Engine->engine()) : 0; + + if (result->hasAccessors()) { + QQmlNotifier *n = 0; + QQmlNotifier **nptr = 0; + + if (ep && ep->propertyCapture && result->accessors->notifier) + nptr = &n; + + QV4::Value rv = LoadProperty<ReadAccessor::Accessor>(ctx->engine->v8Engine, m_object, *result, nptr); + + if (result->accessors->notifier) { + if (n) ep->captureProperty(n); + } else { + ep->captureProperty(m_object, result->coreIndex, result->notifyIndex); + } + + return rv; + } + + if (ep && !result->isConstant()) + ep->captureProperty(m_object, result->coreIndex, result->notifyIndex); + + if (result->isVarProperty()) { + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_object); + Q_ASSERT(vmemo); + return vmemo->vmeProperty(result->coreIndex); + } else if (result->isDirect()) { + return LoadProperty<ReadAccessor::Direct>(ctx->engine->v8Engine, m_object, *result, 0); + } else { + return LoadProperty<ReadAccessor::Indirect>(ctx->engine->v8Engine, m_object, *result, 0); + } +} + +Value QObjectWrapper::getQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty) +{ + if (QQmlData::wasDeleted(object)) { + if (hasProperty) + *hasProperty = false; + return QV4::Value::nullValue(); + } + + if (!QQmlData::get(object, true)) { + if (hasProperty) + *hasProperty = false; + return QV4::Value::nullValue(); + } + + QObjectWrapper *wrapper = wrap(ctx->engine, object).as<QV4::QObjectWrapper>(); + if (!wrapper) { + if (hasProperty) + *hasProperty = false; + return QV4::Value::nullValue(); + } + return wrapper->getQmlProperty(ctx, qmlContext, name, revisionMode, hasProperty); +} + +bool QObjectWrapper::setQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, const Value &value) +{ + if (QQmlData::wasDeleted(object)) + return false; + + QQmlPropertyData local; + QQmlPropertyData *result = 0; + { + result = QQmlPropertyCache::property(ctx->engine->v8Engine->engine(), object, name, qmlContext, local); + } + + if (!result) + return false; + + if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) { + QQmlData *ddata = QQmlData::get(object); + if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) + return false; + } + + if (!result->isWritable() && !result->isQList()) { + QString error = QLatin1String("Cannot assign to read-only property \"") + + name->toQString() + QLatin1Char('\"'); + ctx->throwTypeError(error); + } + + QQmlBinding *newBinding = 0; + if (FunctionObject *f = value.asFunctionObject()) { + if (!f->bindingKeyFlag) { + if (!result->isVarProperty() && result->propType != qMetaTypeId<QJSValue>()) { + // assigning a JS function to a non var or QJSValue property or is not allowed. + QString error = QLatin1String("Cannot assign JavaScript function to "); + if (!QMetaType::typeName(result->propType)) + error += QLatin1String("[unknown property type]"); + else + error += QLatin1String(QMetaType::typeName(result->propType)); + ctx->throwError(error); + } + } else { + // binding assignment. + QQmlContextData *callingQmlContext = QV4::QmlContextWrapper::callingContext(ctx->engine); + + QV4::ExecutionEngine::StackFrame frame = ctx->engine->currentStackFrame(); + + newBinding = new QQmlBinding(value, object, callingQmlContext, frame.source, + qmlSourceCoordinate(frame.line), qmlSourceCoordinate(frame.column)); + newBinding->setTarget(object, *result, callingQmlContext); + newBinding->setEvaluateFlags(newBinding->evaluateFlags() | + QQmlBinding::RequiresThisObject); + } + } + + QQmlAbstractBinding *oldBinding = + QQmlPropertyPrivate::setBinding(object, result->coreIndex, -1, newBinding); + if (oldBinding) + oldBinding->destroy(); + + if (!newBinding && result->isVarProperty()) { + // allow assignment of "special" values (null, undefined, function) to var properties + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); + Q_ASSERT(vmemo); + vmemo->setVMEProperty(result->coreIndex, value); + return true; + } + +#define PROPERTY_STORE(cpptype, value) \ + cpptype o = value; \ + int status = -1; \ + int flags = 0; \ + void *argv[] = { &o, 0, &status, &flags }; \ + QMetaObject::metacall(object, QMetaObject::WriteProperty, result->coreIndex, argv); + + if (value.isNull() && result->isQObject()) { + PROPERTY_STORE(QObject*, 0); + } else if (value.isUndefined() && result->isResettable()) { + void *a[] = { 0 }; + QMetaObject::metacall(object, QMetaObject::ResetProperty, result->coreIndex, a); + } else if (value.isUndefined() && result->propType == qMetaTypeId<QVariant>()) { + PROPERTY_STORE(QVariant, QVariant()); + } else if (value.isUndefined() && result->propType == QMetaType::QJsonValue) { + PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined)); + } else if (!newBinding && result->propType == qMetaTypeId<QJSValue>()) { + PROPERTY_STORE(QJSValue, new QJSValuePrivate(ctx->engine, value)); + } else if (value.isUndefined()) { + QString error = QLatin1String("Cannot assign [undefined] to "); + if (!QMetaType::typeName(result->propType)) + error += QLatin1String("[unknown property type]"); + else + error += QLatin1String(QMetaType::typeName(result->propType)); + ctx->throwError(error); + } else if (value.asFunctionObject()) { + // this is handled by the binding creation above + } else if (result->propType == QMetaType::Int && value.isNumber()) { + PROPERTY_STORE(int, qRound(value.asDouble())); + } else if (result->propType == QMetaType::QReal && value.isNumber()) { + PROPERTY_STORE(qreal, qreal(value.asDouble())); + } else if (result->propType == QMetaType::Float && value.isNumber()) { + PROPERTY_STORE(float, float(value.asDouble())); + } else if (result->propType == QMetaType::Double && value.isNumber()) { + PROPERTY_STORE(double, double(value.asDouble())); + } else if (result->propType == QMetaType::QString && value.isString()) { + PROPERTY_STORE(QString, value.toQString()); + } else if (result->isVarProperty()) { + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); + Q_ASSERT(vmemo); + vmemo->setVMEProperty(result->coreIndex, value); + } else { + QVariant v; + if (result->isQList()) + v = ctx->engine->v8Engine->toVariant(value, qMetaTypeId<QList<QObject *> >()); + else + v = ctx->engine->v8Engine->toVariant(value, result->propType); + + QQmlContextData *callingQmlContext = QV4::QmlContextWrapper::callingContext(ctx->engine); + if (!QQmlPropertyPrivate::write(object, *result, v, callingQmlContext)) { + const char *valueType = 0; + if (v.userType() == QVariant::Invalid) valueType = "null"; + else valueType = QMetaType::typeName(v.userType()); + + const char *targetTypeName = QMetaType::typeName(result->propType); + if (!targetTypeName) + targetTypeName = "an unregistered type"; + + QString error = QLatin1String("Cannot assign ") + + QLatin1String(valueType) + + QLatin1String(" to ") + + QLatin1String(targetTypeName); + ctx->throwError(error); + } + } + + return true; +} + +Value QObjectWrapper::wrap(ExecutionEngine *engine, QObject *object) +{ + if (QQmlData::wasDeleted(object)) + return QV4::Value::nullValue(); + + QQmlData *ddata = QQmlData::get(object, true); + if (!ddata) + return QV4::Value::undefinedValue(); + + if (ddata->jsEngineId == engine->m_engineId && !ddata->jsWrapper.isEmpty()) { + // We own the JS object + return ddata->jsWrapper.value(); + } else if (ddata->jsWrapper.isEmpty() && + (ddata->jsEngineId == engine->m_engineId || // We own the QObject + ddata->jsEngineId == 0 || // No one owns the QObject + !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted + + QV4::Value rv = create(engine, ddata, object); + ddata->jsWrapper = rv; + ddata->jsEngineId = engine->m_engineId; + return rv; + + } else { + // If this object is tainted, we have to check to see if it is in our + // tainted object list + Object *alternateWrapper = 0; + if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV8Object) + alternateWrapper = engine->m_multiplyWrappedQObjects->value(object); + + // If our tainted handle doesn't exist or has been collected, and there isn't + // a handle in the ddata, we can assume ownership of the ddata->v8object + if (ddata->jsWrapper.isEmpty() && !alternateWrapper) { + QV4::Value result = create(engine, ddata, object); + ddata->jsWrapper = result; + ddata->jsEngineId = engine->m_engineId; + return result; + } + + if (!alternateWrapper) { + alternateWrapper = create(engine, ddata, object).asObject(); + if (!engine->m_multiplyWrappedQObjects) + engine->m_multiplyWrappedQObjects = new MultiplyWrappedQObjectMap; + engine->m_multiplyWrappedQObjects->insert(object, alternateWrapper); + ddata->hasTaintedV8Object = true; + } + + return QV4::Value::fromObject(alternateWrapper); + } +} + +QV4::Value QObjectWrapper::create(ExecutionEngine *engine, QQmlData *ddata, QObject *object) +{ + QQmlEngine *qmlEngine = engine->v8Engine->engine(); + if (!ddata->propertyCache && qmlEngine) { + ddata->propertyCache = QQmlEnginePrivate::get(qmlEngine)->cache(object); + if (ddata->propertyCache) ddata->propertyCache->addref(); + } + + return Value::fromObject(new (engine->memoryManager) QV4::QObjectWrapper(engine, object)); +} + +QV4::Value QObjectWrapper::get(Managed *m, String *name, bool *hasProperty) +{ + QObjectWrapper *that = static_cast<QObjectWrapper*>(m); + ExecutionEngine *v4 = m->engine(); + QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(v4); + return that->getQmlProperty(v4->current, qmlContext, name, IgnoreRevision, hasProperty, /*includeImports*/ true); +} + +void QObjectWrapper::put(Managed *m, String *name, const Value &value) +{ + QObjectWrapper *that = static_cast<QObjectWrapper*>(m); + ExecutionEngine *v4 = m->engine(); + + if (QQmlData::wasDeleted(that->m_object)) + return; + + QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(v4); + if (!setQmlProperty(v4->current, qmlContext, that->m_object, name, QV4::QObjectWrapper::IgnoreRevision, value)) { + QString error = QLatin1String("Cannot assign to non-existent property \"") + + name->toQString() + QLatin1Char('\"'); + v4->current->throwError(error); + } +} + +PropertyAttributes QObjectWrapper::query(const Managed *m, String *name) +{ + const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m); + ExecutionEngine *engine = that->engine(); + QQmlContextData *qmlContext = QV4::QmlContextWrapper::callingContext(engine); + QQmlPropertyData local; + if (that->findProperty(engine, qmlContext, name, IgnoreRevision, &local) + || name->isEqualTo(that->m_destroy) || name->isEqualTo(that->m_toString)) + return QV4::Attr_Data; + else + return QV4::Object::query(m, name); +} + +Property *QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes) +{ + *name = 0; + *index = UINT_MAX; + + QObjectWrapper *that = static_cast<QObjectWrapper*>(m); + + if (!that->m_object) + return QV4::Object::advanceIterator(m, it, name, index, attributes); + + const QMetaObject *mo = that->m_object->metaObject(); + const int propertyCount = mo->propertyCount(); + if (it->arrayIndex < propertyCount) { + *name = that->engine()->newString(QString::fromUtf8(mo->property(it->arrayIndex).name())); + ++it->arrayIndex; + if (attributes) + *attributes = QV4::Attr_Data; + it->tmpDynamicProperty.value = that->get(*name); + return &it->tmpDynamicProperty; + } + const int methodCount = mo->methodCount(); + if (it->arrayIndex < propertyCount + methodCount) { + *name = that->engine()->newString(QString::fromUtf8(mo->method(it->arrayIndex - propertyCount).name())); + ++it->arrayIndex; + if (attributes) + *attributes = QV4::Attr_Data; + it->tmpDynamicProperty.value = that->get(*name); + return &it->tmpDynamicProperty; + } + return QV4::Object::advanceIterator(m, it, name, index, attributes); +} + +namespace QV4 { + +struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase +{ + QV4::PersistentValue function; + QV4::PersistentValue thisObject; + int signalIndex; + + QObjectSlotDispatcher() + : QtPrivate::QSlotObjectBase(&impl) + , signalIndex(-1) + {} + + static void impl(int which, QSlotObjectBase *this_, QObject *r, void **metaArgs, bool *ret) + { + switch (which) { + case Destroy: { + delete static_cast<QObjectSlotDispatcher*>(this_); + } + break; + case Call: { + QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_); + QVarLengthArray<int, 9> dummy; + int *argsTypes = QQmlPropertyCache::methodParameterTypes(r, This->signalIndex, dummy, 0); + + int argCount = argsTypes ? argsTypes[0]:0; + + QV4::FunctionObject *f = This->function.value().asFunctionObject(); + QV4::ExecutionEngine *v4 = f->internalClass->engine; + QV4::ExecutionContext *ctx = v4->current; + + QVarLengthArray<QV4::Value, 9> args(argCount); + for (int ii = 0; ii < argCount; ++ii) { + int type = argsTypes[ii + 1]; + if (type == qMetaTypeId<QVariant>()) { + args[ii] = v4->v8Engine->fromVariant(*((QVariant *)metaArgs[ii + 1])); + } else { + args[ii] = v4->v8Engine->fromVariant(QVariant(type, metaArgs[ii + 1])); + } + } + + try { + f->call(This->thisObject.isEmpty() ? Value::fromObject(v4->globalObject) : This->thisObject.value(), args.data(), argCount); + } catch (QV4::Exception &e) { + e.accept(ctx); + QQmlError error; + QQmlExpressionPrivate::exceptionToError(e, error); + if (error.description().isEmpty()) + error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(f->name->toQString())); + QQmlEnginePrivate::get(v4->v8Engine->engine())->warning(error); + } + } + break; + case Compare: { + QObjectSlotDispatcher *connection = static_cast<QObjectSlotDispatcher*>(this_); + if (connection->function.isEmpty()) { + *ret = false; + return; + } + + // This is tricky. Normally the metaArgs[0] pointer is a pointer to the _function_ + // for the new-style QObject::connect. Here we use the engine pointer as sentinel + // to distinguish those type of QSlotObjectBase connections from our QML connections. + QV4::ExecutionEngine *v4 = reinterpret_cast<QV4::ExecutionEngine*>(metaArgs[0]); + if (v4 != connection->function.engine()) { + *ret = false; + return; + } + + QV4::Value function = *reinterpret_cast<QV4::Value*>(metaArgs[1]); + QV4::Value thisObject = *reinterpret_cast<QV4::Value*>(metaArgs[2]); + QObject *receiverToDisconnect = reinterpret_cast<QObject*>(metaArgs[3]); + int slotIndexToDisconnect = *reinterpret_cast<int*>(metaArgs[4]); + + if (slotIndexToDisconnect != -1) { + // This is a QObject function wrapper + if (connection->thisObject.isEmpty() == thisObject.isEmpty() && + (connection->thisObject.isEmpty() || __qmljs_strict_equal(connection->thisObject, thisObject))) { + + QPair<QObject *, int> connectedFunctionData = extractQtMethod(connection->function.value().asFunctionObject()); + if (connectedFunctionData.first == receiverToDisconnect && + connectedFunctionData.second == slotIndexToDisconnect) { + *ret = true; + return; + } + } + } else { + // This is a normal JS function + if (__qmljs_strict_equal(connection->function, function) && + connection->thisObject.isEmpty() == thisObject.isEmpty() && + (connection->thisObject.isEmpty() || __qmljs_strict_equal(connection->thisObject, thisObject))) { + *ret = true; + return; + } + } + + *ret = false; + } + break; + case NumOperations: + break; + } + }; +}; + +} // namespace QV4 + +Value QObjectWrapper::method_connect(SimpleCallContext *ctx) +{ + if (ctx->argumentCount == 0) + V4THROW_ERROR("Function.prototype.connect: no arguments given"); + + QPair<QObject *, int> signalInfo = extractQtSignal(ctx->thisObject); + QObject *signalObject = signalInfo.first; + int signalIndex = signalInfo.second; + + if (signalIndex < 0) + V4THROW_ERROR("Function.prototype.connect: this object is not a signal"); + + if (!signalObject) + V4THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject"); + + if (signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal) + V4THROW_ERROR("Function.prototype.connect: this object is not a signal"); + + QV4::QObjectSlotDispatcher *slot = new QV4::QObjectSlotDispatcher; + slot->signalIndex = signalIndex; + + if (ctx->argumentCount == 1) { + slot->function = ctx->arguments[0]; + } else if (ctx->argumentCount >= 2) { + slot->thisObject = ctx->arguments[0]; + slot->function = ctx->arguments[1]; + } + + if (!slot->function.value().asFunctionObject()) + V4THROW_ERROR("Function.prototype.connect: target is not a function"); + + if (!slot->thisObject.isEmpty() && !slot->thisObject.value().isObject()) + V4THROW_ERROR("Function.prototype.connect: target this is not an object"); + + QObjectPrivate::connect(signalObject, signalIndex, slot, Qt::AutoConnection); + + return QV4::Value::undefinedValue(); +} + +Value QObjectWrapper::method_disconnect(SimpleCallContext *ctx) +{ + if (ctx->argumentCount == 0) + V4THROW_ERROR("Function.prototype.disconnect: no arguments given"); + + QPair<QObject *, int> signalInfo = extractQtSignal(ctx->thisObject); + QObject *signalObject = signalInfo.first; + int signalIndex = signalInfo.second; + + if (signalIndex == -1) + V4THROW_ERROR("Function.prototype.disconnect: this object is not a signal"); + + if (!signalObject) + V4THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject"); + + if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal) + V4THROW_ERROR("Function.prototype.disconnect: this object is not a signal"); + + QV4::Value functionValue = QV4::Value::emptyValue(); + QV4::Value functionThisValue = QV4::Value::emptyValue(); + + if (ctx->argumentCount == 1) { + functionValue = ctx->arguments[0]; + } else if (ctx->argumentCount >= 2) { + functionThisValue = ctx->arguments[0]; + functionValue = ctx->arguments[1]; + } + + if (!functionValue.asFunctionObject()) + V4THROW_ERROR("Function.prototype.disconnect: target is not a function"); + + if (!functionThisValue.isEmpty() && !functionThisValue.isObject()) + V4THROW_ERROR("Function.prototype.disconnect: target this is not an object"); + + QPair<QObject *, int> functionData = extractQtMethod(functionValue.asFunctionObject()); + + void *a[] = { + ctx->engine, + &functionValue, + &functionThisValue, + functionData.first, + &functionData.second + }; + + QObjectPrivate::disconnect(signalObject, signalIndex, reinterpret_cast<void**>(&a)); + + return QV4::Value::undefinedValue(); +} + +static void markChildQObjectsRecursively(QObject *parent) +{ + const QObjectList &children = parent->children(); + for (int i = 0; i < children.count(); ++i) { + QObject *child = children.at(i); + QQmlData *ddata = QQmlData::get(child, /*create*/false); + if (ddata) + ddata->jsWrapper.markOnce(); + markChildQObjectsRecursively(child); + } +} + +void QObjectWrapper::markObjects(Managed *that) +{ + QObjectWrapper *This = static_cast<QObjectWrapper*>(that); + + if (QObject *o = This->m_object.data()) { + QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o); + if (vme) + vme->mark(); + + // Children usually don't need to be marked, the gc keeps them alive. + // But in the rare case of a "floating" QObject without a parent that + // _gets_ marked (we've been called here!) then we also need to + // propagate the marking down to the children recursively. + if (!o->parent()) + markChildQObjectsRecursively(o); + } + + QV4::Object::markObjects(that); +} + +namespace { + struct QObjectDeleter : public QV4::GCDeletable + { + QObjectDeleter(QObject *o) + : m_objectToDelete(o) + {} + ~QObjectDeleter() + { + QQmlData *ddata = QQmlData::get(m_objectToDelete, false); + if (ddata && ddata->ownContext && ddata->context) + ddata->context->emitDestruction(); + // This object is notionally destroyed now + ddata->isQueuedForDeletion = true; + if (lastCall) + delete m_objectToDelete; + else + m_objectToDelete->deleteLater(); + } + + QObject *m_objectToDelete; + }; +} + +void QObjectWrapper::collectDeletables(Managed *m, GCDeletable **deletable) +{ + QObjectWrapper *This = static_cast<QObjectWrapper*>(m); + QPointer<QObject> &object = This->m_object; + if (!object) + return; + + QQmlData *ddata = QQmlData::get(object, false); + if (!ddata) + return; + + if (object->parent() || ddata->indestructible) + return; + + QObjectDeleter *deleter = new QObjectDeleter(object); + object = 0; + deleter->next = *deletable; + *deletable = deleter; +} + +DEFINE_MANAGED_VTABLE_WITH_DELETABLES(QObjectWrapper); + +// XXX TODO: Need to review all calls to QQmlEngine *engine() to confirm QObjects work +// correctly in a worker thread + +namespace { + +template<typename A, typename B, typename C, typename D, typename E, + typename F, typename G, typename H> +class MaxSizeOf8 { + template<typename Z, typename X> + struct SMax { + char dummy[sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X)]; + }; +public: + static const size_t Size = sizeof(SMax<A, SMax<B, SMax<C, SMax<D, SMax<E, SMax<F, SMax<G, H> > > > > > >); +}; + +struct CallArgument { + inline CallArgument(); + inline ~CallArgument(); + inline void *dataPtr(); + + inline void initAsType(int type); + inline void fromValue(int type, QV8Engine *, const QV4::Value&); + inline QV4::Value toValue(QV8Engine *); + +private: + CallArgument(const CallArgument &); + + inline void cleanup(); + + union { + float floatValue; + double doubleValue; + quint32 intValue; + bool boolValue; + QObject *qobjectPtr; + + char allocData[MaxSizeOf8<QVariant, + QString, + QList<QObject *>, + QJSValue, + QQmlV4Handle, + QJsonArray, + QJsonObject, + QJsonValue>::Size]; + qint64 q_for_alignment; + }; + + // Pointers to allocData + union { + QString *qstringPtr; + QVariant *qvariantPtr; + QList<QObject *> *qlistPtr; + QJSValue *qjsValuePtr; + QQmlV4Handle *handlePtr; + QJsonArray *jsonArrayPtr; + QJsonObject *jsonObjectPtr; + QJsonValue *jsonValuePtr; + }; + + int type; +}; +} + +namespace { +struct CallArgs +{ + CallArgs(int length, QV4::Value *args) : _length(length), _args(args) {} + int Length() const { return _length; } + QV4::Value operator[](int idx) { return _args[idx]; } + +private: + int _length; + QV4::Value *_args; +}; +} + +static QV4::Value CallMethod(QObject *object, int index, int returnType, int argCount, + int *argTypes, QV8Engine *engine, CallArgs &callArgs) +{ + if (argCount > 0) { + + // Special handling is required for value types. + // We need to save the current value in a temporary, + // and reapply it after converting all arguments. + // This avoids the "overwriting copy-value-type-value" + // problem during Q_INVOKABLE function invocation. + QQmlValueType *valueTypeObject = qobject_cast<QQmlValueType*>(object); + QVariant valueTypeValue; + if (valueTypeObject) + valueTypeValue = valueTypeObject->value(); + + // Convert all arguments. + QVarLengthArray<CallArgument, 9> args(argCount + 1); + args[0].initAsType(returnType); + for (int ii = 0; ii < argCount; ++ii) + args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]); + QVarLengthArray<void *, 9> argData(args.count()); + for (int ii = 0; ii < args.count(); ++ii) + argData[ii] = args[ii].dataPtr(); + + // Reinstate saved value type object value if required. + if (valueTypeObject) + valueTypeObject->setValue(valueTypeValue); + + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data()); + + return args[0].toValue(engine); + + } else if (returnType != QMetaType::Void) { + + CallArgument arg; + arg.initAsType(returnType); + + void *args[] = { arg.dataPtr() }; + + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args); + + return arg.toValue(engine); + + } else { + + void *args[] = { 0 }; + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args); + return QV4::Value::undefinedValue(); + + } +} + +/*! + Returns the match score for converting \a actual to be of type \a conversionType. A + zero score means "perfect match" whereas a higher score is worse. + + The conversion table is copied out of the \l QScript::callQtMethod() function. +*/ +static int MatchScore(const QV4::Value &actual, int conversionType) +{ + if (actual.isNumber()) { + switch (conversionType) { + case QMetaType::Double: + return 0; + case QMetaType::Float: + return 1; + case QMetaType::LongLong: + case QMetaType::ULongLong: + return 2; + case QMetaType::Long: + case QMetaType::ULong: + return 3; + case QMetaType::Int: + case QMetaType::UInt: + return 4; + case QMetaType::Short: + case QMetaType::UShort: + return 5; + break; + case QMetaType::Char: + case QMetaType::UChar: + return 6; + case QMetaType::QJsonValue: + return 5; + default: + return 10; + } + } else if (actual.isString()) { + switch (conversionType) { + case QMetaType::QString: + return 0; + case QMetaType::QJsonValue: + return 5; + default: + return 10; + } + } else if (actual.isBoolean()) { + switch (conversionType) { + case QMetaType::Bool: + return 0; + case QMetaType::QJsonValue: + return 5; + default: + return 10; + } + } else if (actual.asDateObject()) { + switch (conversionType) { + case QMetaType::QDateTime: + return 0; + case QMetaType::QDate: + return 1; + case QMetaType::QTime: + return 2; + default: + return 10; + } + } else if (actual.as<QV4::RegExpObject>()) { + switch (conversionType) { + case QMetaType::QRegExp: + return 0; + default: + return 10; + } + } else if (actual.asArrayObject()) { + switch (conversionType) { + case QMetaType::QJsonArray: + return 3; + case QMetaType::QStringList: + case QMetaType::QVariantList: + return 5; + case QMetaType::QVector4D: + case QMetaType::QMatrix4x4: + return 6; + case QMetaType::QVector3D: + return 7; + default: + return 10; + } + } else if (actual.isNull()) { + switch (conversionType) { + case QMetaType::VoidStar: + case QMetaType::QObjectStar: + case QMetaType::QJsonValue: + return 0; + default: { + const char *typeName = QMetaType::typeName(conversionType); + if (typeName && typeName[strlen(typeName) - 1] == '*') + return 0; + else + return 10; + } + } + } else if (QV4::Object *obj = actual.asObject()) { + QV8Engine *engine = obj->engine()->v8Engine; + + if (QV4::VariantObject *v = obj->as<QV4::VariantObject>()) { + if (conversionType == qMetaTypeId<QVariant>()) + return 0; + if (engine->toVariant(actual, -1).userType() == conversionType) + return 0; + else + return 10; + } + + if (obj->as<QObjectWrapper>()) { + switch (conversionType) { + case QMetaType::QObjectStar: + return 0; + default: + return 10; + } + } + + if (obj->as<QV4::QmlValueTypeWrapper>()) { + if (engine->toVariant(actual, -1).userType() == conversionType) + return 0; + return 10; + } else if (conversionType == QMetaType::QJsonObject) { + return 5; + } else { + return 10; + } + + } else { + return 10; + } +} + +static inline int QMetaObject_methods(const QMetaObject *metaObject) +{ + struct Private + { + int revision; + int className; + int classInfoCount, classInfoData; + int methodCount, methodData; + }; + + return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount; +} + +/*! +Returns the next related method, if one, or 0. +*/ +static const QQmlPropertyData * RelatedMethod(QObject *object, + const QQmlPropertyData *current, + QQmlPropertyData &dummy) +{ + QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache; + if (!current->isOverload()) + return 0; + + Q_ASSERT(!current->overrideIndexIsProperty); + + if (cache) { + return cache->method(current->overrideIndex); + } else { + const QMetaObject *mo = object->metaObject(); + int methodOffset = mo->methodCount() - QMetaObject_methods(mo); + + while (methodOffset > current->overrideIndex) { + mo = mo->superClass(); + methodOffset -= QMetaObject_methods(mo); + } + + QMetaMethod method = mo->method(current->overrideIndex); + dummy.load(method); + + // Look for overloaded methods + QByteArray methodName = method.name(); + for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) { + if (methodName == mo->method(ii).name()) { + dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload); + dummy.overrideIndexIsProperty = 0; + dummy.overrideIndex = ii; + return &dummy; + } + } + + return &dummy; + } +} + +static QV4::Value CallPrecise(QObject *object, const QQmlPropertyData &data, + QV8Engine *engine, CallArgs &callArgs) +{ + QByteArray unknownTypeError; + + int returnType = QQmlPropertyCache::methodReturnType(object, data, &unknownTypeError); + + if (returnType == QMetaType::UnknownType) { + QString typeName = QString::fromLatin1(unknownTypeError); + QString error = QString::fromLatin1("Unknown method return type: %1").arg(typeName); + QV8Engine::getV4(engine)->current->throwError(error); + } + + if (data.hasArguments()) { + + int *args = 0; + QVarLengthArray<int, 9> dummy; + + args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy, + &unknownTypeError); + + if (!args) { + QString typeName = QString::fromLatin1(unknownTypeError); + QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName); + QV8Engine::getV4(engine)->current->throwError(error); + } + + if (args[0] > callArgs.Length()) { + QString error = QLatin1String("Insufficient arguments"); + QV8Engine::getV4(engine)->current->throwError(error); + } + + return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs); + + } else { + + return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs); + + } +} + +/*! +Resolve the overloaded method to call. The algorithm works conceptually like this: + 1. Resolve the set of overloads it is *possible* to call. + Impossible overloads include those that have too many parameters or have parameters + of unknown type. + 2. Filter the set of overloads to only contain those with the closest number of + parameters. + For example, if we are called with 3 parameters and there are 2 overloads that + take 2 parameters and one that takes 3, eliminate the 2 parameter overloads. + 3. Find the best remaining overload based on its match score. + If two or more overloads have the same match score, call the last one. The match + score is constructed by adding the matchScore() result for each of the parameters. +*/ +static QV4::Value CallOverloaded(QObject *object, const QQmlPropertyData &data, + QV8Engine *engine, CallArgs &callArgs) +{ + int argumentCount = callArgs.Length(); + + const QQmlPropertyData *best = 0; + int bestParameterScore = INT_MAX; + int bestMatchScore = INT_MAX; + + // Special handling is required for value types. + // We need to save the current value in a temporary, + // and reapply it after converting all arguments. + // This avoids the "overwriting copy-value-type-value" + // problem during Q_INVOKABLE function invocation. + QQmlValueType *valueTypeObject = qobject_cast<QQmlValueType*>(object); + QVariant valueTypeValue; + if (valueTypeObject) + valueTypeValue = valueTypeObject->value(); + + QQmlPropertyData dummy; + const QQmlPropertyData *attempt = &data; + + do { + QVarLengthArray<int, 9> dummy; + int methodArgumentCount = 0; + int *methodArgTypes = 0; + if (attempt->hasArguments()) { + typedef QQmlPropertyCache PC; + int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0); + if (!args) // Must be an unknown argument + continue; + + methodArgumentCount = args[0]; + methodArgTypes = args + 1; + } + + if (methodArgumentCount > argumentCount) + continue; // We don't have sufficient arguments to call this method + + int methodParameterScore = argumentCount - methodArgumentCount; + if (methodParameterScore > bestParameterScore) + continue; // We already have a better option + + int methodMatchScore = 0; + for (int ii = 0; ii < methodArgumentCount; ++ii) + methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]); + + if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { + best = attempt; + bestParameterScore = methodParameterScore; + bestMatchScore = methodMatchScore; + } + + if (bestParameterScore == 0 && bestMatchScore == 0) + break; // We can't get better than that + + } while((attempt = RelatedMethod(object, attempt, dummy)) != 0); + + if (best) { + if (valueTypeObject) + valueTypeObject->setValue(valueTypeValue); + return CallPrecise(object, *best, engine, callArgs); + } else { + QString error = QLatin1String("Unable to determine callable overload. Candidates are:"); + const QQmlPropertyData *candidate = &data; + while (candidate) { + error += QLatin1String("\n ") + + QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).methodSignature().constData()); + candidate = RelatedMethod(object, candidate, dummy); + } + + QV8Engine::getV4(engine)->current->throwError(error); + } +} + +CallArgument::CallArgument() +: type(QVariant::Invalid) +{ +} + +CallArgument::~CallArgument() +{ + cleanup(); +} + +void CallArgument::cleanup() +{ + if (type == QMetaType::QString) { + qstringPtr->~QString(); + } else if (type == -1 || type == QMetaType::QVariant) { + qvariantPtr->~QVariant(); + } else if (type == qMetaTypeId<QJSValue>()) { + qjsValuePtr->~QJSValue(); + } else if (type == qMetaTypeId<QList<QObject *> >()) { + qlistPtr->~QList<QObject *>(); + } else if (type == QMetaType::QJsonArray) { + jsonArrayPtr->~QJsonArray(); + } else if (type == QMetaType::QJsonObject) { + jsonObjectPtr->~QJsonObject(); + } else if (type == QMetaType::QJsonValue) { + jsonValuePtr->~QJsonValue(); + } +} + +void *CallArgument::dataPtr() +{ + if (type == -1) + return qvariantPtr->data(); + else + return (void *)&allocData; +} + +void CallArgument::initAsType(int callType) +{ + if (type != 0) { cleanup(); type = 0; } + if (callType == QMetaType::UnknownType) return; + + if (callType == qMetaTypeId<QJSValue>()) { + qjsValuePtr = new (&allocData) QJSValue(); + type = callType; + } else if (callType == QMetaType::Int || + callType == QMetaType::UInt || + callType == QMetaType::Bool || + callType == QMetaType::Double || + callType == QMetaType::Float) { + type = callType; + } else if (callType == QMetaType::QObjectStar) { + qobjectPtr = 0; + type = callType; + } else if (callType == QMetaType::QString) { + qstringPtr = new (&allocData) QString(); + type = callType; + } else if (callType == QMetaType::QVariant) { + type = callType; + qvariantPtr = new (&allocData) QVariant(); + } else if (callType == qMetaTypeId<QList<QObject *> >()) { + type = callType; + qlistPtr = new (&allocData) QList<QObject *>(); + } else if (callType == qMetaTypeId<QQmlV4Handle>()) { + type = callType; + handlePtr = new (&allocData) QQmlV4Handle; + } else if (callType == QMetaType::QJsonArray) { + type = callType; + jsonArrayPtr = new (&allocData) QJsonArray(); + } else if (callType == QMetaType::QJsonObject) { + type = callType; + jsonObjectPtr = new (&allocData) QJsonObject(); + } else if (callType == QMetaType::QJsonValue) { + type = callType; + jsonValuePtr = new (&allocData) QJsonValue(); + } else if (callType == QMetaType::Void) { + type = -1; + qvariantPtr = new (&allocData) QVariant(); + } else { + type = -1; + qvariantPtr = new (&allocData) QVariant(callType, (void *)0); + } +} + +void CallArgument::fromValue(int callType, QV8Engine *engine, const QV4::Value &value) +{ + if (type != 0) { cleanup(); type = 0; } + + if (callType == qMetaTypeId<QJSValue>()) { + qjsValuePtr = new (&allocData) QJSValue(new QJSValuePrivate(QV8Engine::getV4(engine), value)); + type = qMetaTypeId<QJSValue>(); + } else if (callType == QMetaType::Int) { + intValue = quint32(value.toInt32()); + type = callType; + } else if (callType == QMetaType::UInt) { + intValue = quint32(value.toUInt32()); + type = callType; + } else if (callType == QMetaType::Bool) { + boolValue = value.toBoolean(); + type = callType; + } else if (callType == QMetaType::Double) { + doubleValue = double(value.toNumber()); + type = callType; + } else if (callType == QMetaType::Float) { + floatValue = float(value.toNumber()); + type = callType; + } else if (callType == QMetaType::QString) { + if (value.isNull() || value.isUndefined()) + qstringPtr = new (&allocData) QString(); + else + qstringPtr = new (&allocData) QString(value.toQString()); + type = callType; + } else if (callType == QMetaType::QObjectStar) { + qobjectPtr = 0; + if (QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) + qobjectPtr = qobjectWrapper->object(); + type = callType; + } else if (callType == qMetaTypeId<QVariant>()) { + qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1)); + type = callType; + } else if (callType == qMetaTypeId<QList<QObject*> >()) { + qlistPtr = new (&allocData) QList<QObject *>(); + if (QV4::ArrayObject *array = value.asArrayObject()) { + uint32_t length = array->arrayLength(); + for (uint32_t ii = 0; ii < length; ++ii) { + QObject *o = 0; + if (QV4::QObjectWrapper *qobjectWrapper = array->getIndexed(ii).as<QV4::QObjectWrapper>()) + o = qobjectWrapper->object(); + qlistPtr->append(o); + } + } else { + QObject *o = 0; + if (QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) + o = qobjectWrapper->object(); + qlistPtr->append(o); + } + type = callType; + } else if (callType == qMetaTypeId<QQmlV4Handle>()) { + handlePtr = new (&allocData) QQmlV4Handle(QQmlV4Handle(value)); + type = callType; + } else if (callType == QMetaType::QJsonArray) { + jsonArrayPtr = new (&allocData) QJsonArray(QV4::JsonObject::toJsonArray(value.asArrayObject())); + type = callType; + } else if (callType == QMetaType::QJsonObject) { + jsonObjectPtr = new (&allocData) QJsonObject(QV4::JsonObject::toJsonObject(value.asObject())); + type = callType; + } else if (callType == QMetaType::QJsonValue) { + jsonValuePtr = new (&allocData) QJsonValue(QV4::JsonObject::toJsonValue(value)); + type = callType; + } else if (callType == QMetaType::Void) { + *qvariantPtr = QVariant(); + } else { + qvariantPtr = new (&allocData) QVariant(); + type = -1; + + QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0; + QVariant v = engine->toVariant(value, -1); // why -1 instead of callType? + + if (v.userType() == callType) { + *qvariantPtr = v; + } else if (v.canConvert(callType)) { + *qvariantPtr = v; + qvariantPtr->convert(callType); + } else if (QV4::SequencePrototype::isSequenceType(callType) && v.userType() == qMetaTypeId<QVariantList>()) { + // convert the JS array to a sequence of the correct type. + QVariant seqV = engine->toVariant(value, callType); + *qvariantPtr = seqV; + } else { + QQmlMetaObject mo = ep ? ep->rawMetaObjectForType(callType) : QQmlMetaObject(); + if (!mo.isNull()) { + QObject *obj = ep->toQObject(v); + + if (obj != 0 && !QQmlMetaObject::canConvert(obj, mo)) + obj = 0; + + *qvariantPtr = QVariant(callType, &obj); + } else { + *qvariantPtr = QVariant(callType, (void *)0); + } + } + } +} + +QV4::Value CallArgument::toValue(QV8Engine *engine) +{ + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + if (type == qMetaTypeId<QJSValue>()) { + return QJSValuePrivate::get(*qjsValuePtr)->getValue(v4); + } else if (type == QMetaType::Int) { + return QV4::Value::fromInt32(int(intValue)); + } else if (type == QMetaType::UInt) { + return QV4::Value::fromUInt32(intValue); + } else if (type == QMetaType::Bool) { + return QV4::Value::fromBoolean(boolValue); + } else if (type == QMetaType::Double) { + return QV4::Value::fromDouble(doubleValue); + } else if (type == QMetaType::Float) { + return QV4::Value::fromDouble(floatValue); + } else if (type == QMetaType::QString) { + return engine->toString(*qstringPtr); + } else if (type == QMetaType::QObjectStar) { + QObject *object = qobjectPtr; + if (object) + QQmlData::get(object, true)->setImplicitDestructible(); + return QV4::QObjectWrapper::wrap(v4, object); + } else if (type == qMetaTypeId<QList<QObject *> >()) { + // XXX Can this be made more by using Array as a prototype and implementing + // directly against QList<QObject*>? + QList<QObject *> &list = *qlistPtr; + QV4::ArrayObject *array = v4->newArrayObject(); + array->arrayReserve(list.count()); + array->arrayDataLen = list.count(); + for (int ii = 0; ii < list.count(); ++ii) + array->arrayData[ii].value = QV4::QObjectWrapper::wrap(v4, list.at(ii)); + array->setArrayLengthUnchecked(list.count()); + return QV4::Value::fromObject(array); + } else if (type == qMetaTypeId<QQmlV4Handle>()) { + return handlePtr->toValue(); + } else if (type == QMetaType::QJsonArray) { + return QV4::JsonObject::fromJsonArray(v4, *jsonArrayPtr); + } else if (type == QMetaType::QJsonObject) { + return QV4::JsonObject::fromJsonObject(v4, *jsonObjectPtr); + } else if (type == QMetaType::QJsonValue) { + return QV4::JsonObject::fromJsonValue(v4, *jsonValuePtr); + } else if (type == -1 || type == qMetaTypeId<QVariant>()) { + QVariant value = *qvariantPtr; + QV4::Value rv = engine->fromVariant(value); + if (QV4::QObjectWrapper *qobjectWrapper = rv.as<QV4::QObjectWrapper>()) { + if (QObject *object = qobjectWrapper->object()) + QQmlData::get(object, true)->setImplicitDestructible(); + } + return rv; + } else { + return QV4::Value::undefinedValue(); + } +} + +Value QObjectMethod::create(ExecutionContext *scope, QObject *object, int index, const Value &qmlGlobal) +{ + return Value::fromObject(new (scope->engine->memoryManager) QObjectMethod(scope, object, index, qmlGlobal)); +} + +QObjectMethod::QObjectMethod(ExecutionContext *scope, QObject *object, int index, const Value &qmlGlobal) + : FunctionObject(scope) + , m_object(object) + , m_index(index) + , m_qmlGlobal(qmlGlobal) +{ + vtbl = &static_vtbl; + subtype = WrappedQtMethod; +} + +QV4::Value QObjectMethod::method_toString(QV4::ExecutionContext *ctx) +{ + QString result; + if (m_object) { + QString objectName = m_object->objectName(); + + result += QString::fromUtf8(m_object->metaObject()->className()); + result += QLatin1String("(0x"); + result += QString::number((quintptr)m_object.data(),16); + + if (!objectName.isEmpty()) { + result += QLatin1String(", \""); + result += objectName; + result += QLatin1Char('\"'); + } + + result += QLatin1Char(')'); + } else { + result = QLatin1String("null"); + } + + return QV4::Value::fromString(ctx, result); +} + +QV4::Value QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, Value *args, int argc) +{ + if (!m_object) + return QV4::Value::undefinedValue(); + if (QQmlData::keepAliveDuringGarbageCollection(m_object)) + ctx->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object")); + + int delay = 0; + if (argc > 0) + delay = args[0].toUInt32(); + + if (delay > 0) + QTimer::singleShot(delay, m_object, SLOT(deleteLater())); + else + m_object->deleteLater(); + + return QV4::Value::undefinedValue(); +} + +Value QObjectMethod::call(Managed *m, const Value &thisObject, Value *args, int argc) +{ + QObjectMethod *This = static_cast<QObjectMethod*>(m); + return This->callInternal(thisObject, args, argc); +} + +Value QObjectMethod::callInternal(const Value &, Value *args, int argc) +{ + ExecutionContext *context = engine()->current; + if (m_index == DestroyMethod) + return method_destroy(context, args, argc); + else if (m_index == ToStringMethod) + return method_toString(context); + + QObject *object = m_object.data(); + if (!object) + return QV4::Value::undefinedValue(); + + QQmlData *ddata = QQmlData::get(object); + if (!ddata) + return QV4::Value::undefinedValue(); + + QV8Engine *v8Engine = context->engine->v8Engine; + + QQmlPropertyData method; + + if (QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(object)->declarativeData)) { + if (ddata->propertyCache) { + QQmlPropertyData *d = ddata->propertyCache->method(m_index); + if (!d) + return QV4::Value::undefinedValue(); + method = *d; + } + } + + if (method.coreIndex == -1) { + method.load(object->metaObject()->method(m_index)); + + if (method.coreIndex == -1) + return QV4::Value::undefinedValue(); + } + + if (method.isV4Function()) { + QV4::Value rv = QV4::Value::undefinedValue(); + + QQmlV4Function func(argc, args, &rv, m_qmlGlobal.value(), + QmlContextWrapper::getContext(m_qmlGlobal.value()), + v8Engine); + QQmlV4Function *funcptr = &func; + + void *args[] = { 0, &funcptr }; + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args); + + return rv; + } + + CallArgs callArgs(argc, args); + if (!method.isOverload()) { + return CallPrecise(object, method, v8Engine, callArgs); + } else { + return CallOverloaded(object, method, v8Engine, callArgs); + } +} + +DEFINE_MANAGED_VTABLE(QObjectMethod); + +QmlSignalHandler::QmlSignalHandler(ExecutionEngine *engine, QObject *object, int signalIndex) + : Object(engine) + , m_object(object) + , m_signalIndex(signalIndex) +{ + vtbl = &static_vtbl; + prototype = engine->objectPrototype; +} + +DEFINE_MANAGED_VTABLE(QmlSignalHandler); + +void MultiplyWrappedQObjectMap::insert(QObject *key, Object *value) +{ + QHash<QObject*, Object*>::insert(key, value); + connect(key, SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*))); +} + +MultiplyWrappedQObjectMap::Iterator MultiplyWrappedQObjectMap::erase(MultiplyWrappedQObjectMap::Iterator it) +{ + disconnect(it.key(), SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*))); + return QHash<QObject*, Object*>::erase(it); +} + +void MultiplyWrappedQObjectMap::remove(QObject *key) +{ + Iterator it = find(key); + if (it == end()) + return; + erase(it); +} + +void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object) +{ + QHash<QObject*, Object*>::remove(object); +} + +QT_END_NAMESPACE + diff --git a/src/qml/qml/v4/qv4qobjectwrapper_p.h b/src/qml/qml/v4/qv4qobjectwrapper_p.h new file mode 100644 index 0000000000..6580d19fe9 --- /dev/null +++ b/src/qml/qml/v4/qv4qobjectwrapper_p.h @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4QOBJECTWRAPPER_P_H +#define QV4QOBJECTWRAPPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtCore/qmetatype.h> +#include <QtCore/qpair.h> +#include <QtCore/qhash.h> +#include <private/qhashedstring_p.h> +#include <private/qqmldata_p.h> +#include <private/qqmlpropertycache_p.h> +#include <private/qintrusivelist_p.h> + +#include <private/qv4value_p.h> +#include <private/qv4functionobject_p.h> + +QT_BEGIN_NAMESPACE + +class QObject; +class QQmlData; +class QQmlPropertyCache; +class QQmlPropertyData; + +namespace QV4 { +struct QObjectSlotDispatcher; + +struct Q_QML_EXPORT QObjectWrapper : public QV4::Object +{ + Q_MANAGED + + enum RevisionMode { IgnoreRevision, CheckRevision }; + + static void initializeBindings(ExecutionEngine *engine); + + QObject *object() const { return m_object.data(); } + + Value getQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, bool *hasProperty = 0, bool includeImports = false); + static Value getQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = 0); + + static bool setQmlProperty(ExecutionContext *ctx, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, const Value &value); + + static Value wrap(ExecutionEngine *engine, QObject *object); + + using Object::get; + +private: + static Value create(ExecutionEngine *engine, QQmlData *ddata, QObject *object); + + QObjectWrapper(ExecutionEngine *engine, QObject *object); + + QQmlPropertyData *findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const; + + QPointer<QObject> m_object; + String *m_destroy; + String *m_toString; + + static Value get(Managed *m, String *name, bool *hasProperty); + static void put(Managed *m, String *name, const Value &value); + static PropertyAttributes query(const Managed *, String *name); + static Property *advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes); + static void markObjects(Managed *that); + static void collectDeletables(Managed *m, GCDeletable **deletable); + static void destroy(Managed *that) + { + static_cast<QObjectWrapper *>(that)->~QObjectWrapper(); + } + + static Value method_connect(SimpleCallContext *ctx); + static Value method_disconnect(SimpleCallContext *ctx); +}; + +struct QObjectMethod : public QV4::FunctionObject +{ + Q_MANAGED + + enum { DestroyMethod = -1, ToStringMethod = -2 }; + + static Value create(QV4::ExecutionContext *scope, QObject *object, int index, const Value &qmlGlobal = Value::undefinedValue()); + + int methodIndex() const { return m_index; } + QObject *object() const { return m_object.data(); } + +private: + QObjectMethod(QV4::ExecutionContext *scope, QObject *object, int index, const QV4::Value &qmlGlobal); + + QV4::Value method_toString(QV4::ExecutionContext *ctx); + QV4::Value method_destroy(QV4::ExecutionContext *ctx, Value *args, int argc); + + QPointer<QObject> m_object; + int m_index; + QV4::PersistentValue m_qmlGlobal; + + static Value call(Managed *, const Value &thisObject, Value *args, int argc); + + Value callInternal(const Value &, Value *args, int argc); + + static void destroy(Managed *that) + { + static_cast<QObjectMethod *>(that)->~QObjectMethod(); + } +}; + +struct QmlSignalHandler : public QV4::Object +{ + Q_MANAGED + + QmlSignalHandler(ExecutionEngine *engine, QObject *object, int signalIndex); + + int signalIndex() const { return m_signalIndex; } + QObject *object() const { return m_object.data(); } + +private: + QPointer<QObject> m_object; + int m_signalIndex; + + static void destroy(Managed *that) + { + static_cast<QmlSignalHandler *>(that)->~QmlSignalHandler(); + } +}; + +class MultiplyWrappedQObjectMap : public QObject, + private QHash<QObject*, Object*> +{ + Q_OBJECT +public: + typedef QHash<QObject*, Object*>::ConstIterator ConstIterator; + typedef QHash<QObject*, Object*>::Iterator Iterator; + + ConstIterator begin() const { return QHash<QObject*, Object*>::constBegin(); } + Iterator begin() { return QHash<QObject*, Object*>::begin(); } + ConstIterator end() const { return QHash<QObject*, Object*>::constEnd(); } + Iterator end() { return QHash<QObject*, Object*>::end(); } + + void insert(QObject *key, Object *value); + Object *value(QObject *key) const { return QHash<QObject*, Object*>::value(key, 0); } + Iterator erase(Iterator it); + void remove(QObject *key); + +private slots: + void removeDestroyedObject(QObject*); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4QOBJECTWRAPPER_P_H + + diff --git a/src/qml/qml/v4/qv4regexp.cpp b/src/qml/qml/v4/qv4regexp.cpp new file mode 100644 index 0000000000..ab01ce7650 --- /dev/null +++ b/src/qml/qml/v4/qv4regexp.cpp @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4regexp_p.h" + +#include "qv4engine_p.h" + +using namespace QV4; + +RegExpCache::~RegExpCache() +{ + for (RegExpCache::Iterator it = begin(), e = end(); + it != e; ++it) + it.value()->m_cache = 0; + clear(); +} + +DEFINE_MANAGED_VTABLE(RegExp); + +uint RegExp::match(const QString &string, int start, uint *matchOffsets) +{ + if (!isValid()) + return JSC::Yarr::offsetNoMatch; + + WTF::String s(string); + +#if ENABLE(YARR_JIT) + if (!m_jitCode.isFallBack() && m_jitCode.has16BitCode()) + return m_jitCode.execute(s.characters16(), start, s.length(), (int*)matchOffsets).start; +#endif + + return JSC::Yarr::interpret(m_byteCode.get(), s.characters16(), string.length(), start, matchOffsets); +} + +RegExp* RegExp::create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline) +{ + RegExpCacheKey key(pattern, ignoreCase, multiline); + + RegExpCache *cache = engine->regExpCache; + if (cache) { + if (RegExp *result = cache->value(key)) + return result; + } + + RegExp *result = new (engine->memoryManager) RegExp(engine, pattern, ignoreCase, multiline); + + if (!cache) + cache = engine->regExpCache = new RegExpCache; + + result->m_cache = cache; + cache->insert(key, result); + + return result; +} + +RegExp::RegExp(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline) + : Managed(engine->emptyClass) + , m_pattern(pattern) + , m_cache(0) + , m_subPatternCount(0) + , m_ignoreCase(ignoreCase) + , m_multiLine(multiline) +{ + vtbl = &static_vtbl; + type = Type_RegExpObject; + + if (!engine) + return; + const char* error = 0; + JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiline, &error); + if (error) + return; + m_subPatternCount = yarrPattern.m_numSubpatterns; + m_byteCode = JSC::Yarr::byteCompile(yarrPattern, engine->bumperPointerAllocator); +#if ENABLE(YARR_JIT) + if (!yarrPattern.m_containsBackreferences && engine->iselFactory->jitCompileRegexps()) { + JSC::JSGlobalData dummy(engine->regExpAllocator); + JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, &dummy, m_jitCode); + } +#endif +} + +RegExp::~RegExp() +{ + if (m_cache) { + RegExpCacheKey key(this); + m_cache->remove(key); + } + _data = 0; +} + +void RegExp::destroy(Managed *that) +{ + static_cast<RegExp*>(that)->~RegExp(); +} + +void RegExp::markObjects(Managed *that) +{ +} + +Value RegExp::get(Managed *m, String *name, bool *hasProperty) +{ + return Value::undefinedValue(); +} + +Value RegExp::getIndexed(Managed *m, uint index, bool *hasProperty) +{ + return Value::undefinedValue(); +} + +void RegExp::put(Managed *m, String *name, const Value &value) +{ +} + +void RegExp::putIndexed(Managed *m, uint index, const Value &value) +{ +} + +PropertyAttributes RegExp::query(const Managed *m, String *name) +{ + return Attr_Invalid; +} + +PropertyAttributes RegExp::queryIndexed(const Managed *m, uint index) +{ + return Attr_Invalid; +} + +bool RegExp::deleteProperty(Managed *, String *) +{ + return false; +} + +bool RegExp::deleteIndexedProperty(Managed *m, uint index) +{ + return false; +} + +Property *RegExp::advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes) +{ + return 0; +} diff --git a/src/qml/qml/v4/qv4regexp_p.h b/src/qml/qml/v4/qv4regexp_p.h new file mode 100644 index 0000000000..6edbd4b2ad --- /dev/null +++ b/src/qml/qml/v4/qv4regexp_p.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4REGEXP_H +#define QV4REGEXP_H + +#include <QString> +#include <QVector> + +#include <wtf/RefPtr.h> +#include <wtf/FastAllocBase.h> +#include <wtf/BumpPointerAllocator.h> + +#include <limits.h> + +#include <yarr/Yarr.h> +#include <yarr/YarrInterpreter.h> +#include <yarr/YarrJIT.h> + +#include "qv4managed_p.h" +#include "qv4engine_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ExecutionEngine; + +struct RegExpCacheKey +{ + RegExpCacheKey(const QString &pattern, bool ignoreCase, bool multiLine) + : pattern(pattern) + , ignoreCase(ignoreCase) + , multiLine(multiLine) + { } + explicit inline RegExpCacheKey(const RegExp *re); + + bool operator==(const RegExpCacheKey &other) const + { return pattern == other.pattern && ignoreCase == other.ignoreCase && multiLine == other.multiLine; } + bool operator!=(const RegExpCacheKey &other) const + { return !operator==(other); } + + QString pattern; + uint ignoreCase : 1; + uint multiLine : 1; +}; + +inline uint qHash(const RegExpCacheKey& key, uint seed = 0) Q_DECL_NOTHROW +{ return qHash(key.pattern, seed); } + +class RegExpCache : public QHash<RegExpCacheKey, RegExp*> +{ +public: + ~RegExpCache(); +}; + +class RegExp : public Managed +{ + Q_MANAGED +public: + static RegExp* create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false); + ~RegExp(); + + QString pattern() const { return m_pattern; } + + bool isValid() const { return m_byteCode.get(); } + + uint match(const QString& string, int start, uint *matchOffsets); + + bool ignoreCase() const { return m_ignoreCase; } + bool multiLine() const { return m_multiLine; } + int captureCount() const { return m_subPatternCount + 1; } + +protected: + static void destroy(Managed *that); + static void markObjects(Managed *that); + static Value get(Managed *m, String *name, bool *hasProperty); + static Value getIndexed(Managed *m, uint index, bool *hasProperty); + static void put(Managed *m, String *name, const Value &value); + static void putIndexed(Managed *m, uint index, const Value &value); + static PropertyAttributes query(const Managed *m, String *name); + static PropertyAttributes queryIndexed(const Managed *m, uint index); + static bool deleteProperty(Managed *, String *); + static bool deleteIndexedProperty(Managed *m, uint index); + static Property *advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attributes); + +private: + friend class RegExpCache; + Q_DISABLE_COPY(RegExp); + RegExp(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline); + + const QString m_pattern; + OwnPtr<JSC::Yarr::BytecodePattern> m_byteCode; +#if ENABLE(YARR_JIT) + JSC::Yarr::YarrCodeBlock m_jitCode; +#endif + RegExpCache *m_cache; + int m_subPatternCount; + const bool m_ignoreCase; + const bool m_multiLine; +}; + +inline RegExpCacheKey::RegExpCacheKey(const RegExp *re) + : pattern(re->pattern()) + , ignoreCase(re->ignoreCase()) + , multiLine(re->multiLine()) +{} + + +} + +QT_END_NAMESPACE + +#endif // QV4REGEXP_H diff --git a/src/qml/qml/v4/qv4regexpobject.cpp b/src/qml/qml/v4/qv4regexpobject.cpp new file mode 100644 index 0000000000..0dc14e5722 --- /dev/null +++ b/src/qml/qml/v4/qv4regexpobject.cpp @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4regexpobject_p.h" +#include "qv4jsir_p.h" +#include "qv4isel_p.h" +#include "qv4objectproto_p.h" +#include "qv4stringobject_p.h" +#include "qv4mm_p.h" + +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsast_p.h> +#include <qv4jsir_p.h> +#include <qv4codegen_p.h> +#include "private/qlocale_tools_p.h" + +#include <QtCore/qmath.h> +#include <QtCore/QDebug> +#include <QtCore/qregexp.h> +#include <cassert> +#include <typeinfo> +#include <iostream> +#include "qv4alloca_p.h" + +QT_BEGIN_NAMESPACE + +Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax); + +using namespace QV4; + +DEFINE_MANAGED_VTABLE(RegExpObject); + +RegExpObject::RegExpObject(ExecutionEngine *engine, RegExp* value, bool global) + : Object(engine) + , value(value) + , global(global) +{ + init(engine); +} + +// Converts a QRegExp to a JS RegExp. +// The conversion is not 100% exact since ECMA regexp and QRegExp +// have different semantics/flags, but we try to do our best. +RegExpObject::RegExpObject(ExecutionEngine *engine, const QRegExp &re) + : Object(engine) + , value(0) + , global(false) +{ + // Convert the pattern to a ECMAScript pattern. + QString pattern = QT_PREPEND_NAMESPACE(qt_regexp_toCanonical)(re.pattern(), re.patternSyntax()); + if (re.isMinimal()) { + QString ecmaPattern; + int len = pattern.length(); + ecmaPattern.reserve(len); + int i = 0; + const QChar *wc = pattern.unicode(); + bool inBracket = false; + while (i < len) { + QChar c = wc[i++]; + ecmaPattern += c; + switch (c.unicode()) { + case '?': + case '+': + case '*': + case '}': + if (!inBracket) + ecmaPattern += QLatin1Char('?'); + break; + case '\\': + if (i < len) + ecmaPattern += wc[i++]; + break; + case '[': + inBracket = true; + break; + case ']': + inBracket = false; + break; + default: + break; + } + } + pattern = ecmaPattern; + } + + value = RegExp::create(engine, pattern, re.caseSensitivity() == Qt::CaseInsensitive, false); + + init(engine); +} + +void RegExpObject::init(ExecutionEngine *engine) +{ + vtbl = &static_vtbl; + type = Type_RegExpObject; + + Property *lastIndexProperty = insertMember(engine->newIdentifier(QStringLiteral("lastIndex")), + Attr_NotEnumerable|Attr_NotConfigurable); + lastIndexProperty->value = Value::fromInt32(0); + if (!this->value) + return; + + QString p = this->value->pattern(); + if (p.isEmpty()) { + p = QStringLiteral("(?:)"); + } else { + // escape certain parts, see ch. 15.10.4 + p.replace('/', QLatin1String("\\/")); + } + + defineReadonlyProperty(engine->newIdentifier(QStringLiteral("source")), Value::fromString(engine->newString(p))); + defineReadonlyProperty(engine->newIdentifier(QStringLiteral("global")), Value::fromBoolean(global)); + defineReadonlyProperty(engine->newIdentifier(QStringLiteral("ignoreCase")), Value::fromBoolean(this->value->ignoreCase())); + defineReadonlyProperty(engine->newIdentifier(QStringLiteral("multiline")), Value::fromBoolean(this->value->multiLine())); +} + + +void RegExpObject::destroy(Managed *that) +{ + static_cast<RegExpObject *>(that)->~RegExpObject(); +} + +void RegExpObject::markObjects(Managed *that) +{ + RegExpObject *re = static_cast<RegExpObject*>(that); + if (re->value) + re->value->mark(); + Object::markObjects(that); +} + +Property *RegExpObject::lastIndexProperty(ExecutionContext *ctx) +{ + assert(0 == internalClass->find(ctx->engine->newIdentifier(QStringLiteral("lastIndex")))); + return &memberData[0]; +} + +// Converts a JS RegExp to a QRegExp. +// The conversion is not 100% exact since ECMA regexp and QRegExp +// have different semantics/flags, but we try to do our best. +QRegExp RegExpObject::toQRegExp() const +{ + Qt::CaseSensitivity caseSensitivity = value->ignoreCase() ? Qt::CaseInsensitive : Qt::CaseSensitive; + return QRegExp(value->pattern(), caseSensitivity, QRegExp::RegExp2); +} + +QString RegExpObject::toString() const +{ + QString result = QChar('/') + source(); + result += QChar('/'); + if (global) + result += QChar('g'); + if (value->ignoreCase()) + result += QChar('i'); + if (value->multiLine()) + result += QChar('m'); + return result; +} + +QString RegExpObject::source() const +{ + return const_cast<RegExpObject *>(this)->get(internalClass->engine->newIdentifier(QStringLiteral("source"))).stringValue()->toQString(); +} + +uint RegExpObject::flags() const +{ + uint f = 0; + if (global) + f |= QV4::RegExpObject::RegExp_Global; + if (value->ignoreCase()) + f |= QV4::RegExpObject::RegExp_IgnoreCase; + if (value->multiLine()) + f |= QV4::RegExpObject::RegExp_Multiline; + return f; +} + +DEFINE_MANAGED_VTABLE(RegExpCtor); + +RegExpCtor::RegExpCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("RegExp"))) +{ + vtbl = &static_vtbl; +} + +Value RegExpCtor::construct(Managed *m, Value *argv, int argc) +{ + Value r = argc > 0 ? argv[0] : Value::undefinedValue(); + Value f = argc > 1 ? argv[1] : Value::undefinedValue(); + ExecutionContext *ctx = m->engine()->current; + if (RegExpObject *re = r.as<RegExpObject>()) { + if (!f.isUndefined()) + ctx->throwTypeError(); + + RegExpObject *o = ctx->engine->newRegExpObject(re->value, re->global); + return Value::fromObject(o); + } + + QString pattern; + if (!r.isUndefined()) + pattern = r.toString(ctx)->toQString(); + + bool global = false; + bool ignoreCase = false; + bool multiLine = false; + if (!f.isUndefined()) { + f = __qmljs_to_string(f, ctx); + QString str = f.stringValue()->toQString(); + for (int i = 0; i < str.length(); ++i) { + if (str.at(i) == QChar('g') && !global) { + global = true; + } else if (str.at(i) == QChar('i') && !ignoreCase) { + ignoreCase = true; + } else if (str.at(i) == QChar('m') && !multiLine) { + multiLine = true; + } else { + ctx->throwSyntaxError(0); + } + } + } + + RegExp* re = RegExp::create(ctx->engine, pattern, ignoreCase, multiLine); + if (!re->isValid()) + ctx->throwSyntaxError(0); + + RegExpObject *o = ctx->engine->newRegExpObject(re, global); + return Value::fromObject(o); +} + +Value RegExpCtor::call(Managed *that, const Value &, Value *argv, int argc) +{ + if (argc > 0 && argv[0].as<RegExpObject>()) { + if (argc == 1 || argv[1].isUndefined()) + return argv[0]; + } + + return construct(that, argv, argc); +} + +void RegExpPrototype::init(ExecutionContext *ctx, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(2)); + defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); + defineDefaultProperty(ctx, QStringLiteral("exec"), method_exec, 1); + defineDefaultProperty(ctx, QStringLiteral("test"), method_test, 1); + defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); + defineDefaultProperty(ctx, QStringLiteral("compile"), method_compile, 2); +} + +Value RegExpPrototype::method_exec(SimpleCallContext *ctx) +{ + RegExpObject *r = ctx->thisObject.as<RegExpObject>(); + if (!r) + ctx->throwTypeError(); + + Value arg = ctx->argument(0); + arg = __qmljs_to_string(arg, ctx); + QString s = arg.stringValue()->toQString(); + + int offset = r->global ? r->lastIndexProperty(ctx)->value.toInt32() : 0; + if (offset < 0 || offset > s.length()) { + r->lastIndexProperty(ctx)->value = Value::fromInt32(0); + return Value::nullValue(); + } + + uint* matchOffsets = (uint*)alloca(r->value->captureCount() * 2 * sizeof(uint)); + int result = r->value->match(s, offset, matchOffsets); + if (result == -1) { + r->lastIndexProperty(ctx)->value = Value::fromInt32(0); + return Value::nullValue(); + } + + // fill in result data + ArrayObject *array = ctx->engine->newArrayObject(); + for (int i = 0; i < r->value->captureCount(); ++i) { + int start = matchOffsets[i * 2]; + int end = matchOffsets[i * 2 + 1]; + Value entry = Value::undefinedValue(); + if (start != -1 && end != -1) + entry = Value::fromString(ctx, s.mid(start, end - start)); + array->push_back(entry); + } + + array->put(ctx, QLatin1String("index"), Value::fromInt32(result)); + array->put(ctx, QLatin1String("input"), arg); + + if (r->global) + r->lastIndexProperty(ctx)->value = Value::fromInt32(matchOffsets[1]); + + return Value::fromObject(array); +} + +Value RegExpPrototype::method_test(SimpleCallContext *ctx) +{ + Value r = method_exec(ctx); + return Value::fromBoolean(!r.isNull()); +} + +Value RegExpPrototype::method_toString(SimpleCallContext *ctx) +{ + RegExpObject *r = ctx->thisObject.as<RegExpObject>(); + if (!r) + ctx->throwTypeError(); + + return Value::fromString(ctx, r->toString()); +} + +Value RegExpPrototype::method_compile(SimpleCallContext *ctx) +{ + RegExpObject *r = ctx->thisObject.as<RegExpObject>(); + if (!r) + ctx->throwTypeError(); + + RegExpObject *re = ctx->engine->regExpCtor.asFunctionObject()->construct(ctx->arguments, ctx->argumentCount).as<RegExpObject>(); + + r->value = re->value; + r->global = re->global; + return Value::undefinedValue(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4regexpobject_p.h b/src/qml/qml/v4/qv4regexpobject_p.h new file mode 100644 index 0000000000..0b9b7122a9 --- /dev/null +++ b/src/qml/qml/v4/qv4regexpobject_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4REGEXPOBJECT_H +#define QV4REGEXPOBJECT_H + +#include "qv4runtime_p.h" +#include "qv4engine_p.h" +#include "qv4context_p.h" +#include "qv4functionobject_p.h" +#include "qv4string_p.h" +#include "qv4codegen_p.h" +#include "qv4isel_p.h" +#include "qv4managed_p.h" +#include "qv4property_p.h" +#include "qv4objectiterator_p.h" +#include "qv4regexp_p.h" + +#include <QtCore/QString> +#include <QtCore/QHash> +#include <QtCore/QScopedPointer> +#include <cstdio> +#include <cassert> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct RegExp; + +struct RegExpObject: Object { + Q_MANAGED + // needs to be compatible with the flags in qv4jsir_p.h + enum Flags { + RegExp_Global = 0x01, + RegExp_IgnoreCase = 0x02, + RegExp_Multiline = 0x04 + }; + + RegExp* value; + Property *lastIndexProperty(ExecutionContext *ctx); + bool global; + RegExpObject(ExecutionEngine *engine, RegExp* value, bool global); + RegExpObject(ExecutionEngine *engine, const QRegExp &re); + ~RegExpObject() {} + + void init(ExecutionEngine *engine); + + QRegExp toQRegExp() const; + QString toString() const; + QString source() const; + uint flags() const; + +protected: + static void destroy(Managed *that); + static void markObjects(Managed *that); +}; + + +struct RegExpCtor: FunctionObject +{ + RegExpCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct RegExpPrototype: RegExpObject +{ + RegExpPrototype(ExecutionEngine* engine): RegExpObject(engine, RegExp::create(engine, QString()), false) {} + void init(ExecutionContext *ctx, const Value &ctor); + + static Value method_exec(SimpleCallContext *ctx); + static Value method_test(SimpleCallContext *ctx); + static Value method_toString(SimpleCallContext *ctx); + static Value method_compile(SimpleCallContext *ctx); +}; + +} + +QT_END_NAMESPACE + +#endif // QMLJS_OBJECTS_H diff --git a/src/qml/qml/v4/qv4runtime.cpp b/src/qml/qml/v4/qv4runtime.cpp new file mode 100644 index 0000000000..deccb3d4ea --- /dev/null +++ b/src/qml/qml/v4/qv4runtime.cpp @@ -0,0 +1,1269 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4global_p.h" +#include "qv4runtime_p.h" +#include "qv4object_p.h" +#include "qv4jsir_p.h" +#include "qv4objectproto_p.h" +#include "qv4globalobject_p.h" +#include "qv4stringobject_p.h" +#include "qv4lookup_p.h" +#include "qv4function_p.h" +#include "qv4exception_p.h" +#include "private/qlocale_tools_p.h" + +#include <QtCore/qmath.h> +#include <QtCore/qnumeric.h> +#include <QtCore/QDebug> +#include <cstdio> +#include <cassert> +#include <typeinfo> +#include <stdlib.h> + +#include "../../../3rdparty/double-conversion/double-conversion.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +void __qmljs_numberToString(QString *result, double num, int radix) +{ + Q_ASSERT(result); + + if (std::isnan(num)) { + *result = QStringLiteral("NaN"); + return; + } else if (qIsInf(num)) { + *result = QLatin1String(num < 0 ? "-Infinity" : "Infinity"); + return; + } + + if (radix == 10) { + char str[100]; + double_conversion::StringBuilder builder(str, sizeof(str)); + double_conversion::DoubleToStringConverter::EcmaScriptConverter().ToShortest(num, &builder); + *result = QString::fromLatin1(builder.Finalize()); + return; + } + + result->clear(); + bool negative = false; + + if (num < 0) { + negative = true; + num = -num; + } + + double frac = num - ::floor(num); + num = Value::toInteger(num); + + do { + char c = (char)::fmod(num, radix); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + result->prepend(QLatin1Char(c)); + num = ::floor(num / radix); + } while (num != 0); + + if (frac != 0) { + result->append(QLatin1Char('.')); + do { + frac = frac * radix; + char c = (char)::floor(frac); + c = (c < 10) ? (c + '0') : (c - 10 + 'a'); + result->append(QLatin1Char(c)); + frac = frac - ::floor(frac); + } while (frac != 0); + } + + if (negative) + result->prepend(QLatin1Char('-')); +} + +void __qmljs_init_closure(ExecutionContext *ctx, Value *result, Function *clos) +{ + assert(clos); + *result = Value::fromObject(ctx->engine->newScriptFunction(ctx, clos)); +} + +Function *__qmljs_register_function(ExecutionContext *ctx, String *name, + bool hasDirectEval, + bool usesArgumentsObject, bool isStrict, + bool hasNestedFunctions, + String **formals, unsigned formalCount, + String **locals, unsigned localCount) +{ + Function *f = ctx->engine->newFunction(name ? name->toQString() : QString()); + + f->hasDirectEval = hasDirectEval; + f->usesArgumentsObject = usesArgumentsObject; + f->isStrict = isStrict; + f->hasNestedFunctions = hasNestedFunctions; + + for (unsigned i = 0; i < formalCount; ++i) + if (formals[i]) + f->formals.append(formals[i]); + for (unsigned i = 0; i < localCount; ++i) + if (locals[i]) + f->locals.append(locals[i]); + + return f; +} + +void __qmljs_delete_subscript(ExecutionContext *ctx, Value *result, const Value &base, const Value &index) +{ + if (Object *o = base.asObject()) { + uint n = index.asArrayIndex(); + if (n < UINT_MAX) { + Value res = Value::fromBoolean(o->deleteIndexedProperty(n)); + if (result) + *result = res; + return; + } + } + + String *name = index.toString(ctx); + __qmljs_delete_member(ctx, result, base, name); +} + +void __qmljs_delete_member(ExecutionContext *ctx, Value *result, const Value &base, String *name) +{ + Object *obj = base.toObject(ctx); + Value res = Value::fromBoolean(obj->deleteProperty(name)); + if (result) + *result = res; +} + +void __qmljs_delete_name(ExecutionContext *ctx, Value *result, String *name) +{ + Value res = Value::fromBoolean(ctx->deleteProperty(name)); + if (result) + *result = res; +} + +void __qmljs_add_helper(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) +{ + Value pleft = __qmljs_to_primitive(left, PREFERREDTYPE_HINT); + Value pright = __qmljs_to_primitive(right, PREFERREDTYPE_HINT); + if (pleft.isString() || pright.isString()) { + if (!pleft.isString()) + pleft = __qmljs_to_string(pleft, ctx); + if (!pright.isString()) + pright = __qmljs_to_string(pright, ctx); + String *string = __qmljs_string_concat(ctx, pleft.stringValue(), pright.stringValue()); + *result = Value::fromString(string); + return; + } + double x = __qmljs_to_number(pleft); + double y = __qmljs_to_number(pright); + *result = Value::fromDouble(x + y); +} + +void __qmljs_instanceof(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) +{ + Object *o = right.asObject(); + if (!o) + ctx->throwTypeError(); + + bool r = o->hasInstance(left); + *result = Value::fromBoolean(r); +} + +void __qmljs_in(ExecutionContext *ctx, Value *result, const Value &left, const Value &right) +{ + if (!right.isObject()) + ctx->throwTypeError(); + String *s = left.toString(ctx); + bool r = right.objectValue()->__hasProperty__(s); + *result = Value::fromBoolean(r); +} + +void __qmljs_inplace_bit_and_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_bit_and); +} + +void __qmljs_inplace_bit_or_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_bit_or); +} + +void __qmljs_inplace_bit_xor_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_bit_xor); +} + +void __qmljs_inplace_add_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_add); +} + +void __qmljs_inplace_sub_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_sub); +} + +void __qmljs_inplace_mul_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_mul); +} + +void __qmljs_inplace_div_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_div); +} + +void __qmljs_inplace_mod_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_mod); +} + +void __qmljs_inplace_shl_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_shl); +} + +void __qmljs_inplace_shr_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_shr); +} + +void __qmljs_inplace_ushr_name(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->inplaceBitOp(name, value, __qmljs_ushr); +} + +void __qmljs_inplace_bit_and_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_bit_and, index, rhs); +} + +void __qmljs_inplace_bit_or_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_bit_or, index, rhs); +} + +void __qmljs_inplace_bit_xor_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_bit_xor, index, rhs); +} + +void __qmljs_inplace_add_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_add, index, rhs); +} + +void __qmljs_inplace_sub_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_sub, index, rhs); +} + +void __qmljs_inplace_mul_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_mul, index, rhs); +} + +void __qmljs_inplace_div_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_div, index, rhs); +} + +void __qmljs_inplace_mod_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_mod, index, rhs); +} + +void __qmljs_inplace_shl_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_shl, index, rhs); +} + +void __qmljs_inplace_shr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_shr, index, rhs); +} + +void __qmljs_inplace_ushr_element(ExecutionContext *ctx, const Value &base, const Value &index, const Value &rhs) +{ + Object *obj = base.toObject(ctx); + obj->inplaceBinOp(ctx, __qmljs_ushr, index, rhs); +} + +void __qmljs_inplace_bit_and_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_bit_and, name, rhs); +} + +void __qmljs_inplace_bit_or_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_bit_or, name, rhs); +} + +void __qmljs_inplace_bit_xor_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_bit_xor, name, rhs); +} + +void __qmljs_inplace_add_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_add, name, rhs); +} + +void __qmljs_inplace_sub_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_sub, name, rhs); +} + +void __qmljs_inplace_mul_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_mul, name, rhs); +} + +void __qmljs_inplace_div_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_div, name, rhs); +} + +void __qmljs_inplace_mod_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_mod, name, rhs); +} + +void __qmljs_inplace_shl_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_shl, name, rhs); +} + +void __qmljs_inplace_shr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_shr, name, rhs); +} + +void __qmljs_inplace_ushr_member(ExecutionContext *ctx, const Value &base, String *name, const Value &rhs) +{ + Object *o = base.toObject(ctx); + o->inplaceBinOp(ctx, __qmljs_ushr, name, rhs); +} + +double __qmljs_string_to_number(const QString &string) +{ + QString s = string.trimmed(); + if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X"))) + return s.toLong(0, 16); + bool ok; + QByteArray ba = s.toLatin1(); + const char *begin = ba.constData(); + const char *end = 0; + double d = qstrtod(begin, &end, &ok); + if (end - begin != ba.size()) { + if (ba == "Infinity" || ba == "+Infinity") + d = Q_INFINITY; + else if (ba == "-Infinity") + d = -Q_INFINITY; + else + d = std::numeric_limits<double>::quiet_NaN(); + } + return d; +} + +Value __qmljs_string_from_number(ExecutionContext *ctx, double number) +{ + QString qstr; + __qmljs_numberToString(&qstr, number, 10); + String *string = ctx->engine->newString(qstr); + return Value::fromString(string); +} + +String *__qmljs_string_concat(ExecutionContext *ctx, String *first, String *second) +{ + const QString &a = first->toQString(); + const QString &b = second->toQString(); + QString newStr(a.length() + b.length(), Qt::Uninitialized); + QChar *data = newStr.data(); + memcpy(data, a.constData(), a.length()*sizeof(QChar)); + data += a.length(); + memcpy(data, b.constData(), b.length()*sizeof(QChar)); + + return ctx->engine->newString(newStr); +} + +Value __qmljs_object_default_value(Object *object, int typeHint) +{ + if (typeHint == PREFERREDTYPE_HINT) { + if (object->asDateObject()) + typeHint = STRING_HINT; + else + typeHint = NUMBER_HINT; + } + + ExecutionEngine *engine = object->internalClass->engine; + String *meth1 = engine->newString("toString"); + String *meth2 = engine->newString("valueOf"); + + if (typeHint == NUMBER_HINT) + qSwap(meth1, meth2); + + ExecutionContext *ctx = engine->current; + + Value conv = object->get(meth1); + if (FunctionObject *o = conv.asFunctionObject()) { + Value r = o->call(Value::fromObject(object), 0, 0); + if (r.isPrimitive()) + return r; + } + + conv = object->get(meth2); + if (FunctionObject *o = conv.asFunctionObject()) { + Value r = o->call(Value::fromObject(object), 0, 0); + if (r.isPrimitive()) + return r; + } + + ctx->throwTypeError(); + return Value::undefinedValue(); +} + +Bool __qmljs_to_boolean(const Value &value) +{ + return value.toBoolean(); +} + + +Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value) +{ + assert(!value.isObject()); + switch (value.type()) { + case Value::Undefined_Type: + case Value::Null_Type: + ctx->throwTypeError(); + case Value::Boolean_Type: + return ctx->engine->newBooleanObject(value); + case Value::String_Type: + return ctx->engine->newStringObject(value); + break; + case Value::Object_Type: + Q_UNREACHABLE(); + case Value::Integer_Type: + default: // double + return ctx->engine->newNumberObject(value); + } +} + +String *__qmljs_convert_to_string(ExecutionContext *ctx, const Value &value) +{ + switch (value.type()) { + case Value::Undefined_Type: + return ctx->engine->id_undefined; + case Value::Null_Type: + return ctx->engine->id_null; + case Value::Boolean_Type: + if (value.booleanValue()) + return ctx->engine->id_true; + else + return ctx->engine->id_false; + case Value::String_Type: + return value.stringValue(); + case Value::Object_Type: { + Value prim = __qmljs_to_primitive(value, STRING_HINT); + if (prim.isPrimitive()) + return __qmljs_convert_to_string(ctx, prim); + else + ctx->throwTypeError(); + } + case Value::Integer_Type: + return __qmljs_string_from_number(ctx, value.int_32).stringValue(); + default: // double + return __qmljs_string_from_number(ctx, value.doubleValue()).stringValue(); + } // switch +} + +void __qmljs_set_property(ExecutionContext *ctx, const Value &object, String *name, const Value &value) +{ + Object *o = object.toObject(ctx); + o->put(name, value); +} + +void __qmljs_get_element(ExecutionContext *ctx, Value *result, const Value &object, const Value &index) +{ + uint idx = index.asArrayIndex(); + + Object *o = object.asObject(); + if (!o) { + if (idx < UINT_MAX) { + if (String *str = object.asString()) { + if (idx >= (uint)str->toQString().length()) { + if (result) + *result = Value::undefinedValue(); + return; + } + const QString s = str->toQString().mid(idx, 1); + if (result) + *result = Value::fromString(ctx, s); + return; + } + } + + if (object.isNull() || object.isUndefined()) { + QString message = QStringLiteral("Cannot read property '%1' of %2").arg(index.toQString()).arg(object.toQString()); + ctx->throwTypeError(message); + } + + o = __qmljs_convert_to_object(ctx, object); + } + + if (idx < UINT_MAX) { + uint pidx = o->propertyIndexFromArrayIndex(idx); + if (pidx < UINT_MAX) { + if (!o->arrayAttributes || o->arrayAttributes[pidx].isData()) { + if (result) + *result = o->arrayData[pidx].value; + return; + } + } + + Value res = o->getIndexed(idx); + if (result) + *result = res; + return; + } + + String *name = index.toString(ctx); + Value res = o->get(name); + if (result) + *result = res; +} + +void __qmljs_set_element(ExecutionContext *ctx, const Value &object, const Value &index, const Value &value) +{ + Object *o = object.toObject(ctx); + + uint idx = index.asArrayIndex(); + if (idx < UINT_MAX) { + uint pidx = o->propertyIndexFromArrayIndex(idx); + if (pidx < UINT_MAX) { + if (o->arrayAttributes && !o->arrayAttributes[pidx].isEmpty() && !o->arrayAttributes[pidx].isWritable()) { + if (ctx->strictMode) + ctx->throwTypeError(); + return; + } + + Property *p = o->arrayData + pidx; + if (!o->arrayAttributes || o->arrayAttributes[pidx].isData()) { + p->value = value; + return; + } + + if (o->arrayAttributes[pidx].isAccessor()) { + FunctionObject *setter = p->setter(); + if (!setter) { + if (ctx->strictMode) + ctx->throwTypeError(); + return; + } + + Value args[1]; + args[0] = value; + setter->call(Value::fromObject(o), args, 1); + return; + } + } + o->putIndexed(idx, value); + return; + } + + String *name = index.toString(ctx); + o->put(name, value); +} + +void __qmljs_foreach_iterator_object(ExecutionContext *ctx, Value *result, const Value &in) +{ + Object *o = 0; + if (!in.isNull() && !in.isUndefined()) + o = in.toObject(ctx); + Object *it = ctx->engine->newForEachIteratorObject(ctx, o); + *result = Value::fromObject(it); +} + +void __qmljs_foreach_next_property_name(Value *result, const Value &foreach_iterator) +{ + assert(foreach_iterator.isObject()); + + ForEachIteratorObject *it = static_cast<ForEachIteratorObject *>(foreach_iterator.objectValue()); + assert(it->as<ForEachIteratorObject>()); + + *result = it->nextPropertyName(); +} + + +void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, const Value &value) +{ + ctx->setProperty(name, value); +} + +void __qmljs_get_property(ExecutionContext *ctx, Value *result, const Value &object, String *name) +{ + Value res; + Managed *m = object.asManaged(); + if (m) { + res = m->get(name); + } else { + if (object.isNull() || object.isUndefined()) { + QString message = QStringLiteral("Cannot read property '%1' of %2").arg(name->toQString()).arg(object.toQString()); + ctx->throwTypeError(message); + } + + m = __qmljs_convert_to_object(ctx, object); + res = m->get(name); + } + if (result) + *result = res; +} + +void __qmljs_get_activation_property(ExecutionContext *ctx, Value *result, String *name) +{ + *result = ctx->getProperty(name); +} + +uint __qmljs_equal(const Value &x, const Value &y) +{ + if (x.type() == y.type()) { + switch (x.type()) { + case Value::Undefined_Type: + return true; + case Value::Null_Type: + return true; + case Value::Boolean_Type: + return x.booleanValue() == y.booleanValue(); + break; + case Value::Integer_Type: + return x.integerValue() == y.integerValue(); + case Value::String_Type: + return x.stringValue()->isEqualTo(y.stringValue()); + case Value::Object_Type: + if (x.objectValue() == y.objectValue()) + return true; + return x.objectValue()->isEqualTo(y.objectValue()); + default: // double + return x.doubleValue() == y.doubleValue(); + } + // unreachable + } else { + if (x.isNumber() && y.isNumber()) + return x.asDouble() == y.asDouble(); + if (x.isNull() && y.isUndefined()) { + return true; + } else if (x.isUndefined() && y.isNull()) { + return true; + } else if (x.isNumber() && y.isString()) { + Value ny = Value::fromDouble(__qmljs_to_number(y)); + return __qmljs_equal(x, ny); + } else if (x.isString() && y.isNumber()) { + Value nx = Value::fromDouble(__qmljs_to_number(x)); + return __qmljs_equal(nx, y); + } else if (x.isBoolean()) { + Value nx = Value::fromDouble((double) x.booleanValue()); + return __qmljs_equal(nx, y); + } else if (y.isBoolean()) { + Value ny = Value::fromDouble((double) y.booleanValue()); + return __qmljs_equal(x, ny); + } else if ((x.isNumber() || x.isString()) && y.isObject()) { + Value py = __qmljs_to_primitive(y, PREFERREDTYPE_HINT); + return __qmljs_equal(x, py); + } else if (x.isObject() && (y.isNumber() || y.isString())) { + Value px = __qmljs_to_primitive(x, PREFERREDTYPE_HINT); + return __qmljs_equal(px, y); + } + } + + return false; +} + +Bool __qmljs_strict_equal(const Value &x, const Value &y) +{ + TRACE2(x, y); + + if (x.rawValue() == y.rawValue()) { + if (x.isDouble()) + return !std::isnan(x.doubleValue()); + return true; + } + if (x.isNumber() && y.isNumber()) + return x.asDouble() == y.asDouble(); + if (x.isString() && y.isString()) + return x.stringValue()->isEqualTo(y.stringValue()); + return false; +} + + +void __qmljs_call_global_lookup(ExecutionContext *context, Value *result, uint index, Value *args, int argc) +{ + Lookup *l = context->lookups + index; + Value v; + l->globalGetter(l, context, &v); + FunctionObject *o = v.asFunctionObject(); + if (!o) + context->throwTypeError(); + + Value thisObject = Value::undefinedValue(); + + if (o == context->engine->evalFunction && l->name->isEqualTo(context->engine->id_eval)) { + Value res = static_cast<EvalFunction *>(o)->evalCall(thisObject, args, argc, true); + if (result) + *result = res; + return; + } + + Value res = o->call(thisObject, args, argc); + if (result) + *result = res; +} + + +void __qmljs_call_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) +{ + Object *base; + Value func = context->getPropertyAndBase(name, &base); + FunctionObject *o = func.asFunctionObject(); + if (!o) { + QString objectAsString = QStringLiteral("[null]"); + if (base) + objectAsString = Value::fromObject(base).toQString(); + QString msg = QStringLiteral("Property '%1' of object %2 is not a function").arg(name->toQString()).arg(objectAsString); + context->throwTypeError(msg); + } + + Value thisObject = base ? Value::fromObject(base) : Value::undefinedValue(); + + if (o == context->engine->evalFunction && name->isEqualTo(context->engine->id_eval)) { + Value res = static_cast<EvalFunction *>(o)->evalCall(thisObject, args, argc, true); + if (result) + *result = res; + return; + } + + Value res = o->call(thisObject, args, argc); + if (result) + *result = res; +} + +void __qmljs_call_property(ExecutionContext *context, Value *result, const Value &thatObject, String *name, Value *args, int argc) +{ + Value thisObject = thatObject; + Managed *baseObject = thisObject.asManaged(); + if (!baseObject) { + if (thisObject.isNull() || thisObject.isUndefined()) { + QString message = QStringLiteral("Cannot call method '%1' of %2").arg(name->toQString()).arg(thisObject.toQString()); + context->throwTypeError(message); + } + + baseObject = __qmljs_convert_to_object(context, thisObject); + thisObject = Value::fromObject(static_cast<Object *>(baseObject)); + } + + Value func = baseObject->get(name); + FunctionObject *o = func.asFunctionObject(); + if (!o) { + QString error = QString("Property '%1' of object %2 is not a function").arg(name->toQString(), thisObject.toQString()); + context->throwTypeError(error); + } + + Value res = o->call(thisObject, args, argc); + if (result) + *result = res; +} + +void __qmljs_call_property_lookup(ExecutionContext *context, Value *result, const Value &thisObject, uint index, Value *args, int argc) +{ + Lookup *l = context->lookups + index; + + Object *baseObject; + if (thisObject.isObject()) + baseObject = thisObject.objectValue(); + else if (thisObject.isString()) + baseObject = context->engine->stringPrototype; + else + baseObject = __qmljs_convert_to_object(context, thisObject); + + PropertyAttributes attrs; + Property *p = l->lookup(baseObject, &attrs); + if (!p) + context->throwTypeError(); + Value func = attrs.isData() ? p->value : baseObject->getValue(p, attrs); + FunctionObject *o = func.asFunctionObject(); + if (!o) + context->throwTypeError(); + + Value res = o->call(thisObject, args, argc); + if (result) + *result = res; +} + +void __qmljs_call_element(ExecutionContext *context, Value *result, const Value &that, const Value &index, Value *args, int argc) +{ + Object *baseObject = that.toObject(context); + Value thisObject = Value::fromObject(baseObject); + + Value func = baseObject->get(index.toString(context)); + Object *o = func.asObject(); + if (!o) + context->throwTypeError(); + + Value res = o->call(thisObject, args, argc); + if (result) + *result = res; +} + +void __qmljs_call_value(ExecutionContext *context, Value *result, const Value *thisObject, const Value &func, Value *args, int argc) +{ + Object *o = func.asObject(); + if (!o) + context->throwTypeError(); + Value res = o->call(thisObject ? *thisObject : Value::undefinedValue(), args, argc); + if (result) + *result = res; +} + + +void __qmljs_construct_global_lookup(ExecutionContext *context, Value *result, uint index, Value *args, int argc) +{ + Lookup *l = context->lookups + index; + Value func; + l->globalGetter(l, context, &func); + + if (Object *f = func.asObject()) { + Value res = f->construct(args, argc); + if (result) + *result = res; + return; + } + + context->throwTypeError(); +} + + +void __qmljs_construct_activation_property(ExecutionContext *context, Value *result, String *name, Value *args, int argc) +{ + Value func = context->getProperty(name); + __qmljs_construct_value(context, result, func, args, argc); +} + +void __qmljs_construct_value(ExecutionContext *context, Value *result, const Value &func, Value *args, int argc) +{ + if (Object *f = func.asObject()) { + Value res = f->construct(args, argc); + if (result) + *result = res; + return; + } + + context->throwTypeError(); +} + +void __qmljs_construct_property(ExecutionContext *context, Value *result, const Value &base, String *name, Value *args, int argc) +{ + Object *thisObject = base.toObject(context); + + Value func = thisObject->get(name); + if (Object *f = func.asObject()) { + Value res = f->construct(args, argc); + if (result) + *result = res; + return; + } + + context->throwTypeError(); +} + +void __qmljs_throw(ExecutionContext *context, const Value &value) +{ + Exception::throwException(context, value); +} + +void __qmljs_builtin_typeof(ExecutionContext *ctx, Value *result, const Value &value) +{ + if (!result) + return; + String *res = 0; + switch (value.type()) { + case Value::Undefined_Type: + res = ctx->engine->id_undefined; + break; + case Value::Null_Type: + res = ctx->engine->id_object; + break; + case Value::Boolean_Type: + res = ctx->engine->id_boolean; + break; + case Value::String_Type: + res = ctx->engine->id_string; + break; + case Value::Object_Type: + if (value.objectValue()->asFunctionObject()) + res = ctx->engine->id_function; + else + res = ctx->engine->id_object; // ### implementation-defined + break; + default: + res = ctx->engine->id_number; + break; + } + *result = Value::fromString(res); +} + +void __qmljs_builtin_typeof_name(ExecutionContext *context, Value *result, String *name) +{ + Value res; + __qmljs_builtin_typeof(context, &res, context->getPropertyNoThrow(name)); + if (result) + *result = res; +} + +void __qmljs_builtin_typeof_member(ExecutionContext *context, Value *result, const Value &base, String *name) +{ + Object *obj = base.toObject(context); + Value res; + __qmljs_builtin_typeof(context, &res, obj->get(name)); + if (result) + *result = res; +} + +void __qmljs_builtin_typeof_element(ExecutionContext *context, Value *result, const Value &base, const Value &index) +{ + String *name = index.toString(context); + Object *obj = base.toObject(context); + Value res; + __qmljs_builtin_typeof(context, &res, obj->get(name)); + if (result) + *result = res; +} + +void __qmljs_builtin_post_increment(Value *result, Value *val) +{ + if (val->isInteger() && val->integerValue() < INT_MAX) { + if (result) + *result = *val; + val->int_32 += 1; + return; + } + + double d = __qmljs_to_number(*val); + *val = Value::fromDouble(d + 1); + if (result) + *result = Value::fromDouble(d); +} + +void __qmljs_builtin_post_increment_name(ExecutionContext *context, Value *result, String *name) +{ + Value v = context->getProperty(name); + + if (v.isInteger() && v.integerValue() < INT_MAX) { + if (result) + *result = v; + v.int_32 += 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d + 1); + } + + context->setProperty(name, v); +} + +void __qmljs_builtin_post_increment_member(ExecutionContext *context, Value *result, const Value &base, String *name) +{ + Object *o = base.toObject(context); + + Value v = o->get(name); + + if (v.isInteger() && v.integerValue() < INT_MAX) { + if (result) + *result = v; + v.int_32 += 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d + 1); + } + + o->put(name, v); +} + +void __qmljs_builtin_post_increment_element(ExecutionContext *context, Value *result, const Value &base, const Value *index) +{ + Object *o = base.toObject(context); + + uint idx = index->asArrayIndex(); + + if (idx == UINT_MAX) { + String *s = index->toString(context); + return __qmljs_builtin_post_increment_member(context, result, base, s); + } + + Value v = o->getIndexed(idx); + + if (v.isInteger() && v.integerValue() < INT_MAX) { + if (result) + *result = v; + v.int_32 += 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d + 1); + } + + o->putIndexed(idx, v); +} + +void __qmljs_builtin_post_decrement(Value *result, Value *val) +{ + if (val->isInteger() && val->integerValue() > INT_MIN) { + if (result) + *result = *val; + val->int_32 -= 1; + return; + } + + double d = __qmljs_to_number(*val); + *val = Value::fromDouble(d - 1); + if (result) + *result = Value::fromDouble(d); +} + +void __qmljs_builtin_post_decrement_name(ExecutionContext *context, Value *result, String *name) +{ + Value v = context->getProperty(name); + + if (v.isInteger() && v.integerValue() > INT_MIN) { + if (result) + *result = v; + v.int_32 -= 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d - 1); + } + + context->setProperty(name, v); +} + +void __qmljs_builtin_post_decrement_member(ExecutionContext *context, Value *result, const Value &base, String *name) +{ + Object *o = base.toObject(context); + + Value v = o->get(name); + + if (v.isInteger() && v.integerValue() > INT_MIN) { + if (result) + *result = v; + v.int_32 -= 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d - 1); + } + + o->put(name, v); +} + +void __qmljs_builtin_post_decrement_element(ExecutionContext *context, Value *result, const Value &base, const Value &index) +{ + Object *o = base.toObject(context); + + uint idx = index.asArrayIndex(); + + if (idx == UINT_MAX) { + String *s = index.toString(context); + return __qmljs_builtin_post_decrement_member(context, result, base, s); + } + + Value v = o->getIndexed(idx); + + if (v.isInteger() && v.integerValue() > INT_MIN) { + if (result) + *result = v; + v.int_32 -= 1; + } else { + double d = __qmljs_to_number(v); + if (result) + *result = Value::fromDouble(d); + v = Value::fromDouble(d - 1); + } + + o->putIndexed(idx, v); +} + +ExecutionContext *__qmljs_builtin_push_with_scope(const Value &o, ExecutionContext *ctx) +{ + Object *obj = o.toObject(ctx); + return ctx->engine->newWithContext(obj); +} + +ExecutionContext *__qmljs_builtin_push_catch_scope(String *exceptionVarName, const Value &exceptionValue, ExecutionContext *ctx) +{ + return ctx->engine->newCatchContext(exceptionVarName, exceptionValue); +} + +ExecutionContext *__qmljs_builtin_pop_scope(ExecutionContext *ctx) +{ + return ctx->engine->popContext(); +} + +void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) +{ + ctx->createMutableBinding(name, deletable); +} + +void __qmljs_builtin_define_property(ExecutionContext *ctx, const Value &object, String *name, Value *val) +{ + Object *o = object.asObject(); + assert(o); + + uint idx = name->asArrayIndex(); + Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx) : o->insertMember(name, Attr_Data); + pd->value = val ? *val : Value::undefinedValue(); +} + +void __qmljs_builtin_define_array(ExecutionContext *ctx, Value *array, Value *values, uint length) +{ + ArrayObject *a = ctx->engine->newArrayObject(); + + // ### FIXME: We need to allocate the array data to avoid crashes other places + // This should rather be done when required + a->arrayReserve(length); + if (length) { + a->arrayDataLen = length; + Property *pd = a->arrayData; + for (uint i = 0; i < length; ++i) { + if (values[i].isEmpty()) { + a->ensureArrayAttributes(); + pd->value = Value::undefinedValue(); + a->arrayAttributes[i].clear(); + } else { + pd->value = values[i]; + } + ++pd; + } + a->setArrayLengthUnchecked(length); + } + *array = Value::fromObject(a); +} + +void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const Value &object, String *name, const Value *getter, const Value *setter) +{ + Object *o = object.asObject(); + assert(o); + + uint idx = name->asArrayIndex(); + Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx, Attr_Accessor) : o->insertMember(name, Attr_Accessor); + pd->setGetter(getter ? getter->asFunctionObject() : 0); + pd->setSetter(setter ? setter->asFunctionObject() : 0); +} + +void __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value *args, QV4::InternalClass *klass) +{ + Object *o = ctx->engine->newObject(klass); + + for (int i = 0; i < klass->size; ++i) { + if (klass->propertyData[i].isData()) + o->memberData[i].value = *args++; + else { + o->memberData[i].setGetter(args->asFunctionObject()); + args++; + o->memberData[i].setSetter(args->asFunctionObject()); + args++; + } + } + + *result = Value::fromObject(o); +} + +void __qmljs_increment(Value *result, const Value &value) +{ + TRACE1(value); + + if (value.isInteger()) + *result = Value::fromInt32(value.integerValue() + 1); + else { + double d = __qmljs_to_number(value); + *result = Value::fromDouble(d + 1); + } +} + +void __qmljs_decrement(Value *result, const Value &value) +{ + TRACE1(value); + + if (value.isInteger()) + *result = Value::fromInt32(value.integerValue() - 1); + else { + double d = __qmljs_to_number(value); + *result = Value::fromDouble(d - 1); + } +} + +} // namespace QV4 + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4runtime_p.h b/src/qml/qml/v4/qv4runtime_p.h new file mode 100644 index 0000000000..e6e6e0f3a0 --- /dev/null +++ b/src/qml/qml/v4/qv4runtime_p.h @@ -0,0 +1,717 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_RUNTIME_H +#define QMLJS_RUNTIME_H + +#include "qv4global_p.h" +#include "qv4value_p.h" +#include "qv4math_p.h" + + +#include <QtCore/QString> +#include <QtCore/qnumeric.h> +#include <QtCore/QDebug> +#include <QtCore/qurl.h> + +#include <cmath> +#include <cassert> +#include <limits> + +//#include <wtf/MathExtras.h> + +#ifdef DO_TRACE_INSTR +# define TRACE1(x) fprintf(stderr, " %s\n", __FUNCTION__); +# define TRACE2(x, y) fprintf(stderr, " %s\n", __FUNCTION__); +#else +# define TRACE1(x) +# define TRACE2(x, y) +#endif // TRACE1 + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +enum TypeHint { + PREFERREDTYPE_HINT, + NUMBER_HINT, + STRING_HINT +}; + +struct Function; +struct Object; +struct String; +struct ExecutionContext; +struct FunctionObject; +struct BooleanObject; +struct NumberObject; +struct StringObject; +struct DateObject; +struct RegExpObject; +struct ArrayObject; +struct ErrorObject; +struct ExecutionEngine; +struct InternalClass; + +// context +void __qmljs_call_activation_property(QV4::ExecutionContext *, QV4::Value *result, QV4::String *name, QV4::Value *args, int argc); +void __qmljs_call_property(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &that, QV4::String *name, QV4::Value *args, int argc); +void __qmljs_call_property_lookup(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &thisObject, uint index, QV4::Value *args, int argc); +void __qmljs_call_element(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &that, const QV4::Value &index, QV4::Value *args, int argc); +void __qmljs_call_value(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value *thisObject, const QV4::Value &func, QV4::Value *args, int argc); + +void __qmljs_construct_activation_property(QV4::ExecutionContext *, QV4::Value *result, QV4::String *name, QV4::Value *args, int argc); +void __qmljs_construct_property(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, QV4::String *name, QV4::Value *args, int argc); +void __qmljs_construct_value(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &func, QV4::Value *args, int argc); + +void __qmljs_builtin_typeof(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &val); +void __qmljs_builtin_typeof_name(QV4::ExecutionContext *context, QV4::Value* result, QV4::String *name); +void __qmljs_builtin_typeof_member(QV4::ExecutionContext* context, QV4::Value* result, const QV4::Value &base, QV4::String *name); +void __qmljs_builtin_typeof_element(QV4::ExecutionContext* context, QV4::Value *result, const QV4::Value &base, const QV4::Value &index); + +void __qmljs_builtin_post_increment(QV4::Value *result, QV4::Value *val); +void __qmljs_builtin_post_increment_name(QV4::ExecutionContext *context, QV4::Value *result, QV4::String *name); +void __qmljs_builtin_post_increment_member(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, QV4::String *name); +void __qmljs_builtin_post_increment_element(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, const QV4::Value *index); + +void __qmljs_builtin_post_decrement(QV4::Value *result, QV4::Value *val); +void __qmljs_builtin_post_decrement_name(QV4::ExecutionContext *context, QV4::Value *result, QV4::String *name); +void __qmljs_builtin_post_decrement_member(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, QV4::String *name); +void __qmljs_builtin_post_decrement_element(QV4::ExecutionContext *context, QV4::Value *result, const QV4::Value &base, const QV4::Value &index); + +void Q_NORETURN __qmljs_builtin_rethrow(QV4::ExecutionContext *context); +QV4::ExecutionContext *__qmljs_builtin_push_with_scope(const QV4::Value &o, QV4::ExecutionContext *ctx); +QV4::ExecutionContext *__qmljs_builtin_push_catch_scope(QV4::String *exceptionVarName, const QV4::Value &exceptionValue, QV4::ExecutionContext *ctx); +QV4::ExecutionContext *__qmljs_builtin_pop_scope(QV4::ExecutionContext *ctx); +void __qmljs_builtin_declare_var(QV4::ExecutionContext *ctx, bool deletable, QV4::String *name); +void __qmljs_builtin_define_property(QV4::ExecutionContext *ctx, const QV4::Value &object, QV4::String *name, QV4::Value *val); +void __qmljs_builtin_define_array(QV4::ExecutionContext *ctx, QV4::Value *array, QV4::Value *values, uint length); +void __qmljs_builtin_define_getter_setter(QV4::ExecutionContext *ctx, const QV4::Value &object, QV4::String *name, const QV4::Value *getter, const QV4::Value *setter); +void __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value *args, QV4::InternalClass *klass); + +// constructors +void __qmljs_init_closure(QV4::ExecutionContext *ctx, QV4::Value *result, QV4::Function *clos); +QV4::Function *__qmljs_register_function(QV4::ExecutionContext *ctx, QV4::String *name, + bool hasDirectEval, + bool usesArgumentsObject, bool isStrict, + bool hasNestedFunctions, + QV4::String **formals, unsigned formalCount, + QV4::String **locals, unsigned localCount); + +// strings +Q_QML_EXPORT double __qmljs_string_to_number(const QString &s); +QV4::Value __qmljs_string_from_number(QV4::ExecutionContext *ctx, double number); +QV4::String *__qmljs_string_concat(QV4::ExecutionContext *ctx, QV4::String *first, QV4::String *second); + +// objects +Q_QML_EXPORT QV4::Value __qmljs_object_default_value(QV4::Object *object, int typeHint); +void __qmljs_set_activation_property(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value& value); +void __qmljs_set_property(QV4::ExecutionContext *ctx, const QV4::Value &object, QV4::String *name, const QV4::Value &value); +void __qmljs_get_property(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &object, QV4::String *name); +void __qmljs_get_activation_property(QV4::ExecutionContext *ctx, QV4::Value *result, QV4::String *name); + +void __qmljs_call_global_lookup(QV4::ExecutionContext *context, QV4::Value *result, uint index, QV4::Value *args, int argc); +void __qmljs_construct_global_lookup(QV4::ExecutionContext *context, QV4::Value *result, uint index, QV4::Value *args, int argc); + + +void __qmljs_get_element(QV4::ExecutionContext *ctx, QV4::Value *retval, const QV4::Value &object, const QV4::Value &index); +void __qmljs_set_element(QV4::ExecutionContext *ctx, const QV4::Value &object, const QV4::Value &index, const QV4::Value &value); + +// For each +void __qmljs_foreach_iterator_object(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &in); +void __qmljs_foreach_next_property_name(QV4::Value *result, const QV4::Value &foreach_iterator); + +// type conversion and testing +QV4::Value __qmljs_to_primitive(const QV4::Value &value, int typeHint); +QV4::Bool __qmljs_to_boolean(const QV4::Value &value); +double __qmljs_to_number(const QV4::Value &value); +QV4::Value __qmljs_to_string(const QV4::Value &value, QV4::ExecutionContext *ctx); +Q_QML_EXPORT QV4::String *__qmljs_convert_to_string(QV4::ExecutionContext *ctx, const QV4::Value &value); +void __qmljs_numberToString(QString *result, double num, int radix = 10); +QV4::Value __qmljs_to_object(QV4::ExecutionContext *ctx, const QV4::Value &value); +QV4::Object *__qmljs_convert_to_object(QV4::ExecutionContext *ctx, const QV4::Value &value); + +QV4::Bool __qmljs_equal(const QV4::Value &x, const QV4::Value &y); +Q_QML_EXPORT QV4::Bool __qmljs_strict_equal(const QV4::Value &x, const QV4::Value &y); + +// unary operators +typedef void (*UnaryOpName)(QV4::Value *, const QV4::Value &); +void __qmljs_uplus(QV4::Value *result, const QV4::Value &value); +void __qmljs_uminus(QV4::Value *result, const QV4::Value &value); +void __qmljs_compl(QV4::Value *result, const QV4::Value &value); +void __qmljs_not(QV4::Value *result, const QV4::Value &value); +void __qmljs_increment(QV4::Value *result, const QV4::Value &value); +void __qmljs_decrement(QV4::Value *result, const QV4::Value &value); + +void __qmljs_delete_subscript(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &base, const QV4::Value &index); +void __qmljs_delete_member(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &base, QV4::String *name); +void __qmljs_delete_name(QV4::ExecutionContext *ctx, QV4::Value *result, QV4::String *name); + +void Q_NORETURN __qmljs_throw(QV4::ExecutionContext*, const QV4::Value &value); + +// binary operators +typedef void (*BinOp)(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); + +void __qmljs_instanceof(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_in(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_bit_or(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_bit_xor(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_bit_and(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_add(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_sub(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_mul(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_div(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_mod(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_shl(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_shr(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_ushr(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_gt(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_lt(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_ge(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_le(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_eq(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_ne(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_se(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_sne(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); + +void __qmljs_add_helper(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); + + +typedef void (*InplaceBinOpName)(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_bit_and_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_bit_or_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_bit_xor_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_add_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_sub_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_mul_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_div_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_mod_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_shl_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_shr_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); +void __qmljs_inplace_ushr_name(QV4::ExecutionContext *ctx, QV4::String *name, const QV4::Value &value); + +typedef void (*InplaceBinOpElement)(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_bit_and_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_bit_or_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_bit_xor_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_add_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_sub_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_mul_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_div_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_mod_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_shl_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_shr_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); +void __qmljs_inplace_ushr_element(QV4::ExecutionContext *ctx, const QV4::Value &base, const QV4::Value &index, const QV4::Value &rhs); + +typedef void (*InplaceBinOpMember)(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_bit_and_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_bit_or_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_bit_xor_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_add_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_sub_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_mul_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_div_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_mod_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_shl_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_shr_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); +void __qmljs_inplace_ushr_member(QV4::ExecutionContext *ctx, const QV4::Value &base, QV4::String *name, const QV4::Value &rhs); + +typedef QV4::Bool (*CmpOp)(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_gt(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_lt(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_ge(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_le(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_eq(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_ne(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_se(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_sne(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_instanceof(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_in(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right); + +// type conversion and testing +inline QV4::Value __qmljs_to_primitive(const QV4::Value &value, int typeHint) +{ + QV4::Object *o = value.asObject(); + if (!o) + return value; + return __qmljs_object_default_value(o, typeHint); +} + +inline double __qmljs_to_number(const QV4::Value &value) +{ + switch (value.type()) { + case QV4::Value::Undefined_Type: + return std::numeric_limits<double>::quiet_NaN(); + case QV4::Value::Null_Type: + return 0; + case QV4::Value::Boolean_Type: + return (value.booleanValue() ? 1. : 0.); + case QV4::Value::Integer_Type: + return value.int_32; + case QV4::Value::String_Type: + return __qmljs_string_to_number(value.stringValue()->toQString()); + case QV4::Value::Object_Type: { + QV4::Value prim = __qmljs_to_primitive(value, QV4::NUMBER_HINT); + return __qmljs_to_number(prim); + } + default: // double + return value.doubleValue(); + } +} + +inline QV4::Value __qmljs_to_string(const QV4::Value &value, QV4::ExecutionContext *ctx) +{ + if (value.isString()) + return value; + return QV4::Value::fromString(__qmljs_convert_to_string(ctx, value)); +} + +inline QV4::Value __qmljs_to_object(QV4::ExecutionContext *ctx, const QV4::Value &value) +{ + if (value.isObject()) + return value; + return QV4::Value::fromObject(__qmljs_convert_to_object(ctx, value)); +} + + +inline void __qmljs_uplus(QV4::Value *result, const QV4::Value &value) +{ + TRACE1(value); + + *result = value; + if (result->tryIntegerConversion()) + return; + + double n = __qmljs_to_number(value); + *result = QV4::Value::fromDouble(n); +} + +inline void __qmljs_uminus(QV4::Value *result, const QV4::Value &value) +{ + TRACE1(value); + + // +0 != -0, so we need to convert to double when negating 0 + if (value.isInteger() && value.integerValue()) + *result = QV4::Value::fromInt32(-value.integerValue()); + else { + double n = __qmljs_to_number(value); + *result = QV4::Value::fromDouble(-n); + } +} + +inline void __qmljs_compl(QV4::Value *result, const QV4::Value &value) +{ + TRACE1(value); + + int n; + if (value.isConvertibleToInt()) + n = value.int_32; + else + n = QV4::Value::toInt32(__qmljs_to_number(value)); + + *result = QV4::Value::fromInt32(~n); +} + +inline void __qmljs_not(QV4::Value *result, const QV4::Value &value) +{ + TRACE1(value); + + bool b = value.toBoolean(); + *result = QV4::Value::fromBoolean(!b); +} + +// binary operators +inline void __qmljs_bit_or(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right)) { + *result = QV4::Value::fromInt32(left.integerValue() | right.integerValue()); + return; + } + + int lval = QV4::Value::toInt32(__qmljs_to_number(left)); + int rval = QV4::Value::toInt32(__qmljs_to_number(right)); + *result = QV4::Value::fromInt32(lval | rval); +} + +inline void __qmljs_bit_xor(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right)) { + *result = QV4::Value::fromInt32(left.integerValue() ^ right.integerValue()); + return; + } + + int lval = QV4::Value::toInt32(__qmljs_to_number(left)); + int rval = QV4::Value::toInt32(__qmljs_to_number(right)); + *result = QV4::Value::fromInt32(lval ^ rval); +} + +inline void __qmljs_bit_and(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right)) { + *result = QV4::Value::fromInt32(left.integerValue() & right.integerValue()); + return; + } + + int lval = QV4::Value::toInt32(__qmljs_to_number(left)); + int rval = QV4::Value::toInt32(__qmljs_to_number(right)); + *result = QV4::Value::fromInt32(lval & rval); +} + +inline void __qmljs_add(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right)) { + *result = add_int32(left.integerValue(), right.integerValue()); + return; + } + + if (QV4::Value::bothDouble(left, right)) { + *result = QV4::Value::fromDouble(left.doubleValue() + right.doubleValue()); + return; + } + + __qmljs_add_helper(ctx, result, left, right); +} + +inline void __qmljs_sub(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right)) { + *result = sub_int32(left.integerValue(), right.integerValue()); + return; + } + + double lval = __qmljs_to_number(left); + double rval = __qmljs_to_number(right); + *result = QV4::Value::fromDouble(lval - rval); +} + +inline void __qmljs_mul(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right)) { + *result = mul_int32(left.integerValue(), right.integerValue()); + return; + } + + double lval = __qmljs_to_number(left); + double rval = __qmljs_to_number(right); + *result = QV4::Value::fromDouble(lval * rval); +} + +inline void __qmljs_div(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + double lval = __qmljs_to_number(left); + double rval = __qmljs_to_number(right); + *result = QV4::Value::fromDouble(lval / rval); +} + +inline void __qmljs_mod(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right) && right.integerValue() != 0) { + int intRes = left.integerValue() % right.integerValue(); + if (intRes != 0 || left.integerValue() >= 0) { + *result = QV4::Value::fromInt32(intRes); + return; + } + } + + double lval = __qmljs_to_number(left); + double rval = __qmljs_to_number(right); + *result = QV4::Value::fromDouble(std::fmod(lval, rval)); +} + +inline void __qmljs_shl(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right)) { + *result = QV4::Value::fromInt32(left.integerValue() << ((uint(right.integerValue()) & 0x1f))); + return; + } + + int lval = QV4::Value::toInt32(__qmljs_to_number(left)); + unsigned rval = QV4::Value::toUInt32(__qmljs_to_number(right)) & 0x1f; + *result = QV4::Value::fromInt32(lval << rval); +} + +inline void __qmljs_shr(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (QV4::Value::integerCompatible(left, right)) { + *result = QV4::Value::fromInt32(left.integerValue() >> ((uint(right.integerValue()) & 0x1f))); + return; + } + + int lval = QV4::Value::toInt32(__qmljs_to_number(left)); + unsigned rval = QV4::Value::toUInt32(__qmljs_to_number(right)) & 0x1f; + *result = QV4::Value::fromInt32(lval >> rval); +} + +inline void __qmljs_ushr(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + uint res; + if (QV4::Value::integerCompatible(left, right)) { + res = uint(left.integerValue()) >> (uint(right.integerValue()) & 0x1f); + } else { + unsigned lval = QV4::Value::toUInt32(__qmljs_to_number(left)); + unsigned rval = QV4::Value::toUInt32(__qmljs_to_number(right)) & 0x1f; + res = lval >> rval; + } + + if (res > INT_MAX) + *result = QV4::Value::fromDouble(res); + else + *result = QV4::Value::fromInt32(res); +} + +inline void __qmljs_gt(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(__qmljs_cmp_gt(ctx, left, right)); +} + +inline void __qmljs_lt(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(__qmljs_cmp_lt(ctx, left, right)); +} + +inline void __qmljs_ge(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(__qmljs_cmp_ge(ctx, left, right)); +} + +inline void __qmljs_le(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(__qmljs_cmp_le(ctx, left, right)); +} + +inline void __qmljs_eq(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(__qmljs_cmp_eq(ctx, left, right)); +} + +inline void __qmljs_ne(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(!__qmljs_cmp_eq(ctx, left, right)); +} + +inline void __qmljs_se(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + bool r = __qmljs_strict_equal(left, right); + *result = QV4::Value::fromBoolean(r); +} + +inline void __qmljs_sne(QV4::ExecutionContext *, QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + bool r = ! __qmljs_strict_equal(left, right); + *result = QV4::Value::fromBoolean(r); +} + +inline QV4::Bool __qmljs_cmp_gt(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + if (QV4::Value::integerCompatible(left, right)) + return left.integerValue() > right.integerValue(); + + QV4::Value l = __qmljs_to_primitive(left, QV4::NUMBER_HINT); + QV4::Value r = __qmljs_to_primitive(right, QV4::NUMBER_HINT); + + if (QV4::Value::bothDouble(l, r)) { + return l.doubleValue() > r.doubleValue(); + } else if (l.isString() && r.isString()) { + return r.stringValue()->compare(l.stringValue()); + } else { + double dl = __qmljs_to_number(l); + double dr = __qmljs_to_number(r); + return dl > dr; + } +} + +inline QV4::Bool __qmljs_cmp_lt(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + if (QV4::Value::integerCompatible(left, right)) + return left.integerValue() < right.integerValue(); + + QV4::Value l = __qmljs_to_primitive(left, QV4::NUMBER_HINT); + QV4::Value r = __qmljs_to_primitive(right, QV4::NUMBER_HINT); + + if (QV4::Value::bothDouble(l, r)) { + return l.doubleValue() < r.doubleValue(); + } else if (l.isString() && r.isString()) { + return l.stringValue()->compare(r.stringValue()); + } else { + double dl = __qmljs_to_number(l); + double dr = __qmljs_to_number(r); + return dl < dr; + } +} + +inline QV4::Bool __qmljs_cmp_ge(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + if (QV4::Value::integerCompatible(left, right)) + return left.integerValue() >= right.integerValue(); + + QV4::Value l = __qmljs_to_primitive(left, QV4::NUMBER_HINT); + QV4::Value r = __qmljs_to_primitive(right, QV4::NUMBER_HINT); + + if (QV4::Value::bothDouble(l, r)) { + return l.doubleValue() >= r.doubleValue(); + } else if (l.isString() && r.isString()) { + return !l.stringValue()->compare(r.stringValue()); + } else { + double dl = __qmljs_to_number(l); + double dr = __qmljs_to_number(r); + return dl >= dr; + } +} + +inline QV4::Bool __qmljs_cmp_le(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + if (QV4::Value::integerCompatible(left, right)) + return left.integerValue() <= right.integerValue(); + + QV4::Value l = __qmljs_to_primitive(left, QV4::NUMBER_HINT); + QV4::Value r = __qmljs_to_primitive(right, QV4::NUMBER_HINT); + + if (QV4::Value::bothDouble(l, r)) { + return l.doubleValue() <= r.doubleValue(); + } else if (l.isString() && r.isString()) { + return !r.stringValue()->compare(l.stringValue()); + } else { + double dl = __qmljs_to_number(l); + double dr = __qmljs_to_number(r); + return dl <= dr; + } +} + +inline QV4::Bool __qmljs_cmp_eq(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + // need to test for doubles first as NaN != NaN + if (QV4::Value::bothDouble(left, right)) + return left.doubleValue() == right.doubleValue(); + if (left.val == right.val) + return true; + if (left.isString() && right.isString()) + return left.stringValue()->isEqualTo(right.stringValue()); + + return __qmljs_equal(left, right); +} + +inline QV4::Bool __qmljs_cmp_ne(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + return !__qmljs_cmp_eq(0, left, right); +} + +inline QV4::Bool __qmljs_cmp_se(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + return __qmljs_strict_equal(left, right); +} + +inline QV4::Bool __qmljs_cmp_sne(QV4::ExecutionContext *, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + return ! __qmljs_strict_equal(left, right); +} + +inline QV4::Bool __qmljs_cmp_instanceof(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + QV4::Value v; + __qmljs_instanceof(ctx, &v, left, right); + return v.booleanValue(); +} + +inline uint __qmljs_cmp_in(QV4::ExecutionContext *ctx, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + QV4::Value v; + __qmljs_in(ctx, &v, left, right); + return v.booleanValue(); +} + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QMLJS_RUNTIME_H diff --git a/src/qml/qml/v4/qv4script.cpp b/src/qml/qml/v4/qv4script.cpp new file mode 100644 index 0000000000..3de218a451 --- /dev/null +++ b/src/qml/qml/v4/qv4script.cpp @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4script_p.h" +#include "qv4mm_p.h" +#include "qv4functionobject_p.h" +#include "qv4function_p.h" +#include "qv4context_p.h" +#include "qv4debugging_p.h" +#include "qv4exception_p.h" + +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsast_p.h> +#include <qv4jsir_p.h> +#include <qv4codegen_p.h> + +#include <QtCore/QDebug> +#include <QtCore/QString> + +using namespace QV4; + +struct QmlBindingWrapper : FunctionObject +{ + QmlBindingWrapper(ExecutionContext *scope, Function *f, Object *qml) + : FunctionObject(scope, scope->engine->id_eval) + , qml(qml) + { + vtbl = &static_vtbl; + function = f; + usesArgumentsObject = function->usesArgumentsObject; + needsActivation = function->needsActivation(); + defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(1)); + + qmlContext = scope->engine->newQmlContext(this, qml); + scope->engine->popContext(); + } + + static Value call(Managed *that, const Value &, Value *, int); + static void markObjects(Managed *m) + { + QmlBindingWrapper *wrapper = static_cast<QmlBindingWrapper*>(m); + if (wrapper->qml) + wrapper->qml->mark(); + FunctionObject::markObjects(m); + wrapper->qmlContext->mark(); + } + +protected: + static const ManagedVTable static_vtbl; + +private: + Object *qml; + CallContext *qmlContext; + +}; + +DEFINE_MANAGED_VTABLE(QmlBindingWrapper); + +Value QmlBindingWrapper::call(Managed *that, const Value &, Value *, int) +{ + ExecutionEngine *engine = that->engine(); + QmlBindingWrapper *This = static_cast<QmlBindingWrapper *>(that); + + CallContext *ctx = This->qmlContext; + std::fill(ctx->locals, ctx->locals + ctx->function->varCount, Value::undefinedValue()); + engine->pushContext(ctx); + Value result = This->function->code(ctx, This->function->codeData); + engine->popContext(); + + return result; + +} + + +void Script::parse() +{ + if (parsed) + return; + + using namespace QQmlJS; + + parsed = true; + + ExecutionEngine *v4 = scope->engine; + + MemoryManager::GCBlocker gcBlocker(v4->memoryManager); + + V4IR::Module module; + + QQmlJS::Engine ee, *engine = ⅇ + Lexer lexer(engine); + lexer.setCode(sourceCode, line, parseAsBinding); + Parser parser(engine); + + const bool parsed = parser.parseProgram(); + + DiagnosticMessage *error = 0, **errIt = &error; + foreach (const QQmlJS::DiagnosticMessage &m, parser.diagnosticMessages()) { + if (m.isError()) { + *errIt = new DiagnosticMessage; + (*errIt)->fileName = sourceFile; + (*errIt)->offset = m.loc.offset; + (*errIt)->length = m.loc.length; + (*errIt)->startLine = m.loc.startLine; + (*errIt)->startColumn = m.loc.startColumn; + (*errIt)->type = DiagnosticMessage::Error; + (*errIt)->message = m.message; + errIt = &(*errIt)->next; + } else { + qWarning() << sourceFile << ':' << m.loc.startLine << ':' << m.loc.startColumn + << ": warning: " << m.message; + } + } + if (error) + scope->throwSyntaxError(error); + + if (parsed) { + using namespace AST; + Program *program = AST::cast<Program *>(parser.rootNode()); + if (!program) { + // if parsing was successful, and we have no program, then + // we're done...: + return; + } + + QStringList inheritedLocals; + if (inheritContext) + for (String * const *i = scope->variables(), * const *ei = i + scope->variableCount(); i < ei; ++i) + inheritedLocals.append(*i ? (*i)->toQString() : QString()); + + Codegen cg(scope, strictMode); + V4IR::Function *globalIRCode = cg(sourceFile, sourceCode, program, &module, + parseAsBinding ? QQmlJS::Codegen::QmlBinding : QQmlJS::Codegen::EvalCode, inheritedLocals); + QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(v4, &module)); + if (inheritContext) + isel->setUseFastLookups(false); + if (globalIRCode) + vmFunction = isel->vmFunction(globalIRCode); + } + + if (!vmFunction) + // ### FIX file/line number + v4->current->throwError(QV4::Value::fromObject(v4->newSyntaxErrorObject(v4->current, 0))); +} + +Value Script::run() +{ + if (!parsed) + parse(); + if (!vmFunction) + return Value::undefinedValue(); + + QV4::ExecutionEngine *engine = scope->engine; + + if (qml.isEmpty()) { + TemporaryAssignment<Function*> savedGlobalCode(engine->globalCode, vmFunction); + + bool strict = scope->strictMode; + Lookup *lookups = scope->lookups; + + scope->strictMode = vmFunction->isStrict; + scope->lookups = vmFunction->lookups; + + QV4::Value result; + try { + result = vmFunction->code(scope, vmFunction->codeData); + } catch (Exception &e) { + scope->strictMode = strict; + scope->lookups = lookups; + throw; + } + + return result; + + } else { + FunctionObject *f = new (engine->memoryManager) QmlBindingWrapper(scope, vmFunction, qml.value().asObject()); + return f->call(Value::undefinedValue(), 0, 0); + } +} + +Function *Script::function() +{ + if (!parsed) + parse(); + return vmFunction; +} + +Value Script::qmlBinding() +{ + if (!parsed) + parse(); + QV4::ExecutionEngine *v4 = scope->engine; + return Value::fromObject(new (v4->memoryManager) QmlBindingWrapper(scope, vmFunction, qml.value().asObject())); +} + +QV4::Value Script::evaluate(ExecutionEngine *engine, const QString &script, Object *scopeObject) +{ + QV4::Script qmlScript(engine, scopeObject, script, QString()); + + QV4::ExecutionContext *ctx = engine->current; + QV4::Value result = QV4::Value::undefinedValue(); + try { + qmlScript.parse(); + result = qmlScript.run(); + } catch (QV4::Exception &e) { + e.accept(ctx); + } + return result; +} diff --git a/src/qml/qml/v4/qv4script_p.h b/src/qml/qml/v4/qv4script_p.h new file mode 100644 index 0000000000..274a87db26 --- /dev/null +++ b/src/qml/qml/v4/qv4script_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4SCRIPT_H +#define QV4SCRIPT_H + +#include "qv4global_p.h" +#include "qv4engine_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ExecutionContext; + +struct Q_QML_EXPORT Script { + Script(ExecutionContext *scope, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) + : sourceFile(source), line(line), column(column), sourceCode(sourceCode) + , scope(scope), strictMode(false), inheritContext(false), parsed(false) + , vmFunction(0), parseAsBinding(false) {} + Script(ExecutionEngine *engine, Object *qml, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) + : sourceFile(source), line(line), column(column), sourceCode(sourceCode) + , scope(engine->rootContext), strictMode(false), inheritContext(true), parsed(false) + , qml(Value::fromObject(qml)), vmFunction(0), parseAsBinding(true) {} + QString sourceFile; + int line; + int column; + QString sourceCode; + ExecutionContext *scope; + bool strictMode; + bool inheritContext; + bool parsed; + QV4::PersistentValue qml; + Function *vmFunction; + bool parseAsBinding; + + void parse(); + Value run(); + Value qmlBinding(); + + Function *function(); + + + static Value evaluate(ExecutionEngine *engine, const QString &script, Object *scopeObject); +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4/qv4sequenceobject.cpp b/src/qml/qml/v4/qv4sequenceobject.cpp new file mode 100644 index 0000000000..c4d9a71519 --- /dev/null +++ b/src/qml/qml/v4/qv4sequenceobject.cpp @@ -0,0 +1,651 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtQml/qqml.h> + +#include "qv4sequenceobject_p.h" + +#include <private/qv4functionobject_p.h> +#include <private/qv4arrayobject_p.h> +#include <private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +// helper function to generate valid warnings if errors occur during sequence operations. +static void generateWarning(QV4::ExecutionContext *ctx, const QString& description) +{ + QQmlEngine *engine = ctx->engine->v8Engine->engine(); + if (!engine) + return; + QQmlError retn; + retn.setDescription(description); + + QV4::ExecutionEngine::StackFrame frame = ctx->engine->currentStackFrame(); + + retn.setLine(frame.line); + retn.setUrl(QUrl(frame.source)); + QQmlEnginePrivate::warning(engine, retn); +} + +// F(elementType, elementTypeName, sequenceType, defaultValue) +#define FOREACH_QML_SEQUENCE_TYPE(F) \ + F(int, Int, QList<int>, 0) \ + F(qreal, Real, QList<qreal>, 0.0) \ + F(bool, Bool, QList<bool>, false) \ + F(QString, String, QList<QString>, QString()) \ + F(QString, QString, QStringList, QString()) \ + F(QUrl, Url, QList<QUrl>, QUrl()) + +static QV4::Value convertElementToValue(QV4::ExecutionEngine *engine, const QString &element) +{ + return QV4::Value::fromString(engine, element); +} + +static QV4::Value convertElementToValue(QV4::ExecutionEngine *, int element) +{ + return QV4::Value::fromInt32(element); +} + +static QV4::Value convertElementToValue(QV4::ExecutionEngine *engine, const QUrl &element) +{ + return QV4::Value::fromString(engine, element.toString()); +} + +static QV4::Value convertElementToValue(QV4::ExecutionEngine *, qreal element) +{ + return QV4::Value::fromDouble(element); +} + +static QV4::Value convertElementToValue(QV4::ExecutionEngine *, bool element) +{ + return QV4::Value::fromBoolean(element); +} + +static QString convertElementToString(const QString &element) +{ + return element; +} + +static QString convertElementToString(int element) +{ + return QString::number(element); +} + +static QString convertElementToString(const QUrl &element) +{ + return element.toString(); +} + +static QString convertElementToString(qreal element) +{ + QString qstr; + __qmljs_numberToString(&qstr, element, 10); + return qstr; +} + +static QString convertElementToString(bool element) +{ + if (element) + return QStringLiteral("true"); + else + return QStringLiteral("false"); +} + +template <typename ElementType> ElementType convertValueToElement(const QV4::Value &value); + +template <> QString convertValueToElement(const QV4::Value &value) +{ + return value.toQString(); +} + +template <> int convertValueToElement(const QV4::Value &value) +{ + return value.toInt32(); +} + +template <> QUrl convertValueToElement(const QV4::Value &value) +{ + return QUrl(value.toQString()); +} + +template <> qreal convertValueToElement(const QV4::Value &value) +{ + return value.toNumber(); +} + +template <> bool convertValueToElement(const QV4::Value &value) +{ + return value.toBoolean(); +} + +template <typename Container> +class QQmlSequence : public QV4::Object +{ + Q_MANAGED +public: + QQmlSequence(QV4::ExecutionEngine *engine, const Container &container) + : QV4::Object(engine) + , m_container(container) + , m_object(0) + , m_propertyIndex(-1) + , m_isReference(false) + { + type = Type_QmlSequence; + vtbl = &static_vtbl; + prototype = engine->sequencePrototype; + init(engine); + } + + QQmlSequence(QV4::ExecutionEngine *engine, QObject *object, int propertyIndex) + : QV4::Object(engine) + , m_object(object) + , m_propertyIndex(propertyIndex) + , m_isReference(true) + { + type = Type_QmlSequence; + vtbl = &static_vtbl; + prototype = engine->sequencePrototype; + loadReference(); + init(engine); + } + + void init(ExecutionEngine *engine) + { + defineAccessorProperty(engine, QStringLiteral("length"), method_get_length, method_set_length); + } + + QV4::Value containerGetIndexed(uint index, bool *hasProperty) + { + /* Qt containers have int (rather than uint) allowable indexes. */ + if (index > INT_MAX) { + generateWarning(engine()->current, QLatin1String("Index out of range during indexed get")); + if (hasProperty) + *hasProperty = false; + return QV4::Value::undefinedValue(); + } + if (m_isReference) { + if (!m_object) { + if (hasProperty) + *hasProperty = false; + return QV4::Value::undefinedValue(); + } + loadReference(); + } + qint32 signedIdx = static_cast<qint32>(index); + if (signedIdx < m_container.count()) { + if (hasProperty) + *hasProperty = true; + return convertElementToValue(engine(), m_container.at(signedIdx)); + } + if (hasProperty) + *hasProperty = false; + return QV4::Value::undefinedValue(); + } + + void containerPutIndexed(uint index, const QV4::Value &value) + { + /* Qt containers have int (rather than uint) allowable indexes. */ + if (index > INT_MAX) { + generateWarning(engine()->current, QLatin1String("Index out of range during indexed set")); + return; + } + + if (m_isReference) { + if (!m_object) + return; + loadReference(); + } + + qint32 signedIdx = static_cast<qint32>(index); + + int count = m_container.count(); + + typename Container::value_type element = convertValueToElement<typename Container::value_type>(value); + + if (signedIdx == count) { + m_container.append(element); + } else if (signedIdx < count) { + m_container[signedIdx] = element; + } else { + /* according to ECMA262r3 we need to insert */ + /* the value at the given index, increasing length to index+1. */ + m_container.reserve(signedIdx + 1); + while (signedIdx > count++) { + m_container.append(typename Container::value_type()); + } + m_container.append(element); + } + + if (m_isReference) + storeReference(); + } + + QV4::PropertyAttributes containerQueryIndexed(uint index) const + { + /* Qt containers have int (rather than uint) allowable indexes. */ + if (index > INT_MAX) { + generateWarning(engine()->current, QLatin1String("Index out of range during indexed query")); + return QV4::Attr_Invalid; + } + if (m_isReference) { + if (!m_object) + return QV4::Attr_Invalid; + loadReference(); + } + qint32 signedIdx = static_cast<qint32>(index); + return (signedIdx < m_container.count()) ? QV4::Attr_Data : QV4::Attr_Invalid; + } + + Property *containerAdvanceIterator(ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs) + { + *name = 0; + *index = UINT_MAX; + + if (m_isReference) { + if (!m_object) + return QV4::Object::advanceIterator(this, it, name, index, attrs); + loadReference(); + } + + if (it->arrayIndex < m_container.count()) { + if (attrs) + *attrs = QV4::Attr_Data; + *index = it->arrayIndex; + ++it->arrayIndex; + it->tmpDynamicProperty.value = convertElementToValue(engine(), m_container.at(*index)); + return &it->tmpDynamicProperty; + } + return QV4::Object::advanceIterator(this, it, name, index, attrs); + } + + bool containerDeleteIndexedProperty(uint index) + { + /* Qt containers have int (rather than uint) allowable indexes. */ + if (index > INT_MAX) + return false; + if (m_isReference) { + if (!m_object) + return false; + loadReference(); + } + qint32 signedIdx = static_cast<qint32>(index); + + if (signedIdx >= m_container.count()) + return false; + + /* according to ECMA262r3 it should be Undefined, */ + /* but we cannot, so we insert a default-value instead. */ + m_container.replace(signedIdx, typename Container::value_type()); + + if (m_isReference) + storeReference(); + + return true; + } + + bool containerIsEqualTo(Managed *other) + { + QQmlSequence<Container> *otherSequence = other->as<QQmlSequence<Container> >(); + if (!otherSequence) + return false; + if (m_isReference && otherSequence->m_isReference) { + return m_object == otherSequence->m_object && m_propertyIndex == otherSequence->m_propertyIndex; + } else if (!m_isReference && !otherSequence->m_isReference) { + return this == otherSequence; + } + return false; + } + + struct DefaultCompareFunctor + { + bool operator()(typename Container::value_type lhs, typename Container::value_type rhs) + { + return convertElementToString(lhs) < convertElementToString(rhs); + } + }; + + struct CompareFunctor + { + CompareFunctor(QV4::ExecutionContext *ctx, const QV4::Value &compareFn) + : m_ctx(ctx), m_compareFn(compareFn) + {} + + bool operator()(typename Container::value_type lhs, typename Container::value_type rhs) + { + QV4::Managed *fun = this->m_compareFn.asManaged(); + QV4::Value argv[2] = { + convertElementToValue(this->m_ctx->engine, lhs), + convertElementToValue(this->m_ctx->engine, rhs) + }; + QV4::Value result = fun->call(QV4::Value::fromObject(this->m_ctx->engine->globalObject), argv, 2); + return result.toNumber() < 0; + } + + private: + QV4::ExecutionContext *m_ctx; + QV4::Value m_compareFn; + }; + + void sort(QV4::SimpleCallContext *ctx) + { + if (m_isReference) { + if (!m_object) + return; + loadReference(); + } + + if (ctx->argumentCount == 1 && ctx->arguments[0].asFunctionObject()) { + QV4::Value compareFn = ctx->arguments[0]; + CompareFunctor cf(ctx, compareFn); + qSort(m_container.begin(), m_container.end(), cf); + } else { + DefaultCompareFunctor cf; + qSort(m_container.begin(), m_container.end(), cf); + } + + if (m_isReference) + storeReference(); + } + + static QV4::Value method_get_length(QV4::SimpleCallContext *ctx) + { + QQmlSequence<Container> *This = ctx->thisObject.as<QQmlSequence<Container> >(); + if (!This) + ctx->throwTypeError(); + + if (This->m_isReference) { + if (!This->m_object) + return QV4::Value::fromInt32(0); + This->loadReference(); + } + return QV4::Value::fromInt32(This->m_container.count()); + } + + static QV4::Value method_set_length(QV4::SimpleCallContext* ctx) + { + QQmlSequence<Container> *This = ctx->thisObject.as<QQmlSequence<Container> >(); + if (!This) + ctx->throwTypeError(); + + quint32 newLength = ctx->arguments[0].toUInt32(); + /* Qt containers have int (rather than uint) allowable indexes. */ + if (newLength > INT_MAX) { + generateWarning(ctx, QLatin1String("Index out of range during length set")); + return QV4::Value::undefinedValue(); + } + /* Read the sequence from the QObject property if we're a reference */ + if (This->m_isReference) { + if (!This->m_object) + return QV4::Value::undefinedValue(); + This->loadReference(); + } + /* Determine whether we need to modify the sequence */ + qint32 newCount = static_cast<qint32>(newLength); + qint32 count = This->m_container.count(); + if (newCount == count) { + return QV4::Value::undefinedValue(); + } else if (newCount > count) { + /* according to ECMA262r3 we need to insert */ + /* undefined values increasing length to newLength. */ + /* We cannot, so we insert default-values instead. */ + This->m_container.reserve(newCount); + while (newCount > count++) { + This->m_container.append(typename Container::value_type()); + } + } else { + /* according to ECMA262r3 we need to remove */ + /* elements until the sequence is the required length. */ + while (newCount < count) { + count--; + This->m_container.removeAt(count); + } + } + /* write back if required. */ + if (This->m_isReference) { + /* write back. already checked that object is non-null, so skip that check here. */ + This->storeReference(); + } + return QV4::Value::undefinedValue(); + } + + QVariant toVariant() const + { return QVariant::fromValue<Container>(m_container); } + + static QVariant toVariant(QV4::ArrayObject *array) + { + Container result; + uint32_t length = array->arrayLength(); + for (uint32_t i = 0; i < length; ++i) + result << convertValueToElement<typename Container::value_type>(array->getIndexed(i)); + return QVariant::fromValue(result); + } + +private: + void loadReference() const + { + Q_ASSERT(m_object); + Q_ASSERT(m_isReference); + void *a[] = { &m_container, 0 }; + QMetaObject::metacall(m_object, QMetaObject::ReadProperty, m_propertyIndex, a); + } + + void storeReference() + { + Q_ASSERT(m_object); + Q_ASSERT(m_isReference); + int status = -1; + QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding; + void *a[] = { &m_container, 0, &status, &flags }; + QMetaObject::metacall(m_object, QMetaObject::WriteProperty, m_propertyIndex, a); + } + + mutable Container m_container; + QPointer<QObject> m_object; + int m_propertyIndex; + bool m_isReference; + + static QV4::Value getIndexed(QV4::Managed *that, uint index, bool *hasProperty) + { return static_cast<QQmlSequence<Container> *>(that)->containerGetIndexed(index, hasProperty); } + static void putIndexed(Managed *that, uint index, const QV4::Value &value) + { static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(index, value); } + static QV4::PropertyAttributes queryIndexed(const QV4::Managed *that, uint index) + { return static_cast<const QQmlSequence<Container> *>(that)->containerQueryIndexed(index); } + static bool deleteIndexedProperty(QV4::Managed *that, uint index) + { return static_cast<QQmlSequence<Container> *>(that)->containerDeleteIndexedProperty(index); } + static bool isEqualTo(Managed *that, Managed *other) + { return static_cast<QQmlSequence<Container> *>(that)->containerIsEqualTo(other); } + static Property *advanceIterator(Managed *that, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs) + { return static_cast<QQmlSequence<Container> *>(that)->containerAdvanceIterator(it, name, index, attrs); } + + static void destroy(Managed *that) + { + static_cast<QQmlSequence<Container> *>(that)->~QQmlSequence<Container>(); + } +}; + +typedef QQmlSequence<QStringList> QQmlQStringList; +template<> +DEFINE_MANAGED_VTABLE(QQmlQStringList); +typedef QQmlSequence<QList<QString> > QQmlStringList; +template<> +DEFINE_MANAGED_VTABLE(QQmlStringList); +typedef QQmlSequence<QList<int> > QQmlIntList; +template<> +DEFINE_MANAGED_VTABLE(QQmlIntList); +typedef QQmlSequence<QList<QUrl> > QQmlUrlList; +template<> +DEFINE_MANAGED_VTABLE(QQmlUrlList); +typedef QQmlSequence<QList<bool> > QQmlBoolList; +template<> +DEFINE_MANAGED_VTABLE(QQmlBoolList); +typedef QQmlSequence<QList<qreal> > QQmlRealList; +template<> +DEFINE_MANAGED_VTABLE(QQmlRealList); + +#define REGISTER_QML_SEQUENCE_METATYPE(unused, unused2, SequenceType, unused3) qRegisterMetaType<SequenceType>(#SequenceType); +SequencePrototype::SequencePrototype(ExecutionEngine *engine) + : QV4::Object(engine) +{ + prototype = engine->arrayPrototype; + FOREACH_QML_SEQUENCE_TYPE(REGISTER_QML_SEQUENCE_METATYPE) +} +#undef REGISTER_QML_SEQUENCE_METATYPE + +void SequencePrototype::init(QV4::ExecutionEngine *engine) +{ + defineDefaultProperty(engine, QStringLiteral("sort"), method_sort, 1); + defineDefaultProperty(engine, QStringLiteral("valueOf"), method_valueOf, 0); +} + +QV4::Value SequencePrototype::method_sort(QV4::SimpleCallContext *ctx) +{ + QV4::Object *o = ctx->thisObject.asObject(); + if (!o || !o->isListType()) + ctx->throwTypeError(); + + if (ctx->argumentCount >= 2) + return ctx->thisObject; + +#define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \ + if (QQml##SequenceElementTypeName##List *s = o->as<QQml##SequenceElementTypeName##List>()) { \ + s->sort(ctx); \ + } else + + FOREACH_QML_SEQUENCE_TYPE(CALL_SORT) + +#undef CALL_SORT + return ctx->thisObject; +} + +#define IS_SEQUENCE(unused1, unused2, SequenceType, unused3) \ + if (sequenceTypeId == qMetaTypeId<SequenceType>()) { \ + return true; \ + } else + +bool SequencePrototype::isSequenceType(int sequenceTypeId) +{ + FOREACH_QML_SEQUENCE_TYPE(IS_SEQUENCE) { /* else */ return false; } +} +#undef IS_SEQUENCE + +#define NEW_REFERENCE_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ + if (sequenceType == qMetaTypeId<SequenceType>()) { \ + QV4::Object *obj = new (engine->memoryManager) QQml##ElementTypeName##List(engine, object, propertyIndex); \ + return QV4::Value::fromObject(obj); \ + } else + +QV4::Value SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int sequenceType, QObject *object, int propertyIndex, bool *succeeded) +{ + // This function is called when the property is a QObject Q_PROPERTY of + // the given sequence type. Internally we store a typed-sequence + // (as well as object ptr + property index for updated-read and write-back) + // and so access/mutate avoids variant conversion. + *succeeded = true; + FOREACH_QML_SEQUENCE_TYPE(NEW_REFERENCE_SEQUENCE) { /* else */ *succeeded = false; return QV4::Value::undefinedValue(); } +} +#undef NEW_REFERENCE_SEQUENCE + +#define NEW_COPY_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ + if (sequenceType == qMetaTypeId<SequenceType>()) { \ + QV4::Object *obj = new (engine->memoryManager) QQml##ElementTypeName##List(engine, v.value<SequenceType >()); \ + return QV4::Value::fromObject(obj); \ + } else + +QV4::Value SequencePrototype::fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded) +{ + // This function is called when assigning a sequence value to a normal JS var + // in a JS block. Internally, we store a sequence of the specified type. + // Access and mutation is extremely fast since it will not need to modify any + // QObject property. + int sequenceType = v.userType(); + *succeeded = true; + FOREACH_QML_SEQUENCE_TYPE(NEW_COPY_SEQUENCE) { /* else */ *succeeded = false; return QV4::Value::undefinedValue(); } +} +#undef NEW_COPY_SEQUENCE + +#define SEQUENCE_TO_VARIANT(ElementType, ElementTypeName, SequenceType, unused) \ + if (QQml##ElementTypeName##List *list = object->as<QQml##ElementTypeName##List>()) \ + return list->toVariant(); \ + else + +QVariant SequencePrototype::toVariant(QV4::Object *object) +{ + Q_ASSERT(object->isListType()); + FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ return QVariant(); } +} + +#define SEQUENCE_TO_VARIANT(ElementType, ElementTypeName, SequenceType, unused) \ + if (typeHint == qMetaTypeId<SequenceType>()) { \ + return QQml##ElementTypeName##List::toVariant(a); \ + } else + +QVariant SequencePrototype::toVariant(const QV4::Value &array, int typeHint, bool *succeeded) +{ + *succeeded = true; + + QV4::ArrayObject *a = array.asArrayObject(); + if (!a) { + *succeeded = false; + return QVariant(); + } + FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ *succeeded = false; return QVariant(); } +} + +#undef SEQUENCE_TO_VARIANT + +#define MAP_META_TYPE(ElementType, ElementTypeName, SequenceType, unused) \ + if (object->as<QQml##ElementTypeName##List>()) { \ + return qMetaTypeId<SequenceType>(); \ + } else + +int SequencePrototype::metaTypeForSequence(QV4::Object *object) +{ + FOREACH_QML_SEQUENCE_TYPE(MAP_META_TYPE) + /*else*/ { + return -1; + } +} + +#undef MAP_META_TYPE + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4sequenceobject_p.h b/src/qml/qml/v4/qv4sequenceobject_p.h new file mode 100644 index 0000000000..2cade45092 --- /dev/null +++ b/src/qml/qml/v4/qv4sequenceobject_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4SEQUENCEWRAPPER_P_H +#define QV4SEQUENCEWRAPPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtCore/qvariant.h> + +#include "qv4value_p.h" +#include "qv4object_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct SequencePrototype : public QV4::Object +{ + SequencePrototype(QV4::ExecutionEngine *engine); + + void init(QV4::ExecutionEngine *engine); + + static QV4::Value method_valueOf(QV4::SimpleCallContext *ctx) + { + return QV4::Value::fromString(ctx->thisObject.toString(ctx)); + } + + static QV4::Value method_sort(QV4::SimpleCallContext *ctx); + + static bool isSequenceType(int sequenceTypeId); + static QV4::Value newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded); + static QV4::Value fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded); + static int metaTypeForSequence(QV4::Object *object); + static QVariant toVariant(QV4::Object *object); + static QVariant toVariant(const QV4::Value &array, int typeHint, bool *succeeded); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4SEQUENCEWRAPPER_P_H diff --git a/src/qml/qml/v4/qv4serialize.cpp b/src/qml/qml/v4/qv4serialize.cpp new file mode 100644 index 0000000000..f7389dc6d7 --- /dev/null +++ b/src/qml/qml/v4/qv4serialize.cpp @@ -0,0 +1,399 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4serialize_p.h" + +#include <private/qv8engine_p.h> +#include <private/qqmllistmodel_p.h> +#include <private/qqmllistmodelworkeragent_p.h> + +#include <private/qv4value_p.h> +#include <private/qv4dateobject_p.h> +#include <private/qv4regexpobject_p.h> +#include <private/qv4sequenceobject_p.h> +#include <private/qv4objectproto_p.h> + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +// We allow the following JavaScript types to be passed between the main and +// the secondary thread: +// + undefined +// + null +// + Boolean +// + String +// + Function +// + Array +// + "Simple" Objects +// + Number +// + Date +// + RegExp +// <quint8 type><quint24 size><data> + +enum Type { + WorkerUndefined, + WorkerNull, + WorkerTrue, + WorkerFalse, + WorkerString, + WorkerFunction, + WorkerArray, + WorkerObject, + WorkerInt32, + WorkerUint32, + WorkerNumber, + WorkerDate, + WorkerRegexp, + WorkerListModel, + WorkerSequence +}; + +static inline quint32 valueheader(Type type, quint32 size = 0) +{ + return quint8(type) << 24 | (size & 0xFFFFFF); +} + +static inline Type headertype(quint32 header) +{ + return (Type)(header >> 24); +} + +static inline quint32 headersize(quint32 header) +{ + return header & 0xFFFFFF; +} + +static inline void push(QByteArray &data, quint32 value) +{ + data.append((const char *)&value, sizeof(quint32)); +} + +static inline void push(QByteArray &data, double value) +{ + data.append((const char *)&value, sizeof(double)); +} + +static inline void push(QByteArray &data, void *ptr) +{ + data.append((const char *)&ptr, sizeof(void *)); +} + +static inline void reserve(QByteArray &data, int extra) +{ + data.reserve(data.size() + extra); +} + +static inline quint32 popUint32(const char *&data) +{ + quint32 rv = *((quint32 *)data); + data += sizeof(quint32); + return rv; +} + +static inline double popDouble(const char *&data) +{ + double rv = *((double *)data); + data += sizeof(double); + return rv; +} + +static inline void *popPtr(const char *&data) +{ + void *rv = *((void **)data); + data += sizeof(void *); + return rv; +} + +// XXX TODO: Check that worker script is exception safe in the case of +// serialization/deserialization failures + +#define ALIGN(size) (((size) + 3) & ~3) +void Serialize::serialize(QByteArray &data, const QV4::Value &v, QV8Engine *engine) +{ + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + if (v.isEmpty()) { + } else if (v.isUndefined()) { + push(data, valueheader(WorkerUndefined)); + } else if (v.isNull()) { + push(data, valueheader(WorkerNull)); + } else if (v.isBoolean()) { + push(data, valueheader(v.booleanValue() == true ? WorkerTrue : WorkerFalse)); + } else if (QV4::String *s = v.asString()) { + const QString &qstr = s->toQString(); + int length = qstr.length(); + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + int utf16size = ALIGN(length * sizeof(uint16_t)); + + reserve(data, utf16size + sizeof(quint32)); + push(data, valueheader(WorkerString, length)); + + int offset = data.size(); + data.resize(data.size() + utf16size); + char *buffer = data.data() + offset; + + memcpy(buffer, qstr.constData(), length*sizeof(QChar)); + } else if (v.asFunctionObject()) { + // XXX TODO: Implement passing function objects between the main and + // worker scripts + push(data, valueheader(WorkerUndefined)); + } else if (QV4::ArrayObject *array = v.asArrayObject()) { + uint32_t length = array->arrayLength(); + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + reserve(data, sizeof(quint32) + length * sizeof(quint32)); + push(data, valueheader(WorkerArray, length)); + for (uint32_t ii = 0; ii < length; ++ii) + serialize(data, array->getIndexed(ii), engine); + } else if (v.isInteger()) { + reserve(data, 2 * sizeof(quint32)); + push(data, valueheader(WorkerInt32)); + push(data, (quint32)v.integerValue()); +// } else if (v->IsUint32()) { +// reserve(data, 2 * sizeof(quint32)); +// push(data, valueheader(WorkerUint32)); +// push(data, v->Uint32Value()); + } else if (v.isNumber()) { + reserve(data, sizeof(quint32) + sizeof(double)); + push(data, valueheader(WorkerNumber)); + push(data, v.asDouble()); + } else if (QV4::DateObject *d = v.asDateObject()) { + reserve(data, sizeof(quint32) + sizeof(double)); + push(data, valueheader(WorkerDate)); + push(data, d->value.asDouble()); + } else if (QV4::RegExpObject *re = v.as<RegExpObject>()) { + quint32 flags = re->flags(); + QString pattern = re->source(); + int length = pattern.length() + 1; + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + int utf16size = ALIGN(length * sizeof(uint16_t)); + + reserve(data, sizeof(quint32) + utf16size); + push(data, valueheader(WorkerRegexp, flags)); + push(data, (quint32)length); + + int offset = data.size(); + data.resize(data.size() + utf16size); + char *buffer = data.data() + offset; + + memcpy(buffer, pattern.constData(), length*sizeof(QChar)); + } else if (QV4::QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) { + // XXX TODO: Generalize passing objects between the main thread and worker scripts so + // that others can trivially plug in their elements. + QQmlListModel *lm = qobject_cast<QQmlListModel *>(qobjectWrapper->object()); + if (lm && lm->agent()) { + QQmlListModelWorkerAgent *agent = lm->agent(); + agent->addref(); + push(data, valueheader(WorkerListModel)); + push(data, (void *)agent); + return; + } + // No other QObject's are allowed to be sent + push(data, valueheader(WorkerUndefined)); + } else if (QV4::Object *o = v.asObject()) { + + if (o->isListType()) { + // valid sequence. we generate a length (sequence length + 1 for the sequence type) + uint32_t seqLength = o->get(v4->id_length).toUInt32(); + uint32_t length = seqLength + 1; + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + reserve(data, sizeof(quint32) + length * sizeof(quint32)); + push(data, valueheader(WorkerSequence, length)); + serialize(data, QV4::Value::fromInt32(QV4::SequencePrototype::metaTypeForSequence(o)), engine); // sequence type + for (uint32_t ii = 0; ii < seqLength; ++ii) + serialize(data, o->getIndexed(ii), engine); // sequence elements + + return; + } + + // regular object + QV4::ArrayObject *properties = QV4::ObjectPrototype::getOwnPropertyNames(v4, v); + quint32 length = properties->arrayLength(); + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + push(data, valueheader(WorkerObject, length)); + + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + for (quint32 ii = 0; ii < length; ++ii) { + QV4::String *s = properties->getIndexed(ii).asString(); + serialize(data, QV4::Value::fromString(s), engine); + + bool hasCaught = false; + QV4::ExecutionContext *ctx = v4->current; + QV4::Value val = QV4::Value::undefinedValue(); + try { + val = o->get(s); + } catch (QV4::Exception &e) { + e.accept(ctx); + } + + serialize(data, val, engine); + } + return; + } else { + push(data, valueheader(WorkerUndefined)); + } +} + +QV4::Value Serialize::deserialize(const char *&data, QV8Engine *engine) +{ + quint32 header = popUint32(data); + Type type = headertype(header); + + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + + switch (type) { + case WorkerUndefined: + return QV4::Value::undefinedValue(); + case WorkerNull: + return QV4::Value::nullValue(); + case WorkerTrue: + return QV4::Value::fromBoolean(true); + case WorkerFalse: + return QV4::Value::fromBoolean(false); + case WorkerString: + { + quint32 size = headersize(header); + QString qstr((QChar *)data, size); + data += ALIGN(size * sizeof(uint16_t)); + return QV4::Value::fromString(v4->newString(qstr)); + } + case WorkerFunction: + Q_ASSERT(!"Unreachable"); + break; + case WorkerArray: + { + quint32 size = headersize(header); + QV4::ArrayObject *a = v4->newArrayObject(); + for (quint32 ii = 0; ii < size; ++ii) { + a->putIndexed(ii, deserialize(data, engine)); + } + return QV4::Value::fromObject(a); + } + case WorkerObject: + { + quint32 size = headersize(header); + QV4::Object *o = v4->newObject(); + for (quint32 ii = 0; ii < size; ++ii) { + QV4::Value name = deserialize(data, engine); + QV4::Value value = deserialize(data, engine); + o->put(name.asString(), value); + } + return QV4::Value::fromObject(o); + } + case WorkerInt32: + return QV4::Value::fromInt32((qint32)popUint32(data)); + case WorkerUint32: + return QV4::Value::fromUInt32(popUint32(data)); + case WorkerNumber: + return QV4::Value::fromDouble(popDouble(data)); + case WorkerDate: + return QV4::Value::fromObject(v4->newDateObject(QV4::Value::fromDouble(popDouble(data)))); + case WorkerRegexp: + { + quint32 flags = headersize(header); + quint32 length = popUint32(data); + QString pattern = QString((QChar *)data, length - 1); + data += ALIGN(length * sizeof(uint16_t)); + return QV4::Value::fromObject(v4->newRegExpObject(pattern, flags)); + } + case WorkerListModel: + { + void *ptr = popPtr(data); + QQmlListModelWorkerAgent *agent = (QQmlListModelWorkerAgent *)ptr; + QV4::Value rv = QV4::QObjectWrapper::wrap(v4, agent); + // ### Find a better solution then the ugly property + QQmlListModelWorkerAgent::VariantRef ref(agent); + QVariant var = qVariantFromValue(ref); + rv.asObject()->defineReadonlyProperty(v4->newString("__qml:hidden:ref"), engine->fromVariant(var)); + + agent->release(); + agent->setV8Engine(engine); + return rv; + } + case WorkerSequence: + { + bool succeeded = false; + quint32 length = headersize(header); + quint32 seqLength = length - 1; + int sequenceType = deserialize(data, engine).integerValue(); + QV4::ArrayObject *array = v4->newArrayObject(); + array->arrayReserve(seqLength); + array->arrayDataLen = seqLength; + for (quint32 ii = 0; ii < seqLength; ++ii) + array->arrayData[ii].value = deserialize(data, engine); + array->setArrayLengthUnchecked(seqLength); + QVariant seqVariant = QV4::SequencePrototype::toVariant(QV4::Value::fromObject(array), sequenceType, &succeeded); + return QV4::SequencePrototype::fromVariant(v4, seqVariant, &succeeded); + } + } + Q_ASSERT(!"Unreachable"); + return QV4::Value::undefinedValue(); +} + +QByteArray Serialize::serialize(const QV4::Value &value, QV8Engine *engine) +{ + QByteArray rv; + serialize(rv, value, engine); + return rv; +} + +QV4::Value Serialize::deserialize(const QByteArray &data, QV8Engine *engine) +{ + const char *stream = data.constData(); + return deserialize(stream, engine); +} + +QT_END_NAMESPACE + diff --git a/src/qml/qml/v4/qv4compiler_p.h b/src/qml/qml/v4/qv4serialize_p.h index 5b6cee2a55..5a04c9d25f 100644 --- a/src/qml/qml/v4/qv4compiler_p.h +++ b/src/qml/qml/v4/qv4serialize_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef QV4COMPILER_P_H -#define QV4COMPILER_P_H +#ifndef QV4SERIALIZE_P_H +#define QV4SERIALIZE_P_H // // W A R N I N G @@ -53,53 +53,28 @@ // We mean it. // -#include <private/qqmlexpression_p.h> -#include <private/qqmlbinding_p.h> -#include <private/qqmlcompiler_p.h> - -#include <private/qv8_p.h> - -Q_DECLARE_METATYPE(v8::Handle<v8::Value>) +#include <QtCore/qbytearray.h> +#include <private/qv4value_p.h> QT_BEGIN_NAMESPACE -class QQmlTypeNameCache; -class QV4CompilerPrivate; -class Q_AUTOTEST_EXPORT QV4Compiler -{ -public: - QV4Compiler(); - ~QV4Compiler(); - - // Returns true if bindings were compiled - bool isValid() const; +class QV8Engine; - struct Expression - { - Expression(const QQmlImports &imp) : imports(imp) {} - QQmlScript::Object *component; - QQmlScript::Object *context; - QQmlScript::Property *property; - QQmlScript::Variant expression; - QQmlCompilerTypes::IdList *ids; - QQmlTypeNameCache *importCache; - QQmlImports imports; - }; +namespace QV4 { - // -1 on failure, otherwise the binding index to use - int compile(const Expression &, QQmlEnginePrivate *, bool *); +class Serialize { +public: - // Returns the compiled program - QByteArray program() const; + static QByteArray serialize(const Value &, QV8Engine *); + static Value deserialize(const QByteArray &, QV8Engine *); - static void dump(const QByteArray &); - static void enableBindingsTest(bool); - static void enableV4(bool); private: - QV4CompilerPrivate *d; + static void serialize(QByteArray &, const Value &, QV8Engine *); + static Value deserialize(const char *&, QV8Engine *); }; -QT_END_NAMESPACE +} -#endif // QV4COMPILER_P_H +QT_END_NAMESPACE +#endif // QV8WORKER_P_H diff --git a/src/qml/qml/v4/qv4sparsearray.cpp b/src/qml/qml/v4/qv4sparsearray.cpp new file mode 100644 index 0000000000..835a0d004f --- /dev/null +++ b/src/qml/qml/v4/qv4sparsearray.cpp @@ -0,0 +1,459 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4sparsearray_p.h" +#include "qv4runtime_p.h" +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include <stdlib.h> + +#ifdef QT_QMAP_DEBUG +# include <qstring.h> +# include <qvector.h> +#endif + +using namespace QV4; + +bool ArrayElementLessThan::operator()(const Property &p1, const Property &p2) const +{ + Value v1 = p1.value; + Value v2 = p2.value; + + if (v1.isUndefined()) + return false; + if (v2.isUndefined()) + return true; + if (!m_comparefn.isUndefined()) { + Value args[] = { v1, v2 }; + Value result = Value::undefinedValue(); + __qmljs_call_value(m_context, &result, /*thisObject*/0, m_comparefn, args, 2); + return result.toNumber() <= 0; + } + return v1.toString(m_context)->toQString() < v2.toString(m_context)->toQString(); +} + + +const SparseArrayNode *SparseArrayNode::nextNode() const +{ + const SparseArrayNode *n = this; + if (n->right) { + n = n->right; + while (n->left) + n = n->left; + } else { + const SparseArrayNode *y = n->parent(); + while (y && n == y->right) { + n = y; + y = n->parent(); + } + n = y; + } + return n; +} + +const SparseArrayNode *SparseArrayNode::previousNode() const +{ + const SparseArrayNode *n = this; + if (n->left) { + n = n->left; + while (n->right) + n = n->right; + } else { + const SparseArrayNode *y = n->parent(); + while (y && n == y->left) { + n = y; + y = n->parent(); + } + n = y; + } + return n; +} + +SparseArrayNode *SparseArrayNode::copy(SparseArray *d) const +{ + SparseArrayNode *n = d->createNode(size_left, 0, false); + n->value = value; + n->setColor(color()); + if (left) { + n->left = left->copy(d); + n->left->setParent(n); + } else { + n->left = 0; + } + if (right) { + n->right = right->copy(d); + n->right->setParent(n); + } else { + n->right = 0; + } + return n; +} + +/* + x y + \ / \ + y --> x b + / \ \ + a b a +*/ +void SparseArray::rotateLeft(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = x->right; + x->right = y->left; + if (y->left != 0) + y->left->setParent(x); + y->setParent(x->parent()); + if (x == root) + root = y; + else if (x == x->parent()->left) + x->parent()->left = y; + else + x->parent()->right = y; + y->left = x; + x->setParent(y); + y->size_left += x->size_left; +} + + +/* + x y + / / \ + y --> a x + / \ / + a b b +*/ +void SparseArray::rotateRight(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = x->left; + x->left = y->right; + if (y->right != 0) + y->right->setParent(x); + y->setParent(x->parent()); + if (x == root) + root = y; + else if (x == x->parent()->right) + x->parent()->right = y; + else + x->parent()->left = y; + y->right = x; + x->setParent(y); + x->size_left -= y->size_left; +} + + +void SparseArray::rebalance(SparseArrayNode *x) +{ + SparseArrayNode *&root = header.left; + x->setColor(SparseArrayNode::Red); + while (x != root && x->parent()->color() == SparseArrayNode::Red) { + if (x->parent() == x->parent()->parent()->left) { + SparseArrayNode *y = x->parent()->parent()->right; + if (y && y->color() == SparseArrayNode::Red) { + x->parent()->setColor(SparseArrayNode::Black); + y->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + x = x->parent()->parent(); + } else { + if (x == x->parent()->right) { + x = x->parent(); + rotateLeft(x); + } + x->parent()->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + rotateRight (x->parent()->parent()); + } + } else { + SparseArrayNode *y = x->parent()->parent()->left; + if (y && y->color() == SparseArrayNode::Red) { + x->parent()->setColor(SparseArrayNode::Black); + y->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + x = x->parent()->parent(); + } else { + if (x == x->parent()->left) { + x = x->parent(); + rotateRight(x); + } + x->parent()->setColor(SparseArrayNode::Black); + x->parent()->parent()->setColor(SparseArrayNode::Red); + rotateLeft(x->parent()->parent()); + } + } + } + root->setColor(SparseArrayNode::Black); +} + +void SparseArray::deleteNode(SparseArrayNode *z) +{ + SparseArrayNode *&root = header.left; + SparseArrayNode *y = z; + SparseArrayNode *x; + SparseArrayNode *x_parent; + if (y->left == 0) { + x = y->right; + if (y == mostLeftNode) { + if (x) + mostLeftNode = x; // It cannot have (left) children due the red black invariant. + else + mostLeftNode = y->parent(); + } + } else { + if (y->right == 0) { + x = y->left; + } else { + y = y->right; + while (y->left != 0) + y = y->left; + x = y->right; + } + } + if (y != z) { + z->left->setParent(y); + y->left = z->left; + if (y != z->right) { + x_parent = y->parent(); + if (x) + x->setParent(y->parent()); + y->parent()->left = x; + y->right = z->right; + z->right->setParent(y); + } else { + x_parent = y; + } + if (root == z) + root = y; + else if (z->parent()->left == z) + z->parent()->left = y; + else + z->parent()->right = y; + y->setParent(z->parent()); + // Swap the colors + SparseArrayNode::Color c = y->color(); + y->setColor(z->color()); + z->setColor(c); + y = z; + } else { + x_parent = y->parent(); + if (x) + x->setParent(y->parent()); + if (root == z) + root = x; + else if (z->parent()->left == z) + z->parent()->left = x; + else + z->parent()->right = x; + } + if (y->color() != SparseArrayNode::Red) { + while (x != root && (x == 0 || x->color() == SparseArrayNode::Black)) { + if (x == x_parent->left) { + SparseArrayNode *w = x_parent->right; + if (w->color() == SparseArrayNode::Red) { + w->setColor(SparseArrayNode::Black); + x_parent->setColor(SparseArrayNode::Red); + rotateLeft(x_parent); + w = x_parent->right; + } + if ((w->left == 0 || w->left->color() == SparseArrayNode::Black) && + (w->right == 0 || w->right->color() == SparseArrayNode::Black)) { + w->setColor(SparseArrayNode::Red); + x = x_parent; + x_parent = x_parent->parent(); + } else { + if (w->right == 0 || w->right->color() == SparseArrayNode::Black) { + if (w->left) + w->left->setColor(SparseArrayNode::Black); + w->setColor(SparseArrayNode::Red); + rotateRight(w); + w = x_parent->right; + } + w->setColor(x_parent->color()); + x_parent->setColor(SparseArrayNode::Black); + if (w->right) + w->right->setColor(SparseArrayNode::Black); + rotateLeft(x_parent); + break; + } + } else { + SparseArrayNode *w = x_parent->left; + if (w->color() == SparseArrayNode::Red) { + w->setColor(SparseArrayNode::Black); + x_parent->setColor(SparseArrayNode::Red); + rotateRight(x_parent); + w = x_parent->left; + } + if ((w->right == 0 || w->right->color() == SparseArrayNode::Black) && + (w->left == 0 || w->left->color() == SparseArrayNode::Black)) { + w->setColor(SparseArrayNode::Red); + x = x_parent; + x_parent = x_parent->parent(); + } else { + if (w->left == 0 || w->left->color() == SparseArrayNode::Black) { + if (w->right) + w->right->setColor(SparseArrayNode::Black); + w->setColor(SparseArrayNode::Red); + rotateLeft(w); + w = x_parent->left; + } + w->setColor(x_parent->color()); + x_parent->setColor(SparseArrayNode::Black); + if (w->left) + w->left->setColor(SparseArrayNode::Black); + rotateRight(x_parent); + break; + } + } + } + if (x) + x->setColor(SparseArrayNode::Black); + } + free(y); + --numEntries; +} + +void SparseArray::recalcMostLeftNode() +{ + mostLeftNode = &header; + while (mostLeftNode->left) + mostLeftNode = mostLeftNode->left; +} + +static inline int qMapAlignmentThreshold() +{ + // malloc on 32-bit platforms should return pointers that are 8-byte + // aligned or more while on 64-bit platforms they should be 16-byte aligned + // or more + return 2 * sizeof(void*); +} + +static inline void *qMapAllocate(int alloc, int alignment) +{ + return alignment > qMapAlignmentThreshold() + ? qMallocAligned(alloc, alignment) + : ::malloc(alloc); +} + +static inline void qMapDeallocate(SparseArrayNode *node, int alignment) +{ + if (alignment > qMapAlignmentThreshold()) + qFreeAligned(node); + else + ::free(node); +} + +SparseArrayNode *SparseArray::createNode(uint sl, SparseArrayNode *parent, bool left) +{ + SparseArrayNode *node = static_cast<SparseArrayNode *>(qMapAllocate(sizeof(SparseArrayNode), Q_ALIGNOF(SparseArrayNode))); + Q_CHECK_PTR(node); + + node->p = (quintptr)parent; + node->left = 0; + node->right = 0; + node->size_left = sl; + node->value = UINT_MAX; + ++numEntries; + + if (parent) { + if (left) { + parent->left = node; + if (parent == mostLeftNode) + mostLeftNode = node; + } else { + parent->right = node; + } + node->setParent(parent); + rebalance(node); + } + return node; +} + +void SparseArray::freeTree(SparseArrayNode *root, int alignment) +{ + if (root->left) + freeTree(root->left, alignment); + if (root->right) + freeTree(root->right, alignment); + qMapDeallocate(root, alignment); +} + +SparseArray::SparseArray() + : numEntries(0) +{ + header.p = 0; + header.left = 0; + header.right = 0; + mostLeftNode = &header; +} + +SparseArray::SparseArray(const SparseArray &other) +{ + header.p = 0; + header.right = 0; + if (other.header.left) { + header.left = other.header.left->copy(this); + header.left->setParent(&header); + recalcMostLeftNode(); + } +} + +SparseArrayNode *SparseArray::insert(uint akey) +{ + SparseArrayNode *n = root(); + SparseArrayNode *y = end(); + bool left = true; + uint s = akey; + while (n) { + y = n; + if (s == n->size_left) { + return n; + } else if (s < n->size_left) { + left = true; + n = n->left; + } else { + left = false; + s -= n->size_left; + n = n->right; + } + } + + return createNode(s, y, left); +} diff --git a/src/qml/qml/v4/qv4sparsearray_p.h b/src/qml/qml/v4/qv4sparsearray_p.h new file mode 100644 index 0000000000..384d2ef045 --- /dev/null +++ b/src/qml/qml/v4/qv4sparsearray_p.h @@ -0,0 +1,367 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ARRAY_H +#define QV4ARRAY_H + +#include "qv4global_p.h" +#include <QtCore/qmap.h> +#include "qv4value_p.h" +#include "qv4property_p.h" +#include <assert.h> + +#ifdef Q_MAP_DEBUG +#include <QtCore/qdebug.h> +#endif + +#include <new> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct SparseArray; + +class ArrayElementLessThan +{ +public: + inline ArrayElementLessThan(ExecutionContext *context, Object *thisObject, const Value &comparefn) + : m_context(context), thisObject(thisObject), m_comparefn(comparefn) {} + + bool operator()(const Property &v1, const Property &v2) const; + +private: + ExecutionContext *m_context; + Object *thisObject; + Value m_comparefn; +}; + + +struct SparseArrayNode +{ + quintptr p; + SparseArrayNode *left; + SparseArrayNode *right; + uint size_left; + uint value; + + enum Color { Red = 0, Black = 1 }; + enum { Mask = 3 }; // reserve the second bit as well + + const SparseArrayNode *nextNode() const; + SparseArrayNode *nextNode() { return const_cast<SparseArrayNode *>(const_cast<const SparseArrayNode *>(this)->nextNode()); } + const SparseArrayNode *previousNode() const; + SparseArrayNode *previousNode() { return const_cast<SparseArrayNode *>(const_cast<const SparseArrayNode *>(this)->previousNode()); } + + Color color() const { return Color(p & 1); } + void setColor(Color c) { if (c == Black) p |= Black; else p &= ~Black; } + SparseArrayNode *parent() const { return reinterpret_cast<SparseArrayNode *>(p & ~Mask); } + void setParent(SparseArrayNode *pp) { p = (p & Mask) | quintptr(pp); } + + uint key() const { + uint k = size_left; + const SparseArrayNode *n = this; + while (SparseArrayNode *p = n->parent()) { + if (p && p->right == n) + k += p->size_left; + n = p; + } + return k; + } + + SparseArrayNode *copy(SparseArray *d) const; + + SparseArrayNode *lowerBound(uint key); + SparseArrayNode *upperBound(uint key); +}; + + +inline SparseArrayNode *SparseArrayNode::lowerBound(uint akey) +{ + SparseArrayNode *n = this; + SparseArrayNode *last = 0; + while (n) { + if (akey <= n->size_left) { + last = n; + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + return last; +} + + +inline SparseArrayNode *SparseArrayNode::upperBound(uint akey) +{ + SparseArrayNode *n = this; + SparseArrayNode *last = 0; + while (n) { + if (akey < n->size_left) { + last = n; + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + return last; +} + + + +struct Q_QML_EXPORT SparseArray +{ + SparseArray(); + ~SparseArray() { + if (root()) + freeTree(header.left, Q_ALIGNOF(SparseArrayNode)); + } + + SparseArray(const SparseArray &other); +private: + SparseArray &operator=(const SparseArray &other); + + int numEntries; + SparseArrayNode header; + SparseArrayNode *mostLeftNode; + + void rotateLeft(SparseArrayNode *x); + void rotateRight(SparseArrayNode *x); + void rebalance(SparseArrayNode *x); + void recalcMostLeftNode(); + + SparseArrayNode *root() const { return header.left; } + + void deleteNode(SparseArrayNode *z); + + +public: + SparseArrayNode *createNode(uint sl, SparseArrayNode *parent, bool left); + void freeTree(SparseArrayNode *root, int alignment); + + SparseArrayNode *findNode(uint akey) const; + + uint pop_front(); + void push_front(uint at); + uint pop_back(uint len); + void push_back(uint at, uint len); + + QList<int> keys() const; + + const SparseArrayNode *end() const { return &header; } + SparseArrayNode *end() { return &header; } + const SparseArrayNode *begin() const { if (root()) return mostLeftNode; return end(); } + SparseArrayNode *begin() { if (root()) return mostLeftNode; return end(); } + + SparseArrayNode *erase(SparseArrayNode *n); + + SparseArrayNode *lowerBound(uint key); + const SparseArrayNode *lowerBound(uint key) const; + SparseArrayNode *upperBound(uint key); + const SparseArrayNode *upperBound(uint key) const; + SparseArrayNode *insert(uint akey); + + // STL compatibility + typedef uint key_type; + typedef int mapped_type; + typedef qptrdiff difference_type; + typedef int size_type; + +#ifdef Q_MAP_DEBUG + void dump() const; +#endif +}; + +inline SparseArrayNode *SparseArray::findNode(uint akey) const +{ + SparseArrayNode *n = root(); + + while (n) { + if (akey == n->size_left) { + return n; + } else if (akey < n->size_left) { + n = n->left; + } else { + akey -= n->size_left; + n = n->right; + } + } + + return 0; +} + +inline uint SparseArray::pop_front() +{ + uint idx = UINT_MAX ; + + SparseArrayNode *n = findNode(0); + if (n) { + idx = n->value; + deleteNode(n); + // adjust all size_left indices on the path to leftmost item by 1 + SparseArrayNode *n = root(); + while (n) { + n->size_left -= 1; + n = n->left; + } + } + return idx; +} + +inline void SparseArray::push_front(uint value) +{ + // adjust all size_left indices on the path to leftmost item by 1 + SparseArrayNode *n = root(); + while (n) { + n->size_left += 1; + n = n->left; + } + n = insert(0); + n->value = value; +} + +inline uint SparseArray::pop_back(uint len) +{ + uint idx = UINT_MAX; + if (!len) + return idx; + + SparseArrayNode *n = findNode(len - 1); + if (n) { + idx = n->value; + deleteNode(n); + } + return idx; +} + +inline void SparseArray::push_back(uint index, uint len) +{ + SparseArrayNode *n = insert(len); + n->value = index; +} + +#ifdef Q_MAP_DEBUG + +void SparseArray::dump() const +{ + const_iterator it = begin(); + qDebug() << "map dump:"; + while (it != end()) { + const SparseArrayNode *n = it.i; + int depth = 0; + while (n && n != root()) { + ++depth; + n = n->parent(); + } + QByteArray space(4*depth, ' '); + qDebug() << space << (it.i->color() == SparseArrayNode::Red ? "Red " : "Black") << it.i << it.i->left << it.i->right + << it.key() << it.value(); + ++it; + } + qDebug() << "---------"; +} +#endif + + +inline SparseArrayNode *SparseArray::erase(SparseArrayNode *n) +{ + if (n == end()) + return n; + + SparseArrayNode *next = n->nextNode(); + deleteNode(n); + return next; +} + +inline QList<int> SparseArray::keys() const +{ + QList<int> res; + res.reserve(numEntries); + SparseArrayNode *n = mostLeftNode; + while (n != end()) { + res.append(n->key()); + n = n->nextNode(); + } + return res; +} + +inline const SparseArrayNode *SparseArray::lowerBound(uint akey) const +{ + const SparseArrayNode *lb = root()->lowerBound(akey); + if (!lb) + lb = end(); + return lb; +} + + +inline SparseArrayNode *SparseArray::lowerBound(uint akey) +{ + SparseArrayNode *lb = root()->lowerBound(akey); + if (!lb) + lb = end(); + return lb; +} + + +inline const SparseArrayNode *SparseArray::upperBound(uint akey) const +{ + const SparseArrayNode *ub = root()->upperBound(akey); + if (!ub) + ub = end(); + return ub; +} + + +inline SparseArrayNode *SparseArray::upperBound(uint akey) +{ + SparseArrayNode *ub = root()->upperBound(akey); + if (!ub) + ub = end(); + return ub; +} + +} + +QT_END_NAMESPACE + +#endif // QMAP_H diff --git a/src/qml/qml/v4/qv4ssa.cpp b/src/qml/qml/v4/qv4ssa.cpp new file mode 100644 index 0000000000..a139ed9fff --- /dev/null +++ b/src/qml/qml/v4/qv4ssa.cpp @@ -0,0 +1,2122 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4ssa_p.h" +#include "qv4util_p.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QStringList> +#include <QtCore/QSet> +#include <QtCore/QBuffer> +#include <QtCore/QLinkedList> +#include <QtCore/QStack> +#include <qv4runtime_p.h> +#include <qv4context_p.h> +#include <cmath> +#include <iostream> +#include <cassert> + +#ifdef CONST +#undef CONST +#endif + +#define QV4_NO_LIVENESS +#undef SHOW_SSA + +QT_USE_NAMESPACE + +using namespace QQmlJS; +using namespace V4IR; + +namespace { + +QTextStream qout(stdout, QIODevice::WriteOnly); + +void showMeTheCode(Function *function) +{ + static bool showCode = !qgetenv("SHOW_CODE").isNull(); + if (showCode) { + QVector<Stmt *> code; + QHash<Stmt *, BasicBlock *> leader; + + foreach (BasicBlock *block, function->basicBlocks) { + if (block->statements.isEmpty()) + continue; + leader.insert(block->statements.first(), block); + foreach (Stmt *s, block->statements) { + code.append(s); + } + } + + QString name; + if (function->name && !function->name->isEmpty()) + name = *function->name; + else + name.sprintf("%p", function); + + qout << "function " << name << "("; + for (int i = 0; i < function->formals.size(); ++i) { + if (i != 0) + qout << ", "; + qout << *function->formals.at(i); + } + qout << ")" << endl + << "{" << endl; + + foreach (const QString *local, function->locals) { + qout << " var " << *local << ';' << endl; + } + + for (int i = 0; i < code.size(); ++i) { + Stmt *s = code.at(i); + + if (BasicBlock *bb = leader.value(s)) { + qout << endl; + QByteArray str; + str.append('L'); + str.append(QByteArray::number(bb->index)); + str.append(':'); + for (int i = 66 - str.length(); i; --i) + str.append(' '); + qout << str; + qout << "// predecessor blocks:"; + foreach (BasicBlock *in, bb->in) + qout << " L" << in->index; + if (bb->in.isEmpty()) + qout << "(none)"; + if (BasicBlock *container = bb->containingGroup()) + qout << "; container block: L" << container->index; + if (bb->isGroupStart()) + qout << "; group start"; + qout << endl; + } + Stmt *n = (i + 1) < code.size() ? code.at(i + 1) : 0; +// if (n && s->asJump() && s->asJump()->target == leader.value(n)) { +// continue; +// } + + QByteArray str; + QBuffer buf(&str); + buf.open(QIODevice::WriteOnly); + QTextStream out(&buf); + if (s->id > 0) + out << s->id << ": "; + s->dump(out, Stmt::MIR); + out.flush(); + + if (s->location.isValid()) + qout << " // line: " << s->location.startLine << " column: " << s->location.startColumn << endl; + +#ifndef QV4_NO_LIVENESS + for (int i = 60 - str.size(); i >= 0; --i) + str.append(' '); + + qout << " " << str; + + // if (! s->uses.isEmpty()) { + // qout << " // uses:"; + // foreach (unsigned use, s->uses) { + // qout << " %" << use; + // } + // } + + // if (! s->defs.isEmpty()) { + // qout << " // defs:"; + // foreach (unsigned def, s->defs) { + // qout << " %" << def; + // } + // } + +# if 0 + if (! s->d->liveIn.isEmpty()) { + qout << " // lives in:"; + for (int i = 0; i < s->d->liveIn.size(); ++i) { + if (s->d->liveIn.testBit(i)) + qout << " %" << i; + } + } +# else + if (! s->d->liveOut.isEmpty()) { + qout << " // lives out:"; + for (int i = 0; i < s->d->liveOut.size(); ++i) { + if (s->d->liveOut.testBit(i)) + qout << " %" << i; + } + } +# endif +#else + qout << " " << str; +#endif + + qout << endl; + + if (n && s->asCJump() /*&& s->asCJump()->iffalse != leader.value(n)*/) { + qout << " else goto L" << s->asCJump()->iffalse->index << ";" << endl; + } + } + + qout << "}" << endl + << endl; + } +} + +class DominatorTree { + int N; + QHash<BasicBlock *, int> dfnum; + QVector<BasicBlock *> vertex; + QHash<BasicBlock *, BasicBlock *> parent; + QHash<BasicBlock *, BasicBlock *> ancestor; + QHash<BasicBlock *, BasicBlock *> best; + QHash<BasicBlock *, BasicBlock *> semi; + QHash<BasicBlock *, BasicBlock *> idom; + QHash<BasicBlock *, BasicBlock *> samedom; + QHash<BasicBlock *, QSet<BasicBlock *> > bucket; + + void DFS(BasicBlock *p, BasicBlock *n) { + if (dfnum[n] == 0) { + dfnum[n] = N; + vertex[N] = n; + parent[n] = p; + ++N; + foreach (BasicBlock *w, n->out) + DFS(n, w); + } + } + + BasicBlock *ancestorWithLowestSemi(BasicBlock *v) { + BasicBlock *a = ancestor[v]; + if (ancestor[a]) { + BasicBlock *b = ancestorWithLowestSemi(a); + ancestor[v] = ancestor[a]; + if (dfnum[semi[b]] < dfnum[semi[best[v]]]) + best[v] = b; + } + return best[v]; + } + + void link(BasicBlock *p, BasicBlock *n) { + ancestor[n] = p; + best[n] = n; + } + + void calculateIDoms(const QVector<BasicBlock *> &nodes) { + Q_ASSERT(nodes.first()->in.isEmpty()); + vertex.resize(nodes.size()); + foreach (BasicBlock *n, nodes) { + dfnum[n] = 0; + semi[n] = 0; + ancestor[n] = 0; + idom[n] = 0; + samedom[n] = 0; + } + + DFS(0, nodes.first()); + Q_ASSERT(N == nodes.size()); // fails with unreachable nodes... + + for (int i = N - 1; i > 0; --i) { + BasicBlock *n = vertex[i]; + BasicBlock *p = parent[n]; + BasicBlock *s = p; + + foreach (BasicBlock *v, n->in) { + BasicBlock *ss; + if (dfnum[v] <= dfnum[n]) + ss = v; + else + ss = semi[ancestorWithLowestSemi(v)]; + if (dfnum[ss] < dfnum[s]) + s = ss; + } + semi[n] = s; + bucket[s].insert(n); + link(p, n); + foreach (BasicBlock *v, bucket[p]) { + BasicBlock *y = ancestorWithLowestSemi(v); + Q_ASSERT(semi[y] == p); + if (semi[y] == semi[v]) + idom[v] = p; + else + samedom[v] = y; + } + bucket[p].clear(); + } + for (int i = 1; i < N; ++i) { + BasicBlock *n = vertex[i]; + Q_ASSERT(ancestor[n] && ((semi[n] && dfnum[ancestor[n]] <= dfnum[semi[n]]) || semi[n] == n)); + Q_ASSERT(bucket[n].isEmpty()); + if (BasicBlock *sdn = samedom[n]) + idom[n] = idom[sdn]; + } + +#ifdef SHOW_SSA + qout << "Immediate dominators:" << endl; + foreach (BasicBlock *to, nodes) { + qout << '\t'; + if (BasicBlock *from = idom.value(to)) + qout << from->index; + else + qout << "(none)"; + qout << " -> " << to->index << endl; + } +#endif // SHOW_SSA + } + + bool dominates(BasicBlock *dominator, BasicBlock *dominated) const { + for (BasicBlock *it = dominated; it; it = idom[it]) { + if (it == dominator) + return true; + } + + return false; + } + + void computeDF(BasicBlock *n) { + if (DF.contains(n)) + return; // TODO: verify this! + + QSet<BasicBlock *> S; + foreach (BasicBlock *y, n->out) + if (idom[y] != n) + S.insert(y); + + /* + * foreach child c of n in the dominator tree + * computeDF[c] + * foreach element w of DF[c] + * if n does not dominate w or if n = w + * S.insert(w) + * DF[n] = S; + */ + foreach (BasicBlock *c, children[n]) { + computeDF(c); + foreach (BasicBlock *w, DF[c]) + if (!dominates(n, w) || n == w) + S.insert(w); + } + DF[n] = S; + +#ifdef SHOW_SSA + qout << "\tDF[" << n->index << "]: {"; + QList<BasicBlock *> SList = S.values(); + for (int i = 0; i < SList.size(); ++i) { + if (i > 0) + qout << ", "; + qout << SList[i]->index; + } + qout << "}" << endl; +#endif // SHOW_SSA +#ifndef QT_NO_DEBUG + foreach (BasicBlock *fBlock, S) { + Q_ASSERT(!dominates(n, fBlock) || fBlock == n); + bool hasDominatedSucc = false; + foreach (BasicBlock *succ, fBlock->in) + if (dominates(n, succ)) + hasDominatedSucc = true; + if (!hasDominatedSucc) { + qout << fBlock->index << " in DF[" << n->index << "] has no dominated predecessors" << endl; + } + Q_ASSERT(hasDominatedSucc); + } +#endif // !QT_NO_DEBUG + } + + QHash<BasicBlock *, QSet<BasicBlock *> > children; + QHash<BasicBlock *, QSet<BasicBlock *> > DF; + +public: + DominatorTree(const QVector<BasicBlock *> &nodes) + : N(0) + { + calculateIDoms(nodes); + + // compute children of n + foreach (BasicBlock *n, nodes) + children[idom[n]].insert(n); + +#ifdef SHOW_SSA + qout << "Dominator Frontiers:" << endl; +#endif // SHOW_SSA + foreach (BasicBlock *n, nodes) + computeDF(n); + } + + QSet<BasicBlock *> operator[](BasicBlock *n) const { + return DF[n]; + } + + BasicBlock *immediateDominator(BasicBlock *bb) const { + return idom[bb]; + } +}; + +class VariableCollector: public StmtVisitor, ExprVisitor { + QHash<Temp, QSet<BasicBlock *> > _defsites; + QHash<BasicBlock *, QSet<Temp> > A_orig; + QSet<Temp> nonLocals; + QSet<Temp> killed; + + BasicBlock *currentBB; + const bool variablesCanEscape; + bool isCollectable(Temp *t) const + { + switch (t->kind) { + case Temp::Formal: + case Temp::ScopedFormal: + case Temp::ScopedLocal: + return false; + case Temp::Local: + return !variablesCanEscape; + case Temp::VirtualRegister: + return true; + default: + // PhysicalRegister and StackSlot can only get inserted later. + Q_ASSERT(!"Invalid temp kind!"); + return false; + } + } + +public: + VariableCollector(Function *function) + : variablesCanEscape(function->variablesCanEscape()) + { +#ifdef SHOW_SSA + qout << "Variables collected:" << endl; +#endif // SHOW_SSA + + foreach (BasicBlock *bb, function->basicBlocks) { + currentBB = bb; + killed.clear(); + killed.reserve(bb->statements.size() / 2); + foreach (Stmt *s, bb->statements) { + s->accept(this); + } + } + +#ifdef SHOW_SSA + qout << "Non-locals:" << endl; + foreach (const Temp &nonLocal, nonLocals) { + qout << "\t"; + nonLocal.dump(qout); + qout << endl; + } + + qout << "end collected variables." << endl; +#endif // SHOW_SSA + } + + QList<Temp> vars() const { + return _defsites.keys(); + } + + QSet<BasicBlock *> defsite(const Temp &n) const { + return _defsites[n]; + } + + QSet<Temp> inBlock(BasicBlock *n) const { + return A_orig[n]; + } + + bool isNonLocal(const Temp &var) const { return nonLocals.contains(var); } + +protected: + virtual void visitPhi(Phi *) {}; + virtual void visitConvert(Convert *e) { e->expr->accept(this); }; + + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitClosure(Closure *) {} + virtual void visitUnop(Unop *e) { e->expr->accept(this); } + virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(Member *e) { e->base->accept(this); } + virtual void visitExp(Exp *s) { s->expr->accept(this); } + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *s) { s->cond->accept(this); } + virtual void visitRet(Ret *s) { s->expr->accept(this); } + virtual void visitTry(Try *) { // ### TODO + } + + virtual void visitCall(Call *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(New *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitMove(Move *s) { + s->source->accept(this); + + if (Temp *t = s->target->asTemp()) { + if (isCollectable(t)) { +#ifdef SHOW_SSA + qout << '\t'; + t->dump(qout); + qout << " -> L" << currentBB->index << endl; +#endif // SHOW_SSA + + _defsites[*t].insert(currentBB); + A_orig[currentBB].insert(*t); + + // For semi-pruned SSA: + killed.insert(*t); + } + } + } + + virtual void visitTemp(Temp *t) + { + if (isCollectable(t)) + if (!killed.contains(*t)) + nonLocals.insert(*t); + } +}; + +void insertPhiNode(const Temp &a, BasicBlock *y, Function *f) { +#if defined(SHOW_SSA) + qout << "-> inserted phi node for variable "; + a.dump(qout); + qout << " in block " << y->index << endl; +#endif + + Phi *phiNode = f->New<Phi>(); + phiNode->targetTemp = f->New<Temp>(); + phiNode->targetTemp->init(a.kind, a.index, 0); + y->statements.prepend(phiNode); + + phiNode->incoming.resize(y->in.size()); + for (int i = 0, ei = y->in.size(); i < ei; ++i) { + Temp *t = f->New<Temp>(); + t->init(a.kind, a.index, 0); + phiNode->incoming[i] = t; + } +} + +class VariableRenamer: public StmtVisitor, public ExprVisitor +{ + Function *function; + QHash<Temp, QStack<unsigned> > stack; + QSet<BasicBlock *> seen; + + QHash<Temp, unsigned> defCounts; + + const bool variablesCanEscape; + bool isRenamable(Temp *t) const + { + switch (t->kind) { + case Temp::Formal: + case Temp::ScopedFormal: + case Temp::ScopedLocal: + return false; + case Temp::Local: + return !variablesCanEscape; + case Temp::VirtualRegister: + return true; + default: + Q_ASSERT(!"Invalid temp kind!"); + return false; + } + } + int nextFreeTemp() { + const int next = function->tempCount++; +// qDebug()<<"Next free temp:"<<next; + return next; + } + + /* + + Initialization: + for each variable a + count[a] = 0; + stack[a] = empty; + push 0 onto stack + + Rename(n) = + for each statement S in block n [1] + if S not in a phi-function + for each use of some variable x in S + i = top(stack[x]) + replace the use of x with x_i in S + for each definition of some variable a in S + count[a] = count[a] + 1 + i = count[a] + push i onto stack[a] + replace definition of a with definition of a_i in S + for each successor Y of block n [2] + Suppose n is the j-th predecessor of Y + for each phi function in Y + suppose the j-th operand of the phi-function is a + i = top(stack[a]) + replace the j-th operand with a_i + for each child X of n [3] + Rename(X) + for each statement S in block n [4] + for each definition of some variable a in S + pop stack[a] + + */ + +public: + VariableRenamer(Function *f) + : function(f) + , variablesCanEscape(f->variablesCanEscape()) + { + if (!variablesCanEscape) { + Temp t; + t.init(Temp::Local, 0, 0); + for (int i = 0, ei = f->locals.size(); i != ei; ++i) { + t.index = i; + stack[t].push(nextFreeTemp()); + } + } + + Temp t; + t.init(Temp::VirtualRegister, 0, 0); + for (int i = 0, ei = f->tempCount; i != ei; ++i) { + t.index = i; + stack[t].push(i); + } + } + + void run() { + foreach (BasicBlock *n, function->basicBlocks) + rename(n); + +#ifdef SHOW_SSA +// qout << "Temp to local mapping:" << endl; +// foreach (int key, tempMapping.keys()) +// qout << '\t' << key << " -> " << tempMapping[key] << endl; +#endif + } + + void rename(BasicBlock *n) { + if (seen.contains(n)) + return; + seen.insert(n); +// qDebug() << "I: L"<<n->index; + + // [1]: + foreach (Stmt *s, n->statements) + s->accept(this); + + QHash<Temp, unsigned> dc = defCounts; + defCounts.clear(); + + // [2]: + foreach (BasicBlock *Y, n->out) { + const int j = Y->in.indexOf(n); + Q_ASSERT(j >= 0 && j < Y->in.size()); + foreach (Stmt *s, Y->statements) { + if (Phi *phi = s->asPhi()) { + Temp *t = phi->incoming[j]->asTemp(); + unsigned newTmp = stack[*t].top(); +// qDebug()<<"I: replacing phi use"<<a<<"with"<<newTmp<<"in L"<<Y->index; + t->index = newTmp; + t->kind = Temp::VirtualRegister; + } else { + break; + } + } + } + + // [3]: + foreach (BasicBlock *X, n->out) + rename(X); + + // [4]: + for (QHash<Temp, unsigned>::const_iterator i = dc.begin(), ei = dc.end(); i != ei; ++i) { +// qDebug()<<i.key() <<" -> " << i.value(); + for (unsigned j = 0, ej = i.value(); j < ej; ++j) + stack[i.key()].pop(); + } + } + +protected: + virtual void visitTemp(Temp *e) { // only called for uses, not defs + if (isRenamable(e)) { +// qDebug()<<"I: replacing use of"<<e->index<<"with"<<stack[e->index].top(); + e->index = stack[*e].top(); + e->kind = Temp::VirtualRegister; + } + } + + virtual void visitMove(Move *s) { + // uses: + s->source->accept(this); + + // defs: + if (Temp *t = s->target->asTemp()) + renameTemp(t); + else + s->target->accept(this); + } + + void renameTemp(Temp *t) { + if (isRenamable(t)) { + defCounts[*t] = defCounts.value(*t, 0) + 1; + const int newIdx = nextFreeTemp(); + stack[*t].push(newIdx); +// qDebug()<<"I: replacing def of"<<a<<"with"<<newIdx; + t->kind = Temp::VirtualRegister; + t->index = newIdx; + } + } + + virtual void visitConvert(Convert *e) { e->expr->accept(this); } + virtual void visitPhi(Phi *s) { renameTemp(s->targetTemp); } + + virtual void visitExp(Exp *s) { s->expr->accept(this); } + + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *s) { s->cond->accept(this); } + virtual void visitRet(Ret *s) { s->expr->accept(this); } + virtual void visitTry(Try *s) { /* this should never happen */ } + + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitClosure(Closure *) {} + virtual void visitUnop(Unop *e) { e->expr->accept(this); } + virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitCall(Call *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(New *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitSubscript(Subscript *e) { + e->base->accept(this); + e->index->accept(this); + } + + virtual void visitMember(Member *e) { + e->base->accept(this); + } +}; + +void convertToSSA(Function *function, const DominatorTree &df) +{ +#ifdef SHOW_SSA + qout << "Converting function "; + if (function->name) + qout << *function->name; + else + qout << "<no name>"; + qout << " to SSA..." << endl; +#endif // SHOW_SSA + + // Collect all applicable variables: + VariableCollector variables(function); + + // Place phi functions: + QHash<BasicBlock *, QSet<Temp> > A_phi; + foreach (Temp a, variables.vars()) { + if (!variables.isNonLocal(a)) + continue; // for semi-pruned SSA + + QList<BasicBlock *> W = QList<BasicBlock *>::fromSet(variables.defsite(a)); + while (!W.isEmpty()) { + BasicBlock *n = W.first(); + W.removeFirst(); + foreach (BasicBlock *y, df[n]) { + if (!A_phi[y].contains(a)) { + insertPhiNode(a, y, function); + A_phi[y].insert(a); + if (!variables.inBlock(y).contains(a)) + W.append(y); + } + } + } + } + showMeTheCode(function); + + // Rename variables: + VariableRenamer(function).run(); +} + +class DefUsesCalculator: public StmtVisitor, public ExprVisitor { +public: + struct DefUse { + DefUse() + : defStmt(0) + , blockOfStatement(0) + {} + Stmt *defStmt; + BasicBlock *blockOfStatement; + QList<Stmt *> uses; + }; + +private: + const bool _variablesCanEscape; + QHash<Temp, DefUse> _defUses; + QHash<Stmt *, QList<Temp> > _usesPerStatement; + + BasicBlock *_block; + Stmt *_stmt; + + bool isCollectible(Temp *t) const { + switch (t->kind) { + case Temp::Formal: + case Temp::ScopedFormal: + case Temp::ScopedLocal: + return false; + case Temp::Local: + return !_variablesCanEscape; + case Temp::VirtualRegister: + return true; + default: + Q_UNREACHABLE(); + return false; + } + } + + void addUse(Temp *t) { + Q_ASSERT(t); + if (!isCollectible(t)) + return; + + _defUses[*t].uses.append(_stmt); + _usesPerStatement[_stmt].append(*t); + } + + void addDef(Temp *t) { + if (!isCollectible(t)) + return; + + Q_ASSERT(!_defUses.contains(*t) || _defUses.value(*t).defStmt == 0 || _defUses.value(*t).defStmt == _stmt); + + DefUse &defUse = _defUses[*t]; + defUse.defStmt = _stmt; + defUse.blockOfStatement = _block; + } + +public: + DefUsesCalculator(Function *function) + : _variablesCanEscape(function->variablesCanEscape()) + { + foreach (BasicBlock *bb, function->basicBlocks) { + _block = bb; + foreach (Stmt *stmt, bb->statements) { + _stmt = stmt; + stmt->accept(this); + } + } + + QMutableHashIterator<Temp, DefUse> it(_defUses); + while (it.hasNext()) { + it.next(); + if (!it.value().defStmt) + it.remove(); + } + } + + QList<Temp> defs() const { + return _defUses.keys(); + } + + void removeDef(const Temp &var) { + _defUses.remove(var); + } + + void addUses(const Temp &variable, const QList<Stmt *> &newUses) + { _defUses[variable].uses.append(newUses); } + + int useCount(const Temp &variable) const + { return _defUses[variable].uses.size(); } + + Stmt *defStmt(const Temp &variable) const + { return _defUses[variable].defStmt; } + + BasicBlock *defStmtBlock(const Temp &variable) const + { return _defUses[variable].blockOfStatement; } + + void removeUse(Stmt *usingStmt, const Temp &var) + { _defUses[var].uses.removeAll(usingStmt); } + + QList<Temp> usedVars(Stmt *s) const + { return _usesPerStatement[s]; } + + QList<Stmt *> uses(const Temp &var) const + { return _defUses[var].uses; } + + void dump() const + { + foreach (const Temp &var, _defUses.keys()) { + const DefUse &du = _defUses[var]; + var.dump(qout); + qout<<" -> defined in block "<<du.blockOfStatement->index<<", statement: "; + du.defStmt->dump(qout); + qout<<endl<<" uses:"<<endl; + foreach (Stmt *s, du.uses) { + qout<<" ";s->dump(qout);qout<<endl; + } + } + } + +protected: + virtual void visitExp(Exp *s) { s->expr->accept(this); } + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *s) { s->cond->accept(this); } + virtual void visitRet(Ret *s) { s->expr->accept(this); } + virtual void visitTry(Try *) {} + + virtual void visitPhi(Phi *s) { + addDef(s->targetTemp); + foreach (Expr *e, s->incoming) + addUse(e->asTemp()); + } + + virtual void visitMove(Move *s) { + if (Temp *t = s->target->asTemp()) + addDef(t); + else + s->target->accept(this); + + s->source->accept(this); + } + + virtual void visitTemp(Temp *e) { addUse(e); } + + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitClosure(Closure *) {} + virtual void visitConvert(Convert *e) { e->expr->accept(this); } + virtual void visitUnop(Unop *e) { e->expr->accept(this); } + virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(Member *e) { e->base->accept(this); } + virtual void visitCall(Call *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(New *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } +}; + +bool hasPhiOnlyUses(Phi *phi, const DefUsesCalculator &defUses, QSet<Phi *> &collectedPhis) +{ + collectedPhis.insert(phi); + foreach (Stmt *use, defUses.uses(*phi->targetTemp)) { + if (Phi *dependentPhi = use->asPhi()) { + if (!collectedPhis.contains(dependentPhi)) { + if (!hasPhiOnlyUses(dependentPhi, defUses, collectedPhis)) + return false; + } + } else { + return false; + } + } + return true; +} + +void cleanupPhis(DefUsesCalculator &defUses) +{ + QLinkedList<Phi *> phis; + foreach (const Temp &def, defUses.defs()) + if (Phi *phi = defUses.defStmt(def)->asPhi()) + phis.append(phi); + + QSet<Phi *> toRemove; + while (!phis.isEmpty()) { + Phi *phi = phis.first(); + phis.removeFirst(); + if (toRemove.contains(phi)) + continue; + QSet<Phi *> collectedPhis; + if (hasPhiOnlyUses(phi, defUses, collectedPhis)) + toRemove.unite(collectedPhis); + } + + foreach (Phi *phi, toRemove) { + Temp targetVar = *phi->targetTemp; + + BasicBlock *bb = defUses.defStmtBlock(targetVar); + int idx = bb->statements.indexOf(phi); + bb->statements.remove(idx); + + foreach (const Temp &usedVar, defUses.usedVars(phi)) + defUses.removeUse(phi, usedVar); + defUses.removeDef(targetVar); + } +} + +class DeadCodeElimination: public ExprVisitor { + const bool variablesCanEscape; + DefUsesCalculator &_defUses; + QVector<Temp> _worklist; + +public: + DeadCodeElimination(DefUsesCalculator &defUses, Function *function) + : variablesCanEscape(function->variablesCanEscape()) + , _defUses(defUses) + { + _worklist = QVector<Temp>::fromList(_defUses.defs()); + } + + void run() { + while (!_worklist.isEmpty()) { + const Temp v = _worklist.first(); + _worklist.removeFirst(); + + if (_defUses.useCount(v) == 0) { +// qDebug()<<"-"<<v<<"has no uses..."; + Stmt *s = _defUses.defStmt(v); + if (!s) { + _defUses.removeDef(v); + } else if (!hasSideEffect(s)) { +#ifdef SHOW_SSA + qout<<"-- defining stmt for"; + v.dump(qout); + qout<<"has no side effect"<<endl; +#endif + QVector<Stmt *> &stmts = _defUses.defStmtBlock(v)->statements; + int idx = stmts.indexOf(s); + if (idx != -1) + stmts.remove(idx); + foreach (const Temp &usedVar, _defUses.usedVars(s)) { + _defUses.removeUse(s, usedVar); + _worklist.append(usedVar); + } + _defUses.removeDef(v); + } + } + } + +#ifdef SHOW_SSA + qout<<"******************* After dead-code elimination:"; + _defUses.dump(); +#endif + } + +private: + bool _sideEffect; + + bool hasSideEffect(Stmt *s) { + // TODO: check if this can be moved to IR building. + _sideEffect = false; + if (Move *move = s->asMove()) { + if (Temp *t = move->target->asTemp()) { + switch (t->kind) { + case Temp::Formal: + case Temp::ScopedFormal: + case Temp::ScopedLocal: + return true; + case Temp::Local: + if (variablesCanEscape) + return true; + else + break; + case Temp::VirtualRegister: + break; + default: + Q_ASSERT(!"Invalid temp kind!"); + return true; + } + move->source->accept(this); + } else { + return true; + } + } + return _sideEffect; + } + +protected: + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *e) { + // TODO: maybe we can distinguish between built-ins of which we know that they do not have + // a side-effect. + if (e->builtin == Name::builtin_invalid || (e->id && *e->id != QStringLiteral("this"))) + _sideEffect = true; + } + virtual void visitTemp(Temp *e) { + } + virtual void visitClosure(Closure *) {} + virtual void visitConvert(Convert *e) { + // we do not have type information yet, so: + _sideEffect = true; + } + + virtual void visitUnop(Unop *e) { + switch (e->op) { + case OpIncrement: + case OpDecrement: + _sideEffect = true; + break; + + default: + break; + } + + if (!_sideEffect) e->expr->accept(this); + } + virtual void visitBinop(Binop *e) { if (!_sideEffect) e->left->accept(this); if (!_sideEffect) e->right->accept(this); } + virtual void visitSubscript(Subscript *e) { + // TODO: see if we can have subscript accesses without side effect + _sideEffect = true; + } + virtual void visitMember(Member *e) { + // TODO: see if we can have member accesses without side effect + _sideEffect = true; + } + virtual void visitCall(Call *e) { + _sideEffect = true; // TODO: there are built-in functions that have no side effect. + } + virtual void visitNew(New *e) { + _sideEffect = true; // TODO: there are built-in types that have no side effect. + } +}; + +class TypeInference: public StmtVisitor, public ExprVisitor { + bool _variablesCanEscape; + const DefUsesCalculator &_defUses; + QHash<Temp, int> _tempTypes; + QSet<Stmt *> _worklist; + struct TypingResult { + int type; + bool fullyTyped; + + TypingResult(int type, bool fullyTyped): type(type), fullyTyped(fullyTyped) {} + explicit TypingResult(int type = UnknownType): type(type), fullyTyped(type != UnknownType) {} + }; + TypingResult _ty; + +public: + TypeInference(const DefUsesCalculator &defUses) + : _defUses(defUses) + , _ty(UnknownType) + {} + + void run(Function *function) { + _variablesCanEscape = function->variablesCanEscape(); + + // TODO: the worklist handling looks a bit inefficient... check if there is something better + _worklist.clear(); + for (int i = 0, ei = function->basicBlocks.size(); i != ei; ++i) { + BasicBlock *bb = function->basicBlocks[i]; + if (i == 0 || !bb->in.isEmpty()) + foreach (Stmt *s, bb->statements) + _worklist.insert(s); + } + + while (!_worklist.isEmpty()) { + QList<Stmt *> worklist = _worklist.values(); + _worklist.clear(); + while (!worklist.isEmpty()) { + Stmt *s = worklist.first(); + worklist.removeFirst(); +#if defined(SHOW_SSA) + qout<<"Typing stmt ";s->dump(qout);qout<<endl; +#endif + + if (!run(s)) { + _worklist.insert(s); +#if defined(SHOW_SSA) + qout<<"Pushing back stmt: "; + s->dump(qout);qout<<endl; + } else { + qout<<"Finished: "; + s->dump(qout);qout<<endl; +#endif + } + } + } + } + +private: + bool run(Stmt *s) { + TypingResult ty; + std::swap(_ty, ty); + s->accept(this); + std::swap(_ty, ty); + return ty.fullyTyped; + } + + TypingResult run(Expr *e) { + TypingResult ty; + std::swap(_ty, ty); + e->accept(this); + std::swap(_ty, ty); + + if (ty.type != UnknownType) + setType(e, ty.type); + return ty; + } + + bool isAlwaysAnObject(Temp *t) { + switch (t->kind) { + case Temp::Formal: + case Temp::ScopedFormal: + case Temp::ScopedLocal: + return true; + case Temp::Local: + return _variablesCanEscape; + default: + return false; + } + } + + void setType(Expr *e, int ty) { + if (Temp *t = e->asTemp()) { +#if defined(SHOW_SSA) + qout<<"Setting type for "<< (t->scope?"scoped temp ":"temp ") <<t->index<< " to "<<typeName(Type(ty)) << " (" << ty << ")" << endl; +#endif + if (isAlwaysAnObject(t)) { + e->type = ObjectType; + } else { + e->type = (Type) ty; + + if (_tempTypes[*t] != ty) { + _tempTypes[*t] = ty; + +#if defined(SHOW_SSA) + foreach (Stmt *s, _defUses.uses(*t)) { + qout << "Pushing back dependent stmt: "; + s->dump(qout); + qout << endl; + } +#endif + + _worklist += QSet<Stmt *>::fromList(_defUses.uses(*t)); + } + } + } else { + e->type = (Type) ty; + } + } + +protected: + virtual void visitConst(Const *e) { _ty = TypingResult(e->type); } + virtual void visitString(String *) { _ty = TypingResult(StringType); } + virtual void visitRegExp(RegExp *) { _ty = TypingResult(ObjectType); } + virtual void visitName(Name *) { _ty = TypingResult(ObjectType); } + virtual void visitTemp(Temp *e) { + if (isAlwaysAnObject(e)) + _ty = TypingResult(ObjectType); + else + _ty = TypingResult(_tempTypes.value(*e, UnknownType)); + setType(e, _ty.type); + } + virtual void visitClosure(Closure *) { _ty = TypingResult(ObjectType); } // TODO: VERIFY THIS! + virtual void visitConvert(Convert *e) { + _ty = run(e->expr); + } + + virtual void visitUnop(Unop *e) { + _ty = run(e->expr); + switch (e->op) { + case OpUPlus: _ty.type = DoubleType; return; + case OpUMinus: _ty.type = DoubleType; return; + case OpCompl: _ty.type = SInt32Type; return; + case OpNot: _ty.type = BoolType; return; + + case OpIncrement: + case OpDecrement: + Q_ASSERT(!"Inplace operators should have been removed!"); + default: + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); + } + } + + virtual void visitBinop(Binop *e) { + TypingResult leftTy = run(e->left); + TypingResult rightTy = run(e->right); + _ty.fullyTyped = leftTy.fullyTyped && rightTy.fullyTyped; + + switch (e->op) { + case OpAdd: + if (leftTy.type & StringType || rightTy.type & StringType) + _ty.type = StringType; + else if (leftTy.type != UnknownType && rightTy.type != UnknownType) + _ty.type = DoubleType; + else + _ty.type = UnknownType; + break; + case OpSub: + _ty.type = DoubleType; + break; + + case OpMul: + case OpDiv: + case OpMod: + _ty.type = DoubleType; + break; + + case OpBitAnd: + case OpBitOr: + case OpBitXor: + case OpLShift: + case OpRShift: + _ty.type = SInt32Type; + break; + case OpURShift: + _ty.type = UInt32Type; + break; + + case OpGt: + case OpLt: + case OpGe: + case OpLe: + case OpEqual: + case OpNotEqual: + case OpStrictEqual: + case OpStrictNotEqual: + case OpAnd: + case OpOr: + case OpInstanceof: + case OpIn: + _ty.type = BoolType; + break; + + default: + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); + } + } + + virtual void visitCall(Call *e) { + _ty = run(e->base); + for (ExprList *it = e->args; it; it = it->next) + _ty.fullyTyped &= run(it->expr).fullyTyped; + _ty.type = ObjectType; + } + virtual void visitNew(New *e) { + _ty = run(e->base); + for (ExprList *it = e->args; it; it = it->next) + _ty.fullyTyped &= run(it->expr).fullyTyped; + _ty.type = ObjectType; + } + virtual void visitSubscript(Subscript *e) { + _ty.fullyTyped = run(e->base).fullyTyped && run(e->index).fullyTyped; + _ty.type = ObjectType; + } + + virtual void visitMember(Member *e) { + // TODO: for QML, try to do a static lookup + _ty = run(e->base); + _ty.type = ObjectType; + } + + virtual void visitExp(Exp *s) { _ty = run(s->expr); } + virtual void visitMove(Move *s) { + TypingResult sourceTy = run(s->source); + Q_ASSERT(s->op == OpInvalid); + if (Temp *t = s->target->asTemp()) { + setType(t, sourceTy.type); + _ty = sourceTy; + return; + } + + _ty = run(s->target); + _ty.fullyTyped &= sourceTy.fullyTyped; + } + + virtual void visitJump(Jump *) { _ty = TypingResult(MissingType); } + virtual void visitCJump(CJump *s) { _ty = run(s->cond); } + virtual void visitRet(Ret *s) { _ty = run(s->expr); } + virtual void visitTry(Try *s) { setType(s->exceptionVar, ObjectType); _ty = TypingResult(MissingType); } + virtual void visitPhi(Phi *s) { + _ty = run(s->incoming[0]); + for (int i = 1, ei = s->incoming.size(); i != ei; ++i) { + TypingResult ty = run(s->incoming[i]); + _ty.type |= ty.type; + _ty.fullyTyped &= ty.fullyTyped; + } + + // TODO: check & double check the next condition! + if (_ty.type & ObjectType || _ty.type & UndefinedType || _ty.type & NullType) + _ty.type = ObjectType; + else if (_ty.type & NumberType) + _ty.type = DoubleType; + + setType(s->targetTemp, _ty.type); + } +}; + +class TypePropagation: public StmtVisitor, public ExprVisitor { + Type _ty; + + void run(Expr *&e, Type requestedType = UnknownType) { + qSwap(_ty, requestedType); + e->accept(this); + qSwap(_ty, requestedType); + + if (requestedType != UnknownType) + if (e->type != requestedType) + if (requestedType & NumberType) { +// qDebug()<<"adding conversion from"<<typeName(e->type)<<"to"<<typeName(requestedType); + addConversion(e, requestedType); + } + } + + struct Conversion { + Expr **expr; + Type targetType; + Stmt *stmt; + + Conversion(Expr **expr = 0, Type targetType = UnknownType, Stmt *stmt = 0) + : expr(expr) + , targetType(targetType) + , stmt(stmt) + {} + }; + + Stmt *_currStmt; + QVector<Conversion> _conversions; + + void addConversion(Expr *&expr, Type targetType) { + _conversions.append(Conversion(&expr, targetType, _currStmt)); + } + +public: + TypePropagation() : _ty(UnknownType) {} + + void run(Function *f) { + foreach (BasicBlock *bb, f->basicBlocks) { + _conversions.clear(); + + foreach (Stmt *s, bb->statements) { + _currStmt = s; + s->accept(this); + } + + foreach (const Conversion &conversion, _conversions) { + if (conversion.stmt->asMove() && conversion.stmt->asMove()->source->asTemp()) { + *conversion.expr = bb->CONVERT(*conversion.expr, conversion.targetType); + } else { + Temp *target = bb->TEMP(bb->newTemp()); + target->type = conversion.targetType; + Expr *convert = bb->CONVERT(*conversion.expr, conversion.targetType); + Move *convCall = f->New<Move>(); + convCall->init(target, convert, OpInvalid); + + Temp *source = bb->TEMP(target->index); + source->type = conversion.targetType; + *conversion.expr = source; + + int idx = bb->statements.indexOf(conversion.stmt); + bb->statements.insert(idx, convCall); + } + } + } + } + +protected: + virtual void visitConst(Const *c) { + if (_ty & NumberType && c->type & NumberType) { + c->type = _ty; + } + } + + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitTemp(Temp *) {} + virtual void visitClosure(Closure *) {} + virtual void visitConvert(Convert *e) { run(e->expr, e->type); } + virtual void visitUnop(Unop *e) { run(e->expr, e->type); } + virtual void visitBinop(Binop *e) { + // FIXME: This routine needs more tuning! + switch (e->op) { + case OpAdd: + case OpSub: + case OpMul: + case OpDiv: + case OpMod: + case OpBitAnd: + case OpBitOr: + case OpBitXor: + case OpLShift: + case OpRShift: + case OpURShift: + run(e->left, e->type); + run(e->right, e->type); + break; + + case OpGt: + case OpLt: + case OpGe: + case OpLe: + if (e->left->type == DoubleType) + run(e->right, DoubleType); + else if (e->right->type == DoubleType) + run(e->left, DoubleType); + else { + run(e->left, e->type); + run(e->right, e->type); + } + break; + + case OpEqual: + case OpNotEqual: + case OpStrictEqual: + case OpStrictNotEqual: + break; + + case OpInstanceof: + case OpIn: + run(e->left, e->type); + run(e->right, e->type); + break; + + default: + Q_UNIMPLEMENTED(); + Q_UNREACHABLE(); + } + } + virtual void visitCall(Call *e) { + run(e->base); + for (ExprList *it = e->args; it; it = it->next) + run(it->expr); + } + virtual void visitNew(New *e) { + run(e->base); + for (ExprList *it = e->args; it; it = it->next) + run(it->expr); + } + virtual void visitSubscript(Subscript *e) { run(e->base); run(e->index); } + virtual void visitMember(Member *e) { run(e->base); } + virtual void visitExp(Exp *s) { run(s->expr); } + virtual void visitMove(Move *s) { + run(s->target); + run(s->source, s->target->type); + } + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *s) { + run(s->cond, BoolType); + } + virtual void visitRet(Ret *s) { run(s->expr); } + virtual void visitTry(Try *) {} + virtual void visitPhi(Phi *s) { + Type ty = s->targetTemp->type; + foreach (Expr *e, s->incoming) + if (e->asConst()) + run(e, ty); + } +}; + +void doEdgeSplitting(Function *f) +{ + const QVector<BasicBlock *> oldBBs = f->basicBlocks; + + foreach (BasicBlock *bb, oldBBs) { + if (bb->in.size() > 1) { + for (int inIdx = 0, eInIdx = bb->in.size(); inIdx != eInIdx; ++inIdx) { + BasicBlock *inBB = bb->in[inIdx]; + if (inBB->out.size() > 1) { // this should have been split! +#if defined(SHOW_SSA) + qDebug() << "Splitting edge from block" << inBB->index << "to block" << bb->index; +#endif + + // create the basic block: + BasicBlock *newBB = new BasicBlock(f, bb->containingGroup()); + newBB->index = f->basicBlocks.last()->index + 1; + f->basicBlocks.append(newBB); + Jump *s = f->New<Jump>(); + s->init(bb); + newBB->statements.append(s); + + // rewire the old outgoing edge + int outIdx = inBB->out.indexOf(bb); + inBB->out[outIdx] = newBB; + newBB->in.append(inBB); + + // rewire the old incoming edge + bb->in[inIdx] = newBB; + newBB->out.append(bb); + + // patch the terminator + Stmt *terminator = inBB->terminator(); + if (Jump *j = terminator->asJump()) { + Q_ASSERT(outIdx == 0); + j->target = newBB; + } else if (CJump *j = terminator->asCJump()) { + if (outIdx == 0) + j->iftrue = newBB; + else if (outIdx == 1) + j->iffalse = newBB; + else + Q_ASSERT(!"Invalid out edge index for CJUMP!"); + } else { + Q_ASSERT(!"Unknown terminator!"); + } + } + } + } + } +} + +QHash<BasicBlock *, BasicBlock *> scheduleBlocks(Function *function, const DominatorTree &df) +{ + struct I { + const DominatorTree &df; + QHash<BasicBlock *, BasicBlock *> &startEndLoops; + QSet<BasicBlock *> visited; + QVector<BasicBlock *> &sequence; + BasicBlock *currentGroup; + QList<BasicBlock *> postponed; + + I(const DominatorTree &df, QVector<BasicBlock *> &sequence, + QHash<BasicBlock *, BasicBlock *> &startEndLoops) + : df(df) + , sequence(sequence) + , startEndLoops(startEndLoops) + , currentGroup(0) + {} + + void DFS(BasicBlock *bb) { + Q_ASSERT(bb); + if (visited.contains(bb)) + return; + + if (bb->containingGroup() != currentGroup) { + postponed.append(bb); + return; + } + if (bb->isGroupStart()) + currentGroup = bb; + else if (bb->in.size() > 1) + foreach (BasicBlock *inBB, bb->in) + if (!visited.contains(inBB)) + return; + + Q_ASSERT(df.immediateDominator(bb) == 0 || sequence.contains(df.immediateDominator(bb))); + layout(bb); + if (Stmt *terminator = bb->terminator()) { + if (Jump *j = terminator->asJump()) { + Q_ASSERT(bb->out.size() == 1); + DFS(j->target); + } else if (CJump *cj = terminator->asCJump()) { + Q_ASSERT(bb->out.size() == 2); + DFS(cj->iftrue); + DFS(cj->iffalse); + } else if (terminator->asRet()) { + Q_ASSERT(bb->out.size() == 0); + // nothing to do. + } else { + Q_UNREACHABLE(); + } + } else { + Q_UNREACHABLE(); + } + + if (bb->isGroupStart()) { + currentGroup = bb->containingGroup(); + startEndLoops.insert(bb, sequence.last()); + QList<BasicBlock *> p = postponed; + foreach (BasicBlock *pBB, p) + DFS(pBB); + } + } + + void layout(BasicBlock *bb) { + sequence.append(bb); + visited.insert(bb); + postponed.removeAll(bb); + } + }; + + QVector<BasicBlock *> sequence; + sequence.reserve(function->basicBlocks.size()); + QHash<BasicBlock *, BasicBlock *> startEndLoops; + I(df, sequence, startEndLoops).DFS(function->basicBlocks.first()); + qSwap(function->basicBlocks, sequence); + + showMeTheCode(function); + return startEndLoops; +} + +void checkCriticalEdges(QVector<BasicBlock *> basicBlocks) { + foreach (BasicBlock *bb, basicBlocks) { + if (bb && bb->out.size() > 1) { + foreach (BasicBlock *bb2, bb->out) { + if (bb2 && bb2->in.size() > 1) { + qout << "found critical edge between block " + << bb->index << " and block " << bb2->index; + Q_ASSERT(false); + } + } + } + } +} + +void cleanupBasicBlocks(Function *function) +{ +// showMeTheCode(function); + + // remove all basic blocks that have no incoming edges, but skip the entry block + QVector<BasicBlock *> W = function->basicBlocks; + W.removeFirst(); + QSet<BasicBlock *> toRemove; + + while (!W.isEmpty()) { + BasicBlock *bb = W.first(); + W.removeFirst(); + if (toRemove.contains(bb)) + continue; + if (bb->in.isEmpty()) { + foreach (BasicBlock *outBB, bb->out) { + int idx = outBB->in.indexOf(bb); + if (idx != -1) { + outBB->in.remove(idx); + W.append(outBB); + } + } + toRemove.insert(bb); + } + } + + // TODO: merge 2 basic blocks A and B if A has one outgoing edge (to B), B has one incoming + // edge (from A), but not when A has more than 1 incoming edge and B has more than one + // outgoing edge. + + foreach (BasicBlock *bb, toRemove) { + foreach (Stmt *s, bb->statements) + s->destroyData(); + int idx = function->basicBlocks.indexOf(bb); + if (idx != -1) + function->basicBlocks.remove(idx); + delete bb; + } + + // re-number all basic blocks: + for (int i = 0; i < function->basicBlocks.size(); ++i) + function->basicBlocks[i]->index = i; +} + +class InputOutputCollector: protected StmtVisitor, protected ExprVisitor { + const bool variablesCanEscape; + +public: + QList<Temp> inputs; + QList<Temp> outputs; + + InputOutputCollector(bool variablesCanEscape): variablesCanEscape(variablesCanEscape) {} + + void collect(Stmt *s) { + inputs.clear(); + outputs.clear(); + s->accept(this); + } + +protected: + virtual void visitConst(Const *) {} + virtual void visitString(String *) {} + virtual void visitRegExp(RegExp *) {} + virtual void visitName(Name *) {} + virtual void visitTemp(Temp *e) { + switch (e->kind) { + case Temp::Local: + if (!variablesCanEscape) + inputs.append(*e); + break; + + case Temp::VirtualRegister: + inputs.append(*e); + break; + + default: + break; + } + } + virtual void visitClosure(Closure *) {} + virtual void visitConvert(Convert *e) { e->expr->accept(this); } + virtual void visitUnop(Unop *e) { e->expr->accept(this); } + virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitCall(Call *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + virtual void visitNew(New *e) { + e->base->accept(this); + for (ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(Member *e) { e->base->accept(this); } + virtual void visitExp(Exp *s) { s->expr->accept(this); } + virtual void visitMove(Move *s) { + s->source->accept(this); + if (Temp *t = s->target->asTemp()) { + if ((t->kind == Temp::Local && !variablesCanEscape) || t->kind == Temp::VirtualRegister) + outputs.append(*t); + else + s->target->accept(this); + } else { + s->target->accept(this); + } + } + virtual void visitJump(Jump *) {} + virtual void visitCJump(CJump *s) { s->cond->accept(this); } + virtual void visitRet(Ret *s) { s->expr->accept(this); } + virtual void visitTry(Try *) {} + virtual void visitPhi(Phi *s) { + // Handled separately + } +}; + +/* + * The algorithm is described in: + * + * Linear Scan Register Allocation on SSA Form + * Christian Wimmer & Michael Franz, CGO'10, April 24-28, 2010 + * + * There is one slight difference w.r.t. the phi-nodes: in the artice, the phi nodes are attached + * to the basic-blocks. Therefore, in the algorithm in the article, the ranges for input parameters + * for phi nodes run from their definition upto the branch instruction into the block with the phi + * node. In our representation, phi nodes are mostly treaded as normal instructions, so we have to + * enlarge the range to cover the phi node itself. + */ +class LifeRanges { + typedef QSet<Temp> LiveRegs; + + QHash<BasicBlock *, LiveRegs> _liveIn; + QHash<Temp, LifeTimeInterval> _intervals; + QList<LifeTimeInterval> _sortedRanges; + +public: + LifeRanges(Function *function, const QHash<BasicBlock *, BasicBlock *> &startEndLoops) + { + int id = 0; + foreach (BasicBlock *bb, function->basicBlocks) { + foreach (Stmt *s, bb->statements) { + if (s->asPhi()) + s->id = id + 1; + else + s->id = ++id; + } + } + + for (int i = function->basicBlocks.size() - 1; i >= 0; --i) { + BasicBlock *bb = function->basicBlocks[i]; + buildIntervals(bb, startEndLoops.value(bb, 0), function->variablesCanEscape()); + } + + _sortedRanges.reserve(_intervals.size()); + for (QHash<Temp, LifeTimeInterval>::const_iterator i = _intervals.begin(), ei = _intervals.end(); i != ei; ++i) { + LifeTimeInterval range = i.value(); + range.setTemp(i.key()); + _sortedRanges.append(range); + } + qSort(_sortedRanges.begin(), _sortedRanges.end(), LifeTimeInterval::lessThan); + } + + QList<LifeTimeInterval> ranges() const { return _sortedRanges; } + + void dump() const + { + qout << "Life ranges:" << endl; + qout << "Intervals:" << endl; + foreach (const LifeTimeInterval &range, _sortedRanges) { + range.dump(); + qout << endl; + } + + foreach (BasicBlock *bb, _liveIn.keys()) { + qout << "L" << bb->index <<" live-in: "; + QList<Temp> live = QList<Temp>::fromSet(_liveIn.value(bb)); + qSort(live); + for (int i = 0; i < live.size(); ++i) { + if (i > 0) qout << ", "; + live[i].dump(qout); + } + qout << endl; + } + } + +private: + void buildIntervals(BasicBlock *bb, BasicBlock *loopEnd, bool variablesCanEscape) + { + LiveRegs live; + foreach (BasicBlock *successor, bb->out) { + live.unite(_liveIn[successor]); + const int bbIndex = successor->in.indexOf(bb); + Q_ASSERT(bbIndex >= 0); + + foreach (Stmt *s, successor->statements) { + if (Phi *phi = s->asPhi()) { + if (Temp *t = phi->incoming[bbIndex]->asTemp()) + live.insert(*t); + } else { + break; + } + } + } + + foreach (const Temp &opd, live) + _intervals[opd].addRange(bb->statements.first(), bb->statements.last()); + + InputOutputCollector collector(variablesCanEscape); + for (int i = bb->statements.size() - 1; i >= 0; --i) { + Stmt *s = bb->statements[i]; + if (Phi *phi = s->asPhi()) { + live.remove(*phi->targetTemp); + continue; + } + collector.collect(s); + foreach (const Temp &opd, collector.outputs) { + _intervals[opd].setFrom(s); + live.remove(opd); + } + foreach (const Temp &opd, collector.inputs) { + _intervals[opd].addRange(bb->statements.first(), s); + live.insert(opd); + } + } + + if (loopEnd) { // Meaning: bb is a loop header, because loopEnd is set to non-null. + foreach (const Temp &opd, live) + _intervals[opd].addRange(bb->statements.first(), loopEnd->statements.last()); + } + + _liveIn[bb] = live; + } +}; +} // anonymous namespace + +void LifeTimeInterval::setFrom(Stmt *from) { + Q_ASSERT(from && from->id > 0); + + if (_ranges.isEmpty()) // this is the case where there is no use, only a define + _ranges.push_front(Range(from->id, from->id)); + else + _ranges.first().start = from->id; +} + +void LifeTimeInterval::addRange(Stmt *from, Stmt *to) { + Q_ASSERT(from && from->id > 0); + Q_ASSERT(to && to->id > 0); + Q_ASSERT(to->id >= from->id); + + if (_ranges.isEmpty()) { + _ranges.push_front(Range(from->id, to->id)); + return; + } + + Range *p = &_ranges.first(); + if (to->id + 1 >= p->start && p->end + 1 >= from->id) { + p->start = qMin(p->start, from->id); + p->end = qMax(p->end, to->id); + while (_ranges.count() > 1) { + Range *p1 = &_ranges[1]; + if (p->end + 1 < p1->start || p1->end + 1 < p->start) + break; + p1->start = qMin(p->start, p1->start); + p1->end = qMax(p->end, p1->end); + _ranges.pop_front(); + p = &_ranges.first(); + } + } else { + Q_ASSERT(to->id < p->start); + _ranges.push_front(Range(from->id, to->id)); + } +} + +void LifeTimeInterval::dump() const { + _temp.dump(qout); + qout << ": "; + if (_ranges.isEmpty()) + qout << "(none)"; + for (int i = 0; i < _ranges.size(); ++i) { + if (i > 0) qout << ", "; + qout << _ranges[i].start << " - " << _ranges[i].end; + } + if (_reg != Invalid) + qout << " (register " << _reg << ")"; +} + +bool LifeTimeInterval::lessThan(const LifeTimeInterval &r1, const LifeTimeInterval &r2) { + if (r1._ranges.first().start == r2._ranges.first().start) + return r1._ranges.last().end < r2._ranges.last().end; + else + return r1._ranges.first().start < r2._ranges.first().start; +} + +void Optimizer::run() +{ +#if defined(SHOW_SSA) + qout << "##### NOW IN FUNCTION " << (_function->name ? qPrintable(*_function->name) : "anonymous!") + << " with " << _function->basicBlocks.size() << " basic blocks." << endl << flush; +#endif + + // Number all basic blocks, so we have nice numbers in the dumps: + for (int i = 0; i < function->basicBlocks.size(); ++i) + function->basicBlocks[i]->index = i; + showMeTheCode(function); + + cleanupBasicBlocks(function); + + function->removeSharedExpressions(); + +// showMeTheCode(function); + + if (!function->hasTry && !function->hasWith) { +// qout << "Starting edge splitting..." << endl; + doEdgeSplitting(function); +// showMeTheCode(function); + + // Calculate the dominator tree: + DominatorTree df(function->basicBlocks); + + convertToSSA(function, df); +// showMeTheCode(function); + +// qout << "Starting def/uses calculation..." << endl; + DefUsesCalculator defUses(function); + +// qout << "Cleaning up phi nodes..." << endl; + cleanupPhis(defUses); +// showMeTheCode(function); + +// qout << "Starting dead-code elimination..." << endl; + DeadCodeElimination(defUses, function).run(); +// showMeTheCode(function); + +// qout << "Running type inference..." << endl; + TypeInference(defUses).run(function); +// showMeTheCode(function); + +// qout << "Doing type propagation..." << endl; + TypePropagation().run(function); +// showMeTheCode(function); + +// qout << "Doing block scheduling..." << endl; + startEndLoops = scheduleBlocks(function, df); +// showMeTheCode(function); + +#ifndef QT_NO_DEBUG + checkCriticalEdges(function->basicBlocks); +#endif + +// qout << "Finished." << endl; + inSSA = true; + } else { + inSSA = false; + } +} + +namespace { +void insertMove(Function *function, BasicBlock *basicBlock, Temp *target, Expr *source) { + if (target->type != source->type) + source = basicBlock->CONVERT(source, target->type); + + Move *s = function->New<Move>(); + s->init(target, source, OpInvalid); + basicBlock->statements.insert(basicBlock->statements.size() - 1, s); +} +} + +/* + * Quick function to convert out of SSA, so we can put the stuff through the ISel phases. This + * has to be replaced by a phase in the specific ISel back-ends and do register allocation at the + * same time. That way the huge number of redundant moves generated by this function are eliminated. + */ +void Optimizer::convertOutOfSSA() { + // We assume that edge-splitting is already done. + foreach (BasicBlock *bb, function->basicBlocks) { + QVector<Stmt *> &stmts = bb->statements; + while (!stmts.isEmpty()) { + Stmt *s = stmts.first(); + if (Phi *phi = s->asPhi()) { + stmts.removeFirst(); + for (int i = 0, ei = phi->incoming.size(); i != ei; ++i) + insertMove(function, bb->in[i], phi->targetTemp, phi->incoming[i]); + } else { + break; + } + } + } +} + +QList<Optimizer::SSADeconstructionMove> Optimizer::ssaDeconstructionMoves(BasicBlock *basicBlock) +{ + QList<SSADeconstructionMove> moves; + + foreach (BasicBlock *outEdge, basicBlock->out) { + int inIdx = outEdge->in.indexOf(basicBlock); + Q_ASSERT(inIdx >= 0); + foreach (Stmt *s, outEdge->statements) { + if (Phi *phi = s->asPhi()) { + SSADeconstructionMove m; + m.source = phi->incoming[inIdx]; + m.target = phi->targetTemp; + moves.append(m); + } else { + break; + } + } + } + + return moves; +} + +QList<LifeTimeInterval> Optimizer::lifeRanges() const +{ + Q_ASSERT(isInSSA()); + + LifeRanges lifeRanges(function, startEndLoops); +// lifeRanges.dump(); +// showMeTheCode(function); + return lifeRanges.ranges(); +} diff --git a/src/qml/qml/v4/qv4ssa_p.h b/src/qml/qml/v4/qv4ssa_p.h new file mode 100644 index 0000000000..097a40eff1 --- /dev/null +++ b/src/qml/qml/v4/qv4ssa_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4SSA_P_H +#define QV4SSA_P_H + +#include "qv4jsir_p.h" + +QT_BEGIN_NAMESPACE +namespace QQmlJS { +namespace V4IR { + +class LifeTimeInterval { + struct Range { + int start; + int end; + + Range(int start = Invalid, int end = Invalid) + : start(start) + , end(end) + {} + }; + + Temp _temp; + QList<Range> _ranges; + int _reg; + +public: + static const int Invalid = -1; + + LifeTimeInterval() + : _reg(Invalid) + {} + + void setTemp(const Temp &temp) { this->_temp = temp; } + Temp temp() const { return _temp; } + + void setFrom(Stmt *from); + void addRange(Stmt *from, Stmt *to); + + int start() const { return _ranges.first().start; } + int end() const { return _ranges.last().end; } + + int reg() const { return _reg; } + void setReg(int reg) { _reg = reg; } + + void dump() const; + static bool lessThan(const LifeTimeInterval &r1, const LifeTimeInterval &r2); +}; + +class Optimizer +{ +public: + struct SSADeconstructionMove + { + Expr *source; + Temp *target; + + bool needsConversion() const + { return target->type != source->type; } + }; + +public: + Optimizer(Function *function) + : function(function) + , inSSA(false) + {} + + void run(); + void convertOutOfSSA(); + + bool isInSSA() const + { return inSSA; } + + QList<SSADeconstructionMove> ssaDeconstructionMoves(BasicBlock *basicBlock); + + QList<LifeTimeInterval> lifeRanges() const; + +private: + Function *function; + bool inSSA; + QHash<BasicBlock *, BasicBlock *> startEndLoops; +}; + +} // V4IR namespace +} // QQmlJS namespace +QT_END_NAMESPACE + +#endif // QV4SSA_P_H diff --git a/src/qml/qml/v4/qv4stacktrace.cpp b/src/qml/qml/v4/qv4stacktrace.cpp new file mode 100644 index 0000000000..1cc2e53556 --- /dev/null +++ b/src/qml/qml/v4/qv4stacktrace.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if defined(_WIN32) +#include <windows.h> +#include <DbgHelp.h> +#endif + +#include "qv4stacktrace_p.h" +#include "qv4function_p.h" +#include "qv4engine_p.h" +#include "qv4unwindhelper_p.h" + +#if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_MAC) +#define HAVE_GNU_BACKTRACE +#include <execinfo.h> +#endif + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +NativeStackTrace::NativeStackTrace(ExecutionContext *context) +{ + engine = context->engine; + currentNativeFrame = 0; + +#if defined(HAVE_GNU_BACKTRACE) + UnwindHelper::prepareForUnwind(context); + + nativeFrameCount = backtrace(&trace[0], sizeof(trace) / sizeof(trace[0])); +#elif defined(Q_OS_WIN) + + int machineType = 0; + + CONTEXT winContext; + memset(&winContext, 0, sizeof(winContext)); + winContext.ContextFlags = CONTEXT_FULL; + RtlCaptureContext(&winContext); + + STACKFRAME64 sf64; + memset(&sf64, 0, sizeof(sf64)); + +#if defined(Q_PROCESSOR_X86_32) + machineType = IMAGE_FILE_MACHINE_I386; + + sf64.AddrFrame.Offset = winContext.Ebp; + sf64.AddrFrame.Mode = AddrModeFlat; + sf64.AddrPC.Offset = winContext.Eip; + sf64.AddrPC.Mode = AddrModeFlat; + sf64.AddrStack.Offset = winContext.Esp; + sf64.AddrStack.Mode = AddrModeFlat; + +#elif defined(Q_PROCESSOR_X86_64) + machineType = IMAGE_FILE_MACHINE_AMD64; + + sf64.AddrFrame.Offset = winContext.Rbp; + sf64.AddrFrame.Mode = AddrModeFlat; + sf64.AddrPC.Offset = winContext.Rip; + sf64.AddrPC.Mode = AddrModeFlat; + sf64.AddrStack.Offset = winContext.Rsp; + sf64.AddrStack.Mode = AddrModeFlat; + +#else +#error "Platform unsupported!" +#endif + + nativeFrameCount = 0; + + while (StackWalk64(machineType, GetCurrentProcess(), GetCurrentThread(), &sf64, &winContext, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0)) { + + if (sf64.AddrReturn.Offset == 0) + break; + + trace[nativeFrameCount] = reinterpret_cast<void*>(sf64.AddrReturn.Offset); + nativeFrameCount++; + if (nativeFrameCount >= sizeof(trace) / sizeof(trace[0])) + break; + } + +#else + nativeFrameCount = 0; +#endif + } + +NativeFrame NativeStackTrace::nextFrame() { + NativeFrame frame; + frame.function = 0; + frame.line = -1; + + for (; currentNativeFrame < nativeFrameCount && !frame.function; ++currentNativeFrame) { + quintptr pc = reinterpret_cast<quintptr>(trace[currentNativeFrame]); + // The pointers from the back trace point to the return address, but we are interested in + // the caller site. + pc = pc - 1; + + Function *f = engine->functionForProgramCounter(pc); + if (!f) + continue; + + frame.function = f; + frame.line = f->lineNumberForProgramCounter(pc - reinterpret_cast<quintptr>(f->code)); + } + + return frame; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4stacktrace_p.h b/src/qml/qml/v4/qv4stacktrace_p.h new file mode 100644 index 0000000000..79cb4d1813 --- /dev/null +++ b/src/qml/qml/v4/qv4stacktrace_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4STACKTRACE_P_H +#define QV4STACKTRACE_P_H + +#include <qglobal.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Function; +struct ExecutionEngine; +struct ExecutionContext; + +struct NativeFrame { + Function *function; + int line; +}; + +struct NativeStackTrace +{ + void *trace[100]; + int nativeFrameCount; + int currentNativeFrame; + ExecutionEngine *engine; + + NativeStackTrace(ExecutionContext *context); + + NativeFrame nextFrame(); +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4STACKTRACE_P_H diff --git a/src/qml/qml/v4/qv4string.cpp b/src/qml/qml/v4/qv4string.cpp new file mode 100644 index 0000000000..bfdee2d2cc --- /dev/null +++ b/src/qml/qml/v4/qv4string.cpp @@ -0,0 +1,304 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4string_p.h" +#include "qv4identifiertable_p.h" +#include "qv4runtime_p.h" +#include "qv4objectproto_p.h" +#include "qv4stringobject_p.h" +#include <QtCore/QHash> + +using namespace QV4; + +static uint toArrayIndex(const QChar *ch, const QChar *end, bool *ok) +{ + *ok = false; + uint i = ch->unicode() - '0'; + if (i > 9) + return UINT_MAX; + ++ch; + // reject "01", "001", ... + if (i == 0 && ch != end) + return UINT_MAX; + + while (ch < end) { + uint x = ch->unicode() - '0'; + if (x > 9) + return UINT_MAX; + uint n = i*10 + x; + if (n < i) + // overflow + return UINT_MAX; + i = n; + ++ch; + } + *ok = true; + return i; +} + +static uint toArrayIndex(const char *ch, const char *end, bool *ok) +{ + *ok = false; + uint i = *ch - '0'; + if (i > 9) + return UINT_MAX; + ++ch; + // reject "01", "001", ... + if (i == 0 && ch != end) + return UINT_MAX; + + while (ch < end) { + uint x = *ch - '0'; + if (x > 9) + return UINT_MAX; + uint n = i*10 + x; + if (n < i) + // overflow + return UINT_MAX; + i = n; + ++ch; + } + *ok = true; + return i; +} + + +const ManagedVTable String::static_vtbl = +{ + call, + construct, + 0 /*markObjects*/, + destroy, + 0 /*collectDeletables*/, + hasInstance, + get, + getIndexed, + put, + putIndexed, + query, + queryIndexed, + deleteProperty, + deleteIndexedProperty, + 0 /*getLookup*/, + 0 /*setLookup*/, + Managed::isEqualTo, + 0 /*advanceIterator*/, + "String", +}; + +void String::destroy(Managed *that) +{ + static_cast<String*>(that)->~String(); +} + +Value String::get(Managed *m, String *name, bool *hasProperty) +{ + String *that = static_cast<String *>(m); + ExecutionEngine *v4 = m->engine(); + if (name == v4->id_length) { + if (hasProperty) + *hasProperty = true; + return Value::fromInt32(that->_text.length()); + } + PropertyAttributes attrs; + Property *pd = v4->stringPrototype->__getPropertyDescriptor__(name, &attrs); + if (!pd || attrs.isGeneric()) { + if (hasProperty) + *hasProperty = false; + return Value::undefinedValue(); + } + if (hasProperty) + *hasProperty = true; + return v4->stringPrototype->getValue(Value::fromString(that), pd, attrs); +} + +Value String::getIndexed(Managed *m, uint index, bool *hasProperty) +{ + String *that = static_cast<String *>(m); + ExecutionEngine *engine = that->engine(); + if (index < that->_text.length()) { + if (hasProperty) + *hasProperty = true; + return Value::fromString(engine->newString(that->toQString().mid(index, 1))); + } + PropertyAttributes attrs; + Property *pd = engine->stringPrototype->__getPropertyDescriptor__(index, &attrs); + if (!pd || attrs.isGeneric()) { + if (hasProperty) + *hasProperty = false; + return Value::undefinedValue(); + } + if (hasProperty) + *hasProperty = true; + return engine->stringPrototype->getValue(Value::fromString(that), pd, attrs); +} + +void String::put(Managed *m, String *name, const Value &value) +{ + String *that = static_cast<String *>(m); + Object *o = that->engine()->newStringObject(Value::fromString(that)); + o->put(name, value); +} + +void String::putIndexed(Managed *m, uint index, const Value &value) +{ + String *that = static_cast<String *>(m); + Object *o = m->engine()->newStringObject(Value::fromString(that)); + o->putIndexed(index, value); +} + +PropertyAttributes String::query(const Managed *m, String *name) +{ + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return queryIndexed(m, idx); + return Attr_Invalid; +} + +PropertyAttributes String::queryIndexed(const Managed *m, uint index) +{ + const String *that = static_cast<const String *>(m); + return (index < that->_text.length()) ? Attr_NotConfigurable|Attr_NotWritable : Attr_Invalid; +} + +bool String::deleteProperty(Managed *, String *) +{ + return false; +} + +bool String::deleteIndexedProperty(Managed *m, uint index) +{ + return false; +} + +String::String(ExecutionEngine *engine, const QString &text) + : Managed(engine ? engine->emptyClass : 0), _text(text), identifier(0), stringHash(UINT_MAX) +{ + vtbl = &static_vtbl; + type = Type_String; + subtype = StringType_Unknown; +} + +uint String::toUInt(bool *ok) const +{ + *ok = true; + + if (subtype == StringType_Unknown) + createHashValue(); + if (subtype >= StringType_UInt) + return stringHash; + + // ### this conversion shouldn't be required + double d = __qmljs_string_to_number(toQString()); + uint l = (uint)d; + if (d == l) + return l; + *ok = false; + return UINT_MAX; +} + +void String::makeIdentifierImpl() +{ + engine()->identifierTable->identifier(this); +} + +void String::createHashValue() const +{ + const QChar *ch = _text.constData(); + const QChar *end = ch + _text.length(); + + // array indices get their number as hash value + bool ok; + stringHash = toArrayIndex(ch, end, &ok); + if (ok) { + subtype = (stringHash == UINT_MAX) ? StringType_UInt : StringType_ArrayIndex; + return; + } + + uint h = 0xffffffff; + while (ch < end) { + h = 31 * h + ch->unicode(); + ++ch; + } + + stringHash = h; + subtype = StringType_Regular; +} + +uint String::createHashValue(const QChar *ch, int length) +{ + const QChar *end = ch + length; + + // array indices get their number as hash value + bool ok; + uint stringHash = toArrayIndex(ch, end, &ok); + if (ok) + return stringHash; + + uint h = 0xffffffff; + while (ch < end) { + h = 31 * h + ch->unicode(); + ++ch; + } + + return h; +} + +uint String::createHashValue(const char *ch, int length) +{ + const char *end = ch + length; + + // array indices get their number as hash value + bool ok; + uint stringHash = toArrayIndex(ch, end, &ok); + if (ok) + return stringHash; + + uint h = 0xffffffff; + while (ch < end) { + if (*ch >= 0x80) + return UINT_MAX; + h = 31 * h + *ch; + ++ch; + } + + return h; +} diff --git a/src/qml/qml/v4/qv4string_p.h b/src/qml/qml/v4/qv4string_p.h new file mode 100644 index 0000000000..629fa506b7 --- /dev/null +++ b/src/qml/qml/v4/qv4string_p.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4STRING_H +#define QV4STRING_H + +#include <QtCore/qstring.h> +#include "qv4managed_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct ExecutionEngine; +struct Identifier; + +struct Q_QML_EXPORT String : public Managed { + enum StringType { + StringType_Unknown, + StringType_Regular, + StringType_UInt, + StringType_ArrayIndex + }; + + String() : Managed(0), identifier(0), stringHash(UINT_MAX) + { vtbl = &static_vtbl; type = Type_String; subtype = StringType_Unknown; } + String(ExecutionEngine *engine, const QString &text); + ~String() { _data = 0; } + + inline bool isEqualTo(const String *other) const { + if (this == other) + return true; + if (hashValue() != other->hashValue()) + return false; + if (identifier && identifier == other->identifier) + return true; + if (subtype >= StringType_UInt && subtype == other->subtype) + return true; + + return toQString() == other->toQString(); + } + inline bool compare(const String *other) { + return toQString() < other->toQString(); + } + + inline bool isEmpty() const { return _text.isEmpty(); } + inline const QString &toQString() const { + return _text; + } + + inline unsigned hashValue() const { + if (subtype == StringType_Unknown) + createHashValue(); + + return stringHash; + } + uint asArrayIndex() const { + if (subtype == StringType_Unknown) + createHashValue(); + if (subtype == StringType_ArrayIndex) + return stringHash; + return UINT_MAX; + } + uint toUInt(bool *ok) const; + + void makeIdentifier() { + if (identifier) + return; + makeIdentifierImpl(); + } + + void makeIdentifierImpl(); + + void createHashValue() const; + static uint createHashValue(const QChar *ch, int length); + static uint createHashValue(const char *ch, int length); + + bool startsWithUpper() const { + return _text.length() && _text.at(0).isUpper(); + } + int length() const { + return _text.length(); + } + + QString _text; + mutable Identifier *identifier; + mutable uint stringHash; + +protected: + static void destroy(Managed *); + static Value get(Managed *m, String *name, bool *hasProperty); + static Value getIndexed(Managed *m, uint index, bool *hasProperty); + static void put(Managed *m, String *name, const Value &value); + static void putIndexed(Managed *m, uint index, const Value &value); + static PropertyAttributes query(const Managed *m, String *name); + static PropertyAttributes queryIndexed(const Managed *m, uint index); + static bool deleteProperty(Managed *, String *); + static bool deleteIndexedProperty(Managed *m, uint index); + + static const ManagedVTable static_vtbl; +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4/qv4stringobject.cpp b/src/qml/qml/v4/qv4stringobject.cpp new file mode 100644 index 0000000000..5afedd3d4f --- /dev/null +++ b/src/qml/qml/v4/qv4stringobject.cpp @@ -0,0 +1,761 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4stringobject_p.h" +#include "qv4regexpobject_p.h" +#include "qv4objectproto_p.h" +#include "qv4mm_p.h" +#include <QtCore/qnumeric.h> +#include <QtCore/qmath.h> +#include <QtCore/QDateTime> +#include <QtCore/QStringList> +#include <QtCore/QDebug> +#include <cmath> +#include <qmath.h> +#include <qnumeric.h> +#include <cassert> + +#include <private/qqmljsengine_p.h> +#include <private/qqmljslexer_p.h> +#include <private/qqmljsparser_p.h> +#include <private/qqmljsast_p.h> +#include <qv4jsir_p.h> +#include <qv4codegen_p.h> +#include <qv4isel_masm_p.h> + +#ifndef Q_OS_WIN +# include <time.h> +# ifndef Q_OS_VXWORKS +# include <sys/time.h> +# else +# include "qplatformdefs.h" +# endif +#else +# include <windows.h> +#endif + +using namespace QV4; + +DEFINE_MANAGED_VTABLE(StringObject); + +StringObject::StringObject(ExecutionEngine *engine, const Value &value) + : Object(engine), value(value) +{ + vtbl = &static_vtbl; + type = Type_StringObject; + + tmpProperty.value = Value::undefinedValue(); + + assert(value.isString()); + defineReadonlyProperty(engine->id_length, Value::fromUInt32(value.stringValue()->toQString().length())); +} + +Property *StringObject::getIndex(uint index) const +{ + QString str = value.stringValue()->toQString(); + if (index >= (uint)str.length()) + return 0; + String *result = internalClass->engine->newString(str.mid(index, 1)); + tmpProperty.value = Value::fromString(result); + return &tmpProperty; +} + +bool StringObject::deleteIndexedProperty(Managed *m, uint index) +{ + StringObject *o = m->asStringObject(); + if (!o) + m->engine()->current->throwTypeError(); + + if (index < o->value.stringValue()->toQString().length()) { + if (m->engine()->current->strictMode) + m->engine()->current->throwTypeError(); + return false; + } + return true; +} + +Property *StringObject::advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs) +{ + StringObject *s = static_cast<StringObject *>(m); + uint slen = s->value.stringValue()->toQString().length(); + if (it->arrayIndex < slen) { + while (it->arrayIndex < slen) { + *index = it->arrayIndex; + ++it->arrayIndex; + if (attrs) + *attrs = s->arrayAttributes ? s->arrayAttributes[it->arrayIndex] : PropertyAttributes(Attr_NotWritable|Attr_NotConfigurable); + return s->__getOwnProperty__(*index); + } + it->arrayNode = s->sparseArrayBegin(); + // iterate until we're past the end of the string + while (it->arrayNode && it->arrayNode->key() < slen) + it->arrayNode = it->arrayNode->nextNode(); + } + + return Object::advanceIterator(m, it, name, index, attrs); +} + +void StringObject::markObjects(Managed *that) +{ + StringObject *o = static_cast<StringObject *>(that); + o->value.stringValue()->mark(); + Object::markObjects(that); +} + +DEFINE_MANAGED_VTABLE(StringCtor); + +StringCtor::StringCtor(ExecutionContext *scope) + : FunctionObject(scope, scope->engine->newIdentifier(QStringLiteral("String"))) +{ + vtbl = &static_vtbl; +} + +Value StringCtor::construct(Managed *m, Value *argv, int argc) +{ + Value value; + if (argc) + value = Value::fromString(argv[0].toString(m->engine()->current)); + else + value = Value::fromString(m->engine()->current, QString()); + return Value::fromObject(m->engine()->newStringObject(value)); +} + +Value StringCtor::call(Managed *m, const Value &, Value *argv, int argc) +{ + Value value; + if (argc) + value = Value::fromString(argv[0].toString(m->engine()->current)); + else + value = Value::fromString(m->engine()->current, QString()); + return value; +} + +void StringPrototype::init(ExecutionEngine *engine, const Value &ctor) +{ + ctor.objectValue()->defineReadonlyProperty(engine->id_prototype, Value::fromObject(this)); + ctor.objectValue()->defineReadonlyProperty(engine->id_length, Value::fromInt32(1)); + ctor.objectValue()->defineDefaultProperty(engine, QStringLiteral("fromCharCode"), method_fromCharCode, 1); + + defineDefaultProperty(engine, QStringLiteral("constructor"), ctor); + defineDefaultProperty(engine, QStringLiteral("toString"), method_toString); + defineDefaultProperty(engine, QStringLiteral("valueOf"), method_toString); // valueOf and toString are identical + defineDefaultProperty(engine, QStringLiteral("charAt"), method_charAt, 1); + defineDefaultProperty(engine, QStringLiteral("charCodeAt"), method_charCodeAt, 1); + defineDefaultProperty(engine, QStringLiteral("concat"), method_concat, 1); + defineDefaultProperty(engine, QStringLiteral("indexOf"), method_indexOf, 1); + defineDefaultProperty(engine, QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); + defineDefaultProperty(engine, QStringLiteral("localeCompare"), method_localeCompare, 1); + defineDefaultProperty(engine, QStringLiteral("match"), method_match, 1); + defineDefaultProperty(engine, QStringLiteral("replace"), method_replace, 2); + defineDefaultProperty(engine, QStringLiteral("search"), method_search, 1); + defineDefaultProperty(engine, QStringLiteral("slice"), method_slice, 2); + defineDefaultProperty(engine, QStringLiteral("split"), method_split, 2); + defineDefaultProperty(engine, QStringLiteral("substr"), method_substr, 2); + defineDefaultProperty(engine, QStringLiteral("substring"), method_substring, 2); + defineDefaultProperty(engine, QStringLiteral("toLowerCase"), method_toLowerCase); + defineDefaultProperty(engine, QStringLiteral("toLocaleLowerCase"), method_toLocaleLowerCase); + defineDefaultProperty(engine, QStringLiteral("toUpperCase"), method_toUpperCase); + defineDefaultProperty(engine, QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); + defineDefaultProperty(engine, QStringLiteral("trim"), method_trim); +} + +static QString getThisString(ExecutionContext *ctx) +{ + String* str = 0; + Value thisObject = ctx->thisObject; + if (StringObject *thisString = thisObject.asStringObject()) + str = thisString->value.stringValue(); + else if (thisObject.isUndefined() || thisObject.isNull()) + ctx->throwTypeError(); + else + str = ctx->thisObject.toString(ctx); + return str->toQString(); +} + +static QString getThisString(ExecutionContext *context, Value thisObject) +{ + if (thisObject.isString()) + return thisObject.stringValue()->toQString(); + + String* str = 0; + if (StringObject *thisString = thisObject.asStringObject()) + str = thisString->value.stringValue(); + else if (thisObject.isUndefined() || thisObject.isNull()) + context->throwTypeError(); + else + str = thisObject.toString(context); + return str->toQString(); +} + +Value StringPrototype::method_toString(SimpleCallContext *context) +{ + if (context->thisObject.isString()) + return context->thisObject; + + StringObject *o = context->thisObject.asStringObject(); + if (!o) + context->throwTypeError(); + return o->value; +} + +Value StringPrototype::method_charAt(SimpleCallContext *context) +{ + const QString str = getThisString(context, context->thisObject); + + int pos = 0; + if (context->argumentCount > 0) + pos = (int) context->arguments[0].toInteger(); + + QString result; + if (pos >= 0 && pos < str.length()) + result += str.at(pos); + + return Value::fromString(context, result); +} + +Value StringPrototype::method_charCodeAt(SimpleCallContext *context) +{ + const QString str = getThisString(context, context->thisObject); + + int pos = 0; + if (context->argumentCount > 0) + pos = (int) context->arguments[0].toInteger(); + + + if (pos >= 0 && pos < str.length()) + return Value::fromInt32(str.at(pos).unicode()); + + return Value::fromDouble(qSNaN()); +} + +Value StringPrototype::method_concat(SimpleCallContext *context) +{ + QString value = getThisString(context, context->thisObject); + + for (int i = 0; i < context->argumentCount; ++i) { + Value v = __qmljs_to_string(context->arguments[i], context); + assert(v.isString()); + value += v.stringValue()->toQString(); + } + + return Value::fromString(context, value); +} + +Value StringPrototype::method_indexOf(SimpleCallContext *context) +{ + QString value = getThisString(context, context->thisObject); + + QString searchString; + if (context->argumentCount) + searchString = context->arguments[0].toString(context)->toQString(); + + int pos = 0; + if (context->argumentCount > 1) + pos = (int) context->arguments[1].toInteger(); + + int index = -1; + if (! value.isEmpty()) + index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length())); + + return Value::fromDouble(index); +} + +Value StringPrototype::method_lastIndexOf(SimpleCallContext *context) +{ + const QString value = getThisString(context, context->thisObject); + + QString searchString; + if (context->argumentCount) { + Value v = __qmljs_to_string(context->arguments[0], context); + searchString = v.stringValue()->toQString(); + } + + Value posArg = context->argumentCount > 1 ? context->arguments[1] : Value::undefinedValue(); + double position = __qmljs_to_number(posArg); + if (std::isnan(position)) + position = +qInf(); + else + position = trunc(position); + + int pos = trunc(qMin(qMax(position, 0.0), double(value.length()))); + if (!searchString.isEmpty() && pos == value.length()) + --pos; + if (searchString.isNull() && pos == 0) + return Value::fromDouble(-1); + int index = value.lastIndexOf(searchString, pos); + return Value::fromDouble(index); +} + +Value StringPrototype::method_localeCompare(SimpleCallContext *context) +{ + const QString value = getThisString(context, context->thisObject); + const QString that = (context->argumentCount ? context->arguments[0] : Value::undefinedValue()).toString(context)->toQString(); + return Value::fromDouble(QString::localeAwareCompare(value, that)); +} + +Value StringPrototype::method_match(SimpleCallContext *context) +{ + if (context->thisObject.isUndefined() || context->thisObject.isNull()) + context->throwTypeError(); + + String *s = context->thisObject.toString(context); + + Value regexp = context->argumentCount ? context->arguments[0] : Value::undefinedValue(); + RegExpObject *rx = regexp.as<RegExpObject>(); + if (!rx) + rx = context->engine->regExpCtor.asFunctionObject()->construct(®exp, 1).as<RegExpObject>(); + + if (!rx) + // ### CHECK + context->throwTypeError(); + + bool global = rx->global; + + // ### use the standard builtin function, not the one that might be redefined in the proto + FunctionObject *exec = context->engine->regExpPrototype->get(context->engine->newString(QStringLiteral("exec")), 0).asFunctionObject(); + + Value arg = Value::fromString(s); + if (!global) + return exec->call(Value::fromObject(rx), &arg, 1); + + String *lastIndex = context->engine->newString(QStringLiteral("lastIndex")); + rx->put(lastIndex, Value::fromInt32(0)); + ArrayObject *a = context->engine->newArrayObject(); + + double previousLastIndex = 0; + uint n = 0; + while (1) { + Value result = exec->call(Value::fromObject(rx), &arg, 1); + if (result.isNull()) + break; + assert(result.isObject()); + double thisIndex = rx->get(lastIndex, 0).toInteger(); + if (previousLastIndex == thisIndex) { + previousLastIndex = thisIndex + 1; + rx->put(lastIndex, Value::fromDouble(previousLastIndex)); + } else { + previousLastIndex = thisIndex; + } + Value matchStr = result.objectValue()->getIndexed(0); + a->arraySet(n, matchStr); + ++n; + } + if (!n) + return Value::nullValue(); + + return Value::fromObject(a); + +} + +static QString makeReplacementString(const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) +{ + QString result; + result.reserve(replaceValue.length()); + for (int i = 0; i < replaceValue.length(); ++i) { + if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) { + char ch = replaceValue.at(++i).toLatin1(); + uint substStart = JSC::Yarr::offsetNoMatch; + uint substEnd = JSC::Yarr::offsetNoMatch; + if (ch == '$') { + result += ch; + continue; + } else if (ch == '&') { + substStart = matchOffsets[0]; + substEnd = matchOffsets[1]; + } else if (ch == '`') { + substStart = 0; + substEnd = matchOffsets[0]; + } else if (ch == '\'') { + substStart = matchOffsets[1]; + substEnd = input.length(); + } else if (ch >= '1' && ch <= '9') { + char capture = ch - '0'; + if (capture > 0 && capture < captureCount) { + substStart = matchOffsets[capture * 2]; + substEnd = matchOffsets[capture * 2 + 1]; + } + } else if (ch == '0' && i < replaceValue.length() - 1) { + int capture = (ch - '0') * 10; + ch = replaceValue.at(++i).toLatin1(); + capture += ch - '0'; + if (capture > 0 && capture < captureCount) { + substStart = matchOffsets[capture * 2]; + substEnd = matchOffsets[capture * 2 + 1]; + } + } + if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch) + result += input.midRef(substStart, substEnd - substStart); + } else { + result += replaceValue.at(i); + } + } + return result; +} + +Value StringPrototype::method_replace(SimpleCallContext *ctx) +{ + QString string; + if (StringObject *thisString = ctx->thisObject.asStringObject()) + string = thisString->value.stringValue()->toQString(); + else + string = ctx->thisObject.toString(ctx)->toQString(); + + int numCaptures = 0; + QVarLengthArray<uint, 16> matchOffsets; + int numStringMatches = 0; + + Value searchValue = ctx->argument(0); + RegExpObject *regExp = searchValue.as<RegExpObject>(); + if (regExp) { + uint offset = 0; + while (true) { + int oldSize = matchOffsets.size(); + matchOffsets.resize(matchOffsets.size() + regExp->value->captureCount() * 2); + if (regExp->value->match(string, offset, matchOffsets.data() + oldSize) == JSC::Yarr::offsetNoMatch) { + matchOffsets.resize(oldSize); + break; + } + if (!regExp->global) + break; + offset = qMax(offset + 1, matchOffsets[oldSize + 1]); + } + if (regExp->global) + regExp->lastIndexProperty(ctx)->value = Value::fromUInt32(0); + numStringMatches = matchOffsets.size() / (regExp->value->captureCount() * 2); + numCaptures = regExp->value->captureCount(); + } else { + numCaptures = 1; + QString searchString = searchValue.toString(ctx)->toQString(); + int idx = string.indexOf(searchString); + if (idx != -1) { + numStringMatches = 1; + matchOffsets.resize(2); + matchOffsets[0] = idx; + matchOffsets[1] = idx + searchString.length(); + } + } + + QString result = string; + Value replaceValue = ctx->argument(1); + if (FunctionObject* searchCallback = replaceValue.asFunctionObject()) { + int replacementDelta = 0; + int argc = numCaptures + 2; + Value *args = (Value*)alloca((numCaptures + 2) * sizeof(Value)); + for (int i = 0; i < numStringMatches; ++i) { + for (int k = 0; k < numCaptures; ++k) { + int idx = (i * numCaptures + k) * 2; + uint start = matchOffsets[idx]; + uint end = matchOffsets[idx + 1]; + Value entry = Value::undefinedValue(); + if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch) + entry = Value::fromString(ctx, string.mid(start, end - start)); + args[k] = entry; + } + uint matchStart = matchOffsets[i * numCaptures * 2]; + uint matchEnd = matchOffsets[i * numCaptures * 2 + 1]; + args[numCaptures] = Value::fromUInt32(matchStart); + args[numCaptures + 1] = Value::fromString(ctx, string); + Value replacement = searchCallback->call(Value::undefinedValue(), args, argc); + QString replacementString = replacement.toString(ctx)->toQString(); + result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacementString); + replacementDelta += replacementString.length() - matchEnd + matchStart; + } + } else { + QString newString = replaceValue.toString(ctx)->toQString(); + int replacementDelta = 0; + + for (int i = 0; i < numStringMatches; ++i) { + int baseIndex = i * numCaptures * 2; + uint matchStart = matchOffsets[baseIndex]; + uint matchEnd = matchOffsets[baseIndex + 1]; + if (matchStart == JSC::Yarr::offsetNoMatch) + continue; + + QString replacement = makeReplacementString(string, newString, matchOffsets.data() + baseIndex, numCaptures); + result.replace(replacementDelta + matchStart, matchEnd - matchStart, replacement); + replacementDelta += replacement.length() - matchEnd + matchStart; + } + } + + return Value::fromString(ctx, result); +} + +Value StringPrototype::method_search(SimpleCallContext *ctx) +{ + QString string; + if (StringObject *thisString = ctx->thisObject.asStringObject()) + string = thisString->value.stringValue()->toQString(); + else + string = ctx->thisObject.toString(ctx)->toQString(); + + Value regExpValue = ctx->argument(0); + RegExpObject *regExp = regExpValue.as<RegExpObject>(); + if (!regExp) { + regExpValue = ctx->engine->regExpCtor.asFunctionObject()->construct(®ExpValue, 1); + regExp = regExpValue.as<RegExpObject>(); + } + uint* matchOffsets = (uint*)alloca(regExp->value->captureCount() * 2 * sizeof(uint)); + uint result = regExp->value->match(string, /*offset*/0, matchOffsets); + if (result == JSC::Yarr::offsetNoMatch) + return Value::fromInt32(-1); + return Value::fromUInt32(result); +} + +Value StringPrototype::method_slice(SimpleCallContext *ctx) +{ + const QString text = getThisString(ctx); + const double length = text.length(); + + double start = ctx->argument(0).toInteger(); + double end = ctx->argument(1).isUndefined() + ? length : ctx->argument(1).toInteger(); + + if (start < 0) + start = qMax(length + start, 0.); + else + start = qMin(start, length); + + if (end < 0) + end = qMax(length + end, 0.); + else + end = qMin(end, length); + + const int intStart = int(start); + const int intEnd = int(end); + + int count = qMax(0, intEnd - intStart); + return Value::fromString(ctx, text.mid(intStart, count)); +} + +Value StringPrototype::method_split(SimpleCallContext *ctx) +{ + QString text; + if (StringObject *thisObject = ctx->thisObject.asStringObject()) + text = thisObject->value.stringValue()->toQString(); + else + text = ctx->thisObject.toString(ctx)->toQString(); + + Value separatorValue = ctx->argumentCount > 0 ? ctx->argument(0) : Value::undefinedValue(); + Value limitValue = ctx->argumentCount > 1 ? ctx->argument(1) : Value::undefinedValue(); + + ArrayObject* array = ctx->engine->newArrayObject(); + Value result = Value::fromObject(array); + + if (separatorValue.isUndefined()) { + if (limitValue.isUndefined()) { + array->push_back(Value::fromString(ctx, text)); + return result; + } + return Value::fromString(ctx, text.left(limitValue.toInteger())); + } + + uint limit = limitValue.isUndefined() ? UINT_MAX : limitValue.toUInt32(); + + if (limit == 0) + return result; + + if (RegExpObject* re = separatorValue.as<RegExpObject>()) { + if (re->value->pattern().isEmpty()) { + re = 0; + separatorValue = Value::fromString(ctx, QString()); + } + } + + if (RegExpObject* re = separatorValue.as<RegExpObject>()) { + uint offset = 0; + uint* matchOffsets = (uint*)alloca(re->value->captureCount() * 2 * sizeof(uint)); + while (true) { + uint result = re->value->match(text, offset, matchOffsets); + if (result == JSC::Yarr::offsetNoMatch) + break; + + array->push_back(Value::fromString(ctx, text.mid(offset, matchOffsets[0] - offset))); + offset = qMax(offset + 1, matchOffsets[1]); + + if (array->arrayLength() >= limit) + break; + + for (int i = 1; i < re->value->captureCount(); ++i) { + uint start = matchOffsets[i * 2]; + uint end = matchOffsets[i * 2 + 1]; + array->push_back(Value::fromString(ctx, text.mid(start, end - start))); + if (array->arrayLength() >= limit) + break; + } + } + if (array->arrayLength() < limit) + array->push_back(Value::fromString(ctx, text.mid(offset))); + } else { + QString separator = separatorValue.toString(ctx)->toQString(); + if (separator.isEmpty()) { + for (uint i = 0; i < qMin(limit, uint(text.length())); ++i) + array->push_back(Value::fromString(ctx, text.mid(i, 1))); + return result; + } + + int start = 0; + int end; + while ((end = text.indexOf(separator, start)) != -1) { + array->push_back(Value::fromString(ctx, text.mid(start, end - start))); + start = end + separator.size(); + if (array->arrayLength() >= limit) + break; + } + if (array->arrayLength() < limit && start != -1) + array->push_back(Value::fromString(ctx, text.mid(start))); + } + return result; +} + +Value StringPrototype::method_substr(SimpleCallContext *context) +{ + const QString value = getThisString(context, context->thisObject); + + double start = 0; + if (context->argumentCount > 0) + start = context->arguments[0].toInteger(); + + double length = +qInf(); + if (context->argumentCount > 1) + length = context->arguments[1].toInteger(); + + double count = value.length(); + if (start < 0) + start = qMax(count + start, 0.0); + + length = qMin(qMax(length, 0.0), count - start); + + qint32 x = Value::toInt32(start); + qint32 y = Value::toInt32(length); + return Value::fromString(context, value.mid(x, y)); +} + +Value StringPrototype::method_substring(SimpleCallContext *context) +{ + QString value = getThisString(context, context->thisObject); + int length = value.length(); + + double start = 0; + double end = length; + + if (context->argumentCount > 0) + start = context->arguments[0].toInteger(); + + Value endValue = context->argumentCount > 1 ? context->arguments[1] : Value::undefinedValue(); + if (!endValue.isUndefined()) + end = endValue.toInteger(); + + if (std::isnan(start) || start < 0) + start = 0; + + if (std::isnan(end) || end < 0) + end = 0; + + if (start > length) + start = length; + + if (end > length) + end = length; + + if (start > end) { + double was = start; + start = end; + end = was; + } + + qint32 x = (int)start; + qint32 y = (int)(end - start); + return Value::fromString(context, value.mid(x, y)); +} + +Value StringPrototype::method_toLowerCase(SimpleCallContext *ctx) +{ + QString value = getThisString(ctx); + return Value::fromString(ctx, value.toLower()); +} + +Value StringPrototype::method_toLocaleLowerCase(SimpleCallContext *ctx) +{ + return method_toLowerCase(ctx); +} + +Value StringPrototype::method_toUpperCase(SimpleCallContext *ctx) +{ + QString value = getThisString(ctx); + return Value::fromString(ctx, value.toUpper()); +} + +Value StringPrototype::method_toLocaleUpperCase(SimpleCallContext *ctx) +{ + return method_toUpperCase(ctx); +} + +Value StringPrototype::method_fromCharCode(SimpleCallContext *context) +{ + QString str(context->argumentCount, Qt::Uninitialized); + QChar *ch = str.data(); + for (int i = 0; i < context->argumentCount; ++i) { + *ch = QChar(context->arguments[i].toUInt16()); + ++ch; + } + return Value::fromString(context, str); +} + +Value StringPrototype::method_trim(SimpleCallContext *ctx) +{ + if (ctx->thisObject.isNull() || ctx->thisObject.isUndefined()) + ctx->throwTypeError(); + + QString s = __qmljs_to_string(ctx->thisObject, ctx).stringValue()->toQString(); + const QChar *chars = s.constData(); + int start, end; + for (start = 0; start < s.length(); ++start) { + if (!chars[start].isSpace() && chars[start].unicode() != 0xfeff) + break; + } + for (end = s.length() - 1; end >= start; --end) { + if (!chars[end].isSpace() && chars[end].unicode() != 0xfeff) + break; + } + + return Value::fromString(ctx, QString(chars + start, end - start + 1)); +} diff --git a/src/qml/qml/v4/qv4stringobject_p.h b/src/qml/qml/v4/qv4stringobject_p.h new file mode 100644 index 0000000000..0ef6596235 --- /dev/null +++ b/src/qml/qml/v4/qv4stringobject_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4STRINGOBJECT_P_H +#define QV4STRINGOBJECT_P_H + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct StringObject: Object { + Q_MANAGED + + Value value; + mutable Property tmpProperty; + StringObject(ExecutionEngine *engine, const Value &value); + + Property *getIndex(uint index) const; + + static bool deleteIndexedProperty(Managed *m, uint index); + +protected: + static Property *advanceIterator(Managed *m, ObjectIterator *it, String **name, uint *index, PropertyAttributes *attrs); + static void markObjects(Managed *that); +}; + +struct StringCtor: FunctionObject +{ + StringCtor(ExecutionContext *scope); + + static Value construct(Managed *m, Value *args, int argc); + static Value call(Managed *that, const Value &, Value *, int); + +protected: + static const ManagedVTable static_vtbl; +}; + +struct StringPrototype: StringObject +{ + StringPrototype(ExecutionEngine *engine): StringObject(engine, Value::fromString(engine, QString())) {} + void init(ExecutionEngine *engine, const Value &ctor); + + static Value method_toString(SimpleCallContext *context); + static Value method_charAt(SimpleCallContext *context); + static Value method_charCodeAt(SimpleCallContext *context); + static Value method_concat(SimpleCallContext *context); + static Value method_indexOf(SimpleCallContext *context); + static Value method_lastIndexOf(SimpleCallContext *context); + static Value method_localeCompare(SimpleCallContext *context); + static Value method_match(SimpleCallContext *context); + static Value method_replace(SimpleCallContext *ctx); + static Value method_search(SimpleCallContext *ctx); + static Value method_slice(SimpleCallContext *ctx); + static Value method_split(SimpleCallContext *ctx); + static Value method_substr(SimpleCallContext *context); + static Value method_substring(SimpleCallContext *context); + static Value method_toLowerCase(SimpleCallContext *ctx); + static Value method_toLocaleLowerCase(SimpleCallContext *ctx); + static Value method_toUpperCase(SimpleCallContext *ctx); + static Value method_toLocaleUpperCase(SimpleCallContext *ctx); + static Value method_fromCharCode(SimpleCallContext *context); + static Value method_trim(SimpleCallContext *ctx); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/qml/v4/qv4syntaxchecker.cpp b/src/qml/qml/v4/qv4syntaxchecker.cpp new file mode 100644 index 0000000000..a4f7423cd7 --- /dev/null +++ b/src/qml/qml/v4/qv4syntaxchecker.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4syntaxchecker_p.h" + +QT_BEGIN_NAMESPACE + +using namespace QQmlJS; + +SyntaxChecker::SyntaxChecker() + : Lexer(&m_engine) + , m_stateStack(128) +{ +} + +void QQmlJS::SyntaxChecker::clearText() +{ + m_code.clear(); + m_tokens.clear(); +} + +void SyntaxChecker::appendText(const QString &text) +{ + m_code += text; +} + +QString SyntaxChecker::text() const +{ + return m_code; +} + +bool SyntaxChecker::canEvaluate() +{ + int yyaction = 0; + int yytoken = -1; + int yytos = -1; + + setCode(m_code, 1); + + m_tokens.clear(); + m_tokens.append(T_FEED_JS_PROGRAM); + + do { + if (++yytos == m_stateStack.size()) + m_stateStack.resize(m_stateStack.size() * 2); + + m_stateStack[yytos] = yyaction; + +again: + if (yytoken == -1 && action_index[yyaction] != -TERMINAL_COUNT) { + if (m_tokens.isEmpty()) + yytoken = lex(); + else + yytoken = m_tokens.takeFirst(); + } + + yyaction = t_action(yyaction, yytoken); + if (yyaction > 0) { + if (yyaction == ACCEPT_STATE) { + --yytos; + return true; + } + yytoken = -1; + } else if (yyaction < 0) { + const int ruleno = -yyaction - 1; + yytos -= rhs[ruleno]; + yyaction = nt_action(m_stateStack[yytos], lhs[ruleno] - TERMINAL_COUNT); + } + } while (yyaction); + + const int errorState = m_stateStack[yytos]; + if (t_action(errorState, T_AUTOMATIC_SEMICOLON) && canInsertAutomaticSemicolon(yytoken)) { + yyaction = errorState; + m_tokens.prepend(yytoken); + yytoken = T_SEMICOLON; + goto again; + } + + if (yytoken != EOF_SYMBOL) + return true; + + return false; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4syntaxchecker_p.h b/src/qml/qml/v4/qv4syntaxchecker_p.h new file mode 100644 index 0000000000..bdb88b0525 --- /dev/null +++ b/src/qml/qml/v4/qv4syntaxchecker_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4SYNTAXCHECKER_P_H +#define QV4SYNTAXCHECKER_P_H + +#include <private/qqmljslexer_p.h> +#include <private/qqmljsengine_p.h> + +#include <QtCore/QVector> +#include <QtCore/QString> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { + +class SyntaxChecker: Lexer +{ +public: + SyntaxChecker(); + + QString text() const; + void clearText(); + void appendText(const QString &text); + + bool canEvaluate(); + +private: + Engine m_engine; + QVector<int> m_stateStack; + QList<int> m_tokens; + QString m_code; +}; + +} // end of QQmlJS namespace + +QT_END_NAMESPACE + +#endif // QV4SYNTAXCHECKER_P_H diff --git a/src/qml/qml/v4/qv4unwindhelper.cpp b/src/qml/qml/v4/qv4unwindhelper.cpp new file mode 100644 index 0000000000..beb5132626 --- /dev/null +++ b/src/qml/qml/v4/qv4unwindhelper.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qv4unwindhelper_p.h> + +#include <wtf/Platform.h> + +#if CPU(X86_64) && (OS(LINUX) || OS(MAC_OS_X)) +# define USE_DW2_HELPER +#elif CPU(X86) && COMPILER(GCC) +# define USE_DW2_HELPER +#elif CPU(ARM) && (OS(LINUX) || OS(QNX)) +# define USE_ARM_HELPER +#elif OS(WINDOWS) + // SJLJ will unwind on Windows +# define USE_NULL_HELPER +#elif OS(IOS) + // SJLJ will unwind on iOS +# define USE_NULL_HELPER +#else +# warning "Unsupported/untested platform!" +# define USE_NULL_HELPER +#endif + +#ifdef USE_DW2_HELPER +# include <qv4unwindhelper_p-dw2.h> +#endif // USE_DW2_HELPER + +#ifdef USE_ARM_HELPER +# include <qv4unwindhelper_p-arm.h> +#endif // USE_ARM_HELPER + +QT_BEGIN_NAMESPACE + +#ifdef USE_NULL_HELPER +using namespace QV4; +void UnwindHelper::prepareForUnwind(ExecutionContext *) {} +void UnwindHelper::registerFunction(Function *function) {Q_UNUSED(function);} +void UnwindHelper::registerFunctions(const QVector<Function *> &functions) {Q_UNUSED(functions);} +void UnwindHelper::deregisterFunction(Function *function) {Q_UNUSED(function);} +void UnwindHelper::deregisterFunctions(const QVector<Function *> &functions) {Q_UNUSED(functions);} +#endif // USE_NULL_HELPER + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4unwindhelper_p-arm.h b/src/qml/qml/v4/qv4unwindhelper_p-arm.h new file mode 100644 index 0000000000..dd1f1e4856 --- /dev/null +++ b/src/qml/qml/v4/qv4unwindhelper_p-arm.h @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4UNWINDHELPER_PDW2_H +#define QV4UNWINDHELPER_PDW2_H + +#include "qv4unwindhelper_p.h" +#include "qv4functionobject_p.h" +#include "qv4function_p.h" +#include <wtf/Platform.h> + +#include <QMap> +#include <QMutex> + +#define __USE_GNU +#include <dlfcn.h> + +#if USE(LIBUNWIND_DEBUG) +#include <libunwind.h> +#include <execinfo.h> +#endif + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +static void *removeThumbBit(void *addr) +{ + return reinterpret_cast<void*>(reinterpret_cast<intptr_t>(addr) & ~1u); +} + +static QMutex functionProtector; +static QMap<quintptr, Function*> allFunctions; + +static Function *lookupFunction(void *pc) +{ + quintptr key = reinterpret_cast<quintptr>(pc); + QMap<quintptr, Function*>::ConstIterator it = allFunctions.lowerBound(key); + if (it != allFunctions.begin() && allFunctions.count() > 0) + --it; + if (it == allFunctions.end()) + return 0; + + quintptr codeStart = reinterpret_cast<quintptr>(removeThumbBit((*it)->codeRef.code().executableAddress())); + if (key < codeStart || key >= codeStart + (*it)->codeSize) + return 0; + return *it; +} + + +/* Program: +vsp = r4 (REG_TO_SP r4) +vsp -= 8 * 4 -- > vsp = vsp - (7 << 2) - 4 +pop r12, r10, r9, r8, r7, r6, r5, r4 +pop r4 +pop lr +pop r0, r1, r2, r3 +*/ + +#define REG_TO_SP 0b10010000 +#define VSP_MINUS 0b01000000 +#define POP_REG_MULTI 0b10000000 +#define POP_R4_MULTI 0b10100000 +#define POP_R4_R14_MULTI 0b10101000 +#define POP_R0_TO_R3 0b10110001 +#define FINISH 0b10110000 + +#define MK_UW_WORD(first, second, third, fourth) \ + (((first) << 24) | \ + ((second) << 16) | \ + ((third) << 8) | \ + (fourth)) + +static unsigned int extbl[] = { + MK_UW_WORD(0x80 | // High bit set to indicate that this isn't a PREL31 + 2, // Choose personality routine #2 + 2, // Number of 4 byte words used to encode remaining unwind instructions + REG_TO_SP | 4, // Encoded program from above. + VSP_MINUS | 7), + MK_UW_WORD(POP_REG_MULTI | 1, 0b01111111, + POP_R4_R14_MULTI, + POP_R0_TO_R3), + MK_UW_WORD(0b00001111, + FINISH, + FINISH, + FINISH) +}; + +static unsigned write_prel31(unsigned *addr, void *ptr) +{ + int delta = (char *)ptr - (char*)addr; + if (delta < 0) + delta |= (1 << 30); + else + delta &= ~(1 << 30); + *addr = ((unsigned)delta) & 0x7fffffffU; +} + +void UnwindHelper::deregisterFunction(Function *function) +{ + QMutexLocker locker(&functionProtector); + allFunctions.remove(reinterpret_cast<quintptr>(function->code)); +} + +void UnwindHelper::deregisterFunctions(const QVector<Function *> &functions) +{ + QMutexLocker locker(&functionProtector); + foreach (Function *f, functions) + allFunctions.remove(reinterpret_cast<quintptr>(f->code)); +} + +void UnwindHelper::registerFunction(Function *function) +{ + QMutexLocker locker(&functionProtector); + allFunctions.insert(reinterpret_cast<quintptr>(function->code), function); +} + +void UnwindHelper::registerFunctions(const QVector<Function *> &functions) +{ + QMutexLocker locker(&functionProtector); + foreach (Function *f, functions) + allFunctions.insert(reinterpret_cast<quintptr>(f->code), f); +} + +void UnwindHelper::prepareForUnwind(ExecutionContext *) +{ +} + +int UnwindHelper::unwindInfoSize() +{ + return 2 * sizeof(unsigned int) // 2 extbl entries + + sizeof(extbl); +} + +void UnwindHelper::writeARMUnwindInfo(void *codeAddr, int codeSize) +{ + unsigned int *exidx = (unsigned int *)((char *)codeAddr + codeSize); + + unsigned char *exprog = (unsigned char *)((unsigned char *)codeAddr + codeSize + 8); + + write_prel31(exidx, codeAddr); + exidx[1] = 4; // PREL31 offset to extbl, which follows right afterwards + + memcpy(exprog, extbl, sizeof(extbl)); + +#if USE(LIBUNWIND_DEBUG) + unw_dyn_info_t *info = (unw_dyn_info_t*)malloc(sizeof(unw_dyn_info_t)); + info->start_ip = (unw_word_t)codeAddr; + info->end_ip = info->start_ip + codeSize; + info->gp = 0; + info->format = UNW_INFO_FORMAT_ARM_EXIDX; + info->u.rti.name_ptr = 0; + info->u.rti.segbase = 0; + info->u.rti.table_len = 8; + info->u.rti.table_data = (unw_word_t)exidx; + _U_dyn_register(info); +#endif +} + +} + +QT_END_NAMESPACE + +#if defined(Q_OS_ANDROID) +extern "C" void *dl_unwind_find_exidx(void *pc, int *entryCount); +#endif + +extern "C" Q_DECL_EXPORT void *__gnu_Unwind_Find_exidx(void *pc, int *entryCount) +{ +#if !defined(Q_OS_ANDROID) + typedef void *(*Old_Unwind_Find_exidx)(void*, int*); + static Old_Unwind_Find_exidx oldFunction = 0; + if (!oldFunction) + oldFunction = (Old_Unwind_Find_exidx)dlsym(RTLD_NEXT, "__gnu_Unwind_Find_exidx"); +#endif + + { + QMutexLocker locker(&QT_PREPEND_NAMESPACE(QV4::functionProtector)); + QV4::Function *function = QT_PREPEND_NAMESPACE(QV4::lookupFunction(pc)); + if (function) { + *entryCount = 1; + void * codeStart = QT_PREPEND_NAMESPACE(QV4::removeThumbBit(function->codeRef.code().executableAddress())); + // At the end of the function we store our synthetic exception table entry. + return (char *)codeStart + function->codeSize; + } + } + +#if defined(Q_OS_ANDROID) + return dl_unwind_find_exidx(pc, entryCount); +#else + return oldFunction(pc, entryCount); +#endif +} + +#endif // QV4UNWINDHELPER_PDW2_H diff --git a/src/qml/qml/v4/qv4unwindhelper_p-dw2.h b/src/qml/qml/v4/qv4unwindhelper_p-dw2.h new file mode 100644 index 0000000000..57615f0999 --- /dev/null +++ b/src/qml/qml/v4/qv4unwindhelper_p-dw2.h @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4UNWINDHELPER_PDW2_H +#define QV4UNWINDHELPER_PDW2_H + +#include "qv4unwindhelper_p.h" +#include "qv4functionobject_p.h" +#include "qv4function_p.h" +#include <wtf/Platform.h> +#include <wtf/PageAllocation.h> +#include <ExecutableAllocator.h> + +#include <QMap> +#include <QMutex> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace { +#if CPU(X86_64) +// Generated by fdegen +static const unsigned char cie_fde_data[] = { + 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x78, 0x10, 0xc, 0x7, 0x8, + 0x90, 0x1, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, + 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x41, 0x13, 0x7e, 0x86, + 0x2, 0x43, 0xd, 0x6, 0x8c, 0x3, 0x8e, 0x4, + 0x0, 0x0, 0x0, 0x0 +}; +static const int fde_offset = 20; +static const int initial_location_offset = 28; +static const int address_range_offset = 36; +#elif CPU(X86) +static const unsigned char cie_fde_data[] = { + 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x7c, 0x8, 0xc, 0x4, 0x4, + 0x88, 0x1, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, + 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x41, 0x13, 0x7e, 0x85, + 0x2, 0x43, 0xd, 0x5, 0x86, 0x3, 0x87, 0x4, + 0x0, 0x0, 0x0, 0x0, +}; +static const int fde_offset = 20; +static const int initial_location_offset = 28; +static const int address_range_offset = 32; +#endif + +void writeIntPtrValue(unsigned char *addr, intptr_t val) +{ + addr[0] = (val >> 0) & 0xff; + addr[1] = (val >> 8) & 0xff; + addr[2] = (val >> 16) & 0xff; + addr[3] = (val >> 24) & 0xff; +#if QT_POINTER_SIZE == 8 + addr[4] = (val >> 32) & 0xff; + addr[5] = (val >> 40) & 0xff; + addr[6] = (val >> 48) & 0xff; + addr[7] = (val >> 56) & 0xff; +#endif +} +} // anonymous namespace + +extern "C" void __register_frame(void *fde); +extern "C" void __deregister_frame(void *fde); + +struct UnwindInfo : public ExecutableAllocator::PlatformUnwindInfo +{ + UnwindInfo(const QByteArray &cieFde); + virtual ~UnwindInfo(); + QByteArray data; +}; + +UnwindInfo::UnwindInfo(const QByteArray &cieFde) + : data(cieFde) +{ + __register_frame(data.data() + fde_offset); +} + +UnwindInfo::~UnwindInfo() +{ + __deregister_frame(data.data() + fde_offset); +} + +static void ensureUnwindInfo(Function *f) +{ + if (!f->codeRef) + return; // Not a JIT generated function + + JSC::ExecutableMemoryHandle *handle = f->codeRef.executableMemory(); + if (!handle) + return; + ExecutableAllocator::ChunkOfPages *chunk = handle->chunk(); + + // Already registered? + if (chunk->unwindInfo) + return; + + QByteArray info; + info.resize(sizeof(cie_fde_data)); + + unsigned char *cie_and_fde = reinterpret_cast<unsigned char *>(info.data()); + memcpy(cie_and_fde, cie_fde_data, sizeof(cie_fde_data)); + + intptr_t ptr = static_cast<char *>(chunk->pages->base()) - static_cast<char *>(0); + writeIntPtrValue(cie_and_fde + initial_location_offset, ptr); + + writeIntPtrValue(cie_and_fde + address_range_offset, chunk->pages->size()); + + chunk->unwindInfo = new UnwindInfo(info); +} + +void UnwindHelper::prepareForUnwind(ExecutionContext *context) +{ + for (ExecutionContext *ctx = context; ctx; ctx = ctx->parent) { + if (CallContext *callCtx = ctx->asCallContext()) + if (FunctionObject *fobj = callCtx->function) + if (Function *fun = fobj->function) + ensureUnwindInfo(fun); + for (ExecutionContext::EvalCode *code = ctx->currentEvalCode; + code; code = code->next) + ensureUnwindInfo(code->function); + } + + if (context->engine->globalCode) + ensureUnwindInfo(context->engine->globalCode); +} + +void UnwindHelper::registerFunction(Function *) +{ +} + +void UnwindHelper::registerFunctions(const QVector<Function *>&) +{ +} + +void UnwindHelper::deregisterFunction(Function *) +{ +} + +void UnwindHelper::deregisterFunctions(const QVector<Function *> &) +{ +} + +} + +QT_END_NAMESPACE + +#endif // QV4UNWINDHELPER_PDW2_H diff --git a/src/qml/qml/v4/qv4unwindhelper_p.h b/src/qml/qml/v4/qv4unwindhelper_p.h new file mode 100644 index 0000000000..9ef564449a --- /dev/null +++ b/src/qml/qml/v4/qv4unwindhelper_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4UNWINDHELPER_H +#define QV4UNWINDHELPER_H + +#include <QtCore/QVector> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Function; +struct ExecutionContext; + +class UnwindHelper +{ +public: + static void prepareForUnwind(ExecutionContext *ctx); + static void registerFunction(Function *function); + static void registerFunctions(const QVector<Function *> &functions); + static void deregisterFunction(Function *function); + static void deregisterFunctions(const QVector<Function *> &functions); +#ifdef Q_PROCESSOR_ARM + static int unwindInfoSize(); + static void writeARMUnwindInfo(void *codeAddr, int codeSize); +#endif +}; + +} + +QT_END_NAMESPACE + +#endif // QV4UNWINDHELPER_H diff --git a/src/qml/qml/v4/qv4util_p.h b/src/qml/qml/v4/qv4util_p.h new file mode 100644 index 0000000000..dbd9f89faa --- /dev/null +++ b/src/qml/qml/v4/qv4util_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4UTIL_H +#define QV4UTIL_H + +#include "qv4global_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +template <typename T> +struct TemporaryAssignment +{ + TemporaryAssignment(T &var, const T& temporaryValue) + : variable(var) + , savedValue(var) + { + variable = temporaryValue; + } + ~TemporaryAssignment() + { + variable = savedValue; + } + T &variable; + T savedValue; +private: + TemporaryAssignment(const TemporaryAssignment<T>&); + TemporaryAssignment operator=(const TemporaryAssignment<T>&); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4UTIL_H diff --git a/src/qml/qml/v4/qv4value.cpp b/src/qml/qml/v4/qv4value.cpp new file mode 100644 index 0000000000..a41262f12f --- /dev/null +++ b/src/qml/qml/v4/qv4value.cpp @@ -0,0 +1,403 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qv4engine_p.h> +#include <qv4object_p.h> +#include <qv4objectproto_p.h> +#include "qv4mm_p.h" +#include "qv4exception_p.h" + +#include <wtf/MathExtras.h> + +using namespace QV4; + +int Value::toUInt16() const +{ + if (isConvertibleToInt()) + return (ushort)(uint)integerValue(); + + double number = __qmljs_to_number(*this); + + double D16 = 65536.0; + if ((number >= 0 && number < D16)) + return static_cast<ushort>(number); + + if (!std::isfinite(number)) + return +0; + + double d = ::floor(::fabs(number)); + if (std::signbit(number)) + d = -d; + + number = ::fmod(d , D16); + + if (number < 0) + number += D16; + + return (unsigned short)number; +} + +double Value::toInteger() const +{ + if (isConvertibleToInt()) + return int_32; + + return Value::toInteger(__qmljs_to_number(*this)); +} + +double Value::toNumber() const +{ + return __qmljs_to_number(*this); +} + +QString Value::toQString() const +{ + switch (type()) { + case Value::Undefined_Type: + return QStringLiteral("undefined"); + case Value::Null_Type: + return QStringLiteral("null"); + case Value::Boolean_Type: + if (booleanValue()) + return QStringLiteral("true"); + else + return QStringLiteral("false"); + case Value::String_Type: + return stringValue()->toQString(); + case Value::Object_Type: { + ExecutionContext *ctx = objectValue()->internalClass->engine->current; + try { + Value prim = __qmljs_to_primitive(*this, STRING_HINT); + if (prim.isPrimitive()) + return prim.toQString(); + } catch (Exception &e) { + e.accept(ctx); + try { + Value prim = __qmljs_to_primitive(e.value(), STRING_HINT); + if (prim.isPrimitive()) + return prim.toQString(); + } catch(Exception &e) { + e.accept(ctx); + } + } + return QString(); + } + case Value::Integer_Type: { + QString str; + __qmljs_numberToString(&str, (double)int_32, 10); + return str; + } + default: { // double + QString str; + __qmljs_numberToString(&str, doubleValue(), 10); + return str; + } + } // switch +} + +bool Value::sameValue(Value other) const { + if (val == other.val) + return true; + if (isString() && other.isString()) + return stringValue()->isEqualTo(other.stringValue()); + if (isInteger()) + return int_32 ? (double(int_32) == other.dbl) : (other.val == 0); + if (other.isInteger()) + return other.int_32 ? (dbl == double(other.int_32)) : (val == 0); + return false; +} + +Value Value::fromString(ExecutionContext *ctx, const QString &s) +{ + return fromString(ctx->engine->newString(s)); +} + +Value Value::fromString(ExecutionEngine *engine, const QString &s) +{ + return fromString(engine->newString(s)); +} + + +int Value::toInt32(double number) +{ + const double D32 = 4294967296.0; + const double D31 = D32 / 2.0; + + if ((number >= -D31 && number < D31)) + return static_cast<int>(number); + + + if (!std::isfinite(number)) + return 0; + + double d = ::floor(::fabs(number)); + if (std::signbit(number)) + d = -d; + + number = ::fmod(d , D32); + + if (number < -D31) + number += D32; + else if (number >= D31) + number -= D32; + + return int(number); +} + +unsigned int Value::toUInt32(double number) +{ + const double D32 = 4294967296.0; + if ((number >= 0 && number < D32)) + return static_cast<uint>(number); + + if (!std::isfinite(number)) + return +0; + + double d = ::floor(::fabs(number)); + if (std::signbit(number)) + d = -d; + + number = ::fmod(d , D32); + + if (number < 0) + number += D32; + + return unsigned(number); +} + +double Value::toInteger(double number) +{ + if (std::isnan(number)) + return +0; + else if (! number || std::isinf(number)) + return number; + const double v = floor(fabs(number)); + return std::signbit(number) ? -v : v; +} + +String *Value::toString(ExecutionContext *ctx) const +{ + if (isString()) + return stringValue(); + return __qmljs_convert_to_string(ctx, *this); +} + +Value Value::property(ExecutionContext *ctx, String *name) const +{ + return isObject() ? objectValue()->get(name) : undefinedValue(); +} + + +PersistentValue::PersistentValue(const Value &val) + : d(new PersistentValuePrivate(val)) +{ +} + +PersistentValue::PersistentValue(const PersistentValue &other) + : d(other.d) +{ + if (d) + d->ref(); +} + +PersistentValue &PersistentValue::operator=(const PersistentValue &other) +{ + if (d == other.d) + return *this; + + // the memory manager cleans up those with a refcount of 0 + + if (d) + d->deref(); + d = other.d; + if (d) + d->ref(); + + return *this; +} + +PersistentValue &PersistentValue::operator =(const Value &other) +{ + if (!d) { + d = new PersistentValuePrivate(other); + return *this; + } + d = d->detach(other); + return *this; +} + +PersistentValue::~PersistentValue() +{ + if (d) + d->deref(); +} + +WeakValue::WeakValue(const Value &val) + : d(new PersistentValuePrivate(val, /*engine*/0, /*weak*/true)) +{ +} + +WeakValue::WeakValue(const WeakValue &other) + : d(other.d) +{ + if (d) + d->ref(); +} + +WeakValue &WeakValue::operator=(const WeakValue &other) +{ + if (d == other.d) + return *this; + + // the memory manager cleans up those with a refcount of 0 + + if (d) + d->deref(); + d = other.d; + if (d) + d->ref(); + + return *this; +} + +WeakValue &WeakValue::operator =(const Value &other) +{ + if (!d) { + d = new PersistentValuePrivate(other, /*engine*/0, /*weak*/true); + return *this; + } + d = d->detach(other, /*weak*/true); + return *this; +} + + +WeakValue::~WeakValue() +{ + if (d) + d->deref(); +} + +void WeakValue::markOnce() +{ + if (!d) + return; + Managed *m = d->value.asManaged(); + if (!m) + return; + m->mark(); +} + +PersistentValuePrivate::PersistentValuePrivate(const Value &v, ExecutionEngine *e, bool weak) + : value(v) + , refcount(1) + , prev(0) + , next(0) + , engine(e) +{ + if (!engine) { + Managed *m = v.asManaged(); + if (!m) + return; + + engine = m->engine(); + } + if (engine) { + PersistentValuePrivate **listRoot = weak ? &engine->memoryManager->m_weakValues : &engine->memoryManager->m_persistentValues; + + prev = listRoot; + next = *listRoot; + *prev = this; + if (next) + next->prev = &this->next; + } +} + +PersistentValuePrivate::~PersistentValuePrivate() +{ +} + +void PersistentValuePrivate::removeFromList() +{ + if (prev) { + if (next) + next->prev = prev; + *prev = next; + next = 0; + prev = 0; + } +} + +void PersistentValuePrivate::deref() +{ + // if engine is not 0, they are registered with the memory manager + // and will get cleaned up in the next gc run + if (!--refcount) { + removeFromList(); + delete this; + } +} + +PersistentValuePrivate *PersistentValuePrivate::detach(const QV4::Value &value, bool weak) +{ + if (refcount == 1) { + this->value = value; + + Managed *m = value.asManaged(); + if (!prev) { + if (m) { + ExecutionEngine *engine = m->engine(); + if (engine) { + PersistentValuePrivate **listRoot = weak ? &engine->memoryManager->m_weakValues : &engine->memoryManager->m_persistentValues; + prev = listRoot; + next = *listRoot; + *prev = this; + if (next) + next->prev = &this->next; + } + } + } else if (!m) + removeFromList(); + + return this; + } + --refcount; + return new PersistentValuePrivate(value, engine, weak); +} + diff --git a/src/qml/qml/v4/qv4value_def_p.h b/src/qml/qml/v4/qv4value_def_p.h new file mode 100644 index 0000000000..2e3a9c0425 --- /dev/null +++ b/src/qml/qml/v4/qv4value_def_p.h @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4VALUE_DEF_P_H +#define QV4VALUE_DEF_P_H + +#include <QtCore/QString> +#include "qv4global_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +typedef uint Bool; + +struct Q_QML_EXPORT Value +{ + union { + quint64 val; + double dbl; + struct { +#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN + uint tag; +#endif + union { + uint uint_32; + int int_32; +#if QT_POINTER_SIZE == 4 + Managed *m; + Object *o; + String *s; +#endif + }; +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + uint tag; +#endif + }; + }; + + enum Masks { + NotDouble_Mask = 0xfffc0000, + Type_Mask = 0xffff8000, + Immediate_Mask = NotDouble_Mask | 0x00008000, + Special_Mask = Immediate_Mask | 0x20000, + Tag_Shift = 32 + }; + enum ValueType { + Undefined_Type = Immediate_Mask | 0x00000, + Null_Type = Immediate_Mask | 0x10000, + Boolean_Type = Immediate_Mask | 0x20000, + Integer_Type = Immediate_Mask | 0x30000, + Object_Type = NotDouble_Mask | 0x00000, + String_Type = NotDouble_Mask | 0x10000, + Deleted_Type = NotDouble_Mask | 0x30000 + }; + + enum ImmediateFlags { + ConvertibleToInt = Immediate_Mask | 0x1 + }; + + enum ValueTypeInternal { + _Undefined_Type = Undefined_Type, + _Empty_Type = Deleted_Type, + _Null_Type = Null_Type | ConvertibleToInt, + _Boolean_Type = Boolean_Type | ConvertibleToInt, + _Integer_Type = Integer_Type | ConvertibleToInt, + _Object_Type = Object_Type, + _String_Type = String_Type + + }; + + inline unsigned type() const { + return tag & Type_Mask; + } + + // used internally in property + inline bool isEmpty() const { return tag == _Empty_Type; } + + inline bool isUndefined() const { return tag == _Undefined_Type; } + inline bool isNull() const { return tag == _Null_Type; } + inline bool isBoolean() const { return tag == _Boolean_Type; } + inline bool isInteger() const { return tag == _Integer_Type; } + inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; } + inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; } +#if QT_POINTER_SIZE == 8 + inline bool isString() const { return (tag & Type_Mask) == String_Type; } + inline bool isObject() const { return (tag & Type_Mask) == Object_Type; } +#else + inline bool isString() const { return tag == String_Type; } + inline bool isObject() const { return tag == Object_Type; } +#endif + inline bool isConvertibleToInt() const { return (tag & ConvertibleToInt) == ConvertibleToInt; } + inline bool isInt32() { + if (tag == _Integer_Type) + return true; + if (isDouble()) { + int i = (int)dbl; + if (i == dbl) { + int_32 = i; + tag = _Integer_Type; + return true; + } + } + return false; + } + + bool booleanValue() const { + return int_32; + } + double doubleValue() const { + return dbl; + } + void setDouble(double d) { + dbl = d; + } + double asDouble() const { + if (tag == _Integer_Type) + return int_32; + return dbl; + } + int integerValue() const { + return int_32; + } + +#if QT_POINTER_SIZE == 8 + String *stringValue() const { + return (String *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } + Object *objectValue() const { + return (Object *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } + Managed *managed() const { + return (Managed *)(val & ~(quint64(Type_Mask) << Tag_Shift)); + } +#else + String *stringValue() const { + return s; + } + Object *objectValue() const { + return o; + } + Managed *managed() const { + return m; + } +#endif + + quint64 rawValue() const { + return val; + } + + static Value emptyValue(); + static Value undefinedValue(); + static Value nullValue(); + static Value fromBoolean(Bool b); + static Value fromDouble(double d); + static Value fromInt32(int i); + static Value fromUInt32(uint i); + static Value fromString(String *s); + static Value fromObject(Object *o); + +#ifndef QMLJS_LLVM_RUNTIME + static Value fromString(ExecutionContext *ctx, const QString &fromString); + static Value fromString(ExecutionEngine *engine, const QString &s); +#endif + + static double toInteger(double fromNumber); + static int toInt32(double value); + static unsigned int toUInt32(double value); + + int toUInt16() const; + int toInt32() const; + unsigned int toUInt32() const; + + bool toBoolean() const; + double toInteger() const; + double toNumber() const; + QString toQString() const; + String *toString(ExecutionContext *ctx) const; + Object *toObject(ExecutionContext *ctx) const; + + inline bool isPrimitive() const { return !isObject(); } +#if QT_POINTER_SIZE == 8 + inline bool integerCompatible() const { + const quint64 mask = quint64(ConvertibleToInt) << 32; + return (val & mask) == mask; + } + static inline bool integerCompatible(Value a, Value b) { + const quint64 mask = quint64(ConvertibleToInt) << 32; + return ((a.val & b.val) & mask) == mask; + } + static inline bool bothDouble(Value a, Value b) { + const quint64 mask = quint64(NotDouble_Mask) << 32; + return ((a.val | b.val) & mask) != mask; + } +#else + inline bool integerCompatible() const { + return (tag & ConvertibleToInt) == ConvertibleToInt; + } + static inline bool integerCompatible(Value a, Value b) { + return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt; + } + static inline bool bothDouble(Value a, Value b) { + return ((a.tag | b.tag) & NotDouble_Mask) != NotDouble_Mask; + } +#endif + inline bool tryIntegerConversion() { + bool b = isConvertibleToInt(); + if (b) + tag = _Integer_Type; + return b; + } + + String *asString() const; + Managed *asManaged() const; + Object *asObject() const; + FunctionObject *asFunctionObject() const; + BooleanObject *asBooleanObject() const; + NumberObject *asNumberObject() const; + StringObject *asStringObject() const; + DateObject *asDateObject() const; + ArrayObject *asArrayObject() const; + ErrorObject *asErrorObject() const; + + template<typename T> inline T *as() const; + + uint asArrayIndex() const; + uint asArrayLength(bool *ok) const; + + Value property(ExecutionContext *ctx, String *name) const; + + inline ExecutionEngine *engine() const; + + // Section 9.12 + bool sameValue(Value other) const; + + inline void mark() const; +}; + +} + +QT_END_NAMESPACE + +#endif // QV4VALUE_DEF_P_H diff --git a/src/qml/qml/v4/qv4value_p.h b/src/qml/qml/v4/qv4value_p.h new file mode 100644 index 0000000000..6d3f0e6edc --- /dev/null +++ b/src/qml/qml/v4/qv4value_p.h @@ -0,0 +1,426 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJS_VALUE_H +#define QMLJS_VALUE_H + +#include <cmath> // this HAS to come + +#include <QtCore/QString> +#include <QtCore/qnumeric.h> +#include "qv4global_p.h" +#include "qv4string_p.h" +#include <QtCore/QDebug> +#include "qv4managed_p.h" + +//#include <wtf/MathExtras.h> + +#include "qv4value_def_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +double __qmljs_to_number(const QV4::Value &value); +Q_QML_EXPORT QV4::String *__qmljs_convert_to_string(QV4::ExecutionContext *ctx, const QV4::Value &value); +QV4::Object *__qmljs_convert_to_object(QV4::ExecutionContext *ctx, const QV4::Value &value); + + +inline ExecutionEngine *Value::engine() const { + Managed *m = asManaged(); + return m ? m->engine() : 0; +} + +inline void Value::mark() const { + Managed *m = asManaged(); + if (m) + m->mark(); +} + +inline Value Value::undefinedValue() +{ + Value v; +#if QT_POINTER_SIZE == 8 + v.val = quint64(_Undefined_Type) << Tag_Shift; +#else + v.tag = _Undefined_Type; + v.int_32 = 0; +#endif + return v; +} + +inline Value Value::nullValue() +{ + Value v; +#if QT_POINTER_SIZE == 8 + v.val = quint64(_Null_Type) << Tag_Shift; +#else + v.tag = _Null_Type; + v.int_32 = 0; +#endif + return v; +} + +inline Value Value::emptyValue() +{ + Value v; + v.tag = Value::_Empty_Type; + v.uint_32 = 0; + return v; +} + + +inline Value Value::fromBoolean(Bool b) +{ + Value v; + v.tag = _Boolean_Type; + v.int_32 = (bool)b; + return v; +} + +inline Value Value::fromDouble(double d) +{ + Value v; + v.dbl = d; + return v; +} + +inline Value Value::fromInt32(int i) +{ + Value v; + v.tag = _Integer_Type; + v.int_32 = i; + return v; +} + +inline Value Value::fromUInt32(uint i) +{ + Value v; + if (i < INT_MAX) { + v.tag = _Integer_Type; + v.int_32 = (int)i; + } else { + v.dbl = i; + } + return v; +} + +inline Value Value::fromString(String *s) +{ + Value v; +#if QT_POINTER_SIZE == 8 + v.val = (quint64)s; + v.val |= quint64(_String_Type) << Tag_Shift; +#else + v.tag = _String_Type; + v.s = s; +#endif + return v; +} + +inline Value Value::fromObject(Object *o) +{ + Value v; +#if QT_POINTER_SIZE == 8 + v.val = (quint64)o; + v.val |= quint64(_Object_Type) << Tag_Shift; +#else + v.tag = _Object_Type; + v.o = o; +#endif + return v; +} + +inline bool Value::toBoolean() const +{ + switch (type()) { + case Value::Undefined_Type: + case Value::Null_Type: + return false; + case Value::Boolean_Type: + case Value::Integer_Type: + return (bool)int_32; + case Value::String_Type: + return stringValue()->toQString().length() > 0; + case Value::Object_Type: + return true; + default: // double + if (! doubleValue() || std::isnan(doubleValue())) + return false; + return true; + } +} + +inline Object *Value::toObject(ExecutionContext *ctx) const +{ + if (isObject()) + return objectValue(); + return __qmljs_convert_to_object(ctx, *this); +} + +inline int Value::toInt32() const +{ + if (isConvertibleToInt()) + return int_32; + double d; + if (isDouble()) + d = dbl; + else + d = __qmljs_to_number(*this); + + const double D32 = 4294967296.0; + const double D31 = D32 / 2.0; + + if ((d >= -D31 && d < D31)) + return static_cast<int>(d); + + return Value::toInt32(__qmljs_to_number(*this)); +} + +inline unsigned int Value::toUInt32() const +{ + if (isConvertibleToInt()) + return (unsigned) int_32; + double d; + if (isDouble()) + d = dbl; + else + d = __qmljs_to_number(*this); + + const double D32 = 4294967296.0; + if (dbl >= 0 && dbl < D32) + return static_cast<uint>(dbl); + return toUInt32(d); +} + +inline uint Value::asArrayIndex() const +{ + if (isInteger() && int_32 >= 0) + return (uint)int_32; + if (!isDouble()) + return UINT_MAX; + uint idx = (uint)dbl; + if (idx != dbl) + return UINT_MAX; + return idx; +} + +inline uint Value::asArrayLength(bool *ok) const +{ + *ok = true; + if (isConvertibleToInt() && int_32 >= 0) + return (uint)int_32; + if (isDouble()) { + uint idx = (uint)dbl; + if ((double)idx != dbl) { + *ok = false; + return UINT_MAX; + } + return idx; + } + if (isString()) + return stringValue()->toUInt(ok); + + uint idx = toUInt32(); + double d = toNumber(); + if (d != idx) { + *ok = false; + return UINT_MAX; + } + return idx; +} + +inline String *Value::asString() const +{ + if (isString()) + return stringValue(); + return 0; +} + +inline Managed *Value::asManaged() const +{ + if (isObject() || isString()) + return managed(); + return 0; +} + +inline Object *Value::asObject() const +{ + return isObject() ? objectValue() : 0; +} + +inline FunctionObject *Value::asFunctionObject() const +{ + return isObject() ? managed()->asFunctionObject() : 0; +} + +inline BooleanObject *Value::asBooleanObject() const +{ + return isObject() ? managed()->asBooleanObject() : 0; +} + +inline NumberObject *Value::asNumberObject() const +{ + return isObject() ? managed()->asNumberObject() : 0; +} + +inline StringObject *Value::asStringObject() const +{ + return isObject() ? managed()->asStringObject() : 0; +} + +inline DateObject *Value::asDateObject() const +{ + return isObject() ? managed()->asDateObject() : 0; +} + +inline ArrayObject *Value::asArrayObject() const +{ + return isObject() ? managed()->asArrayObject() : 0; +} + +inline ErrorObject *Value::asErrorObject() const +{ + return isObject() ? managed()->asErrorObject() : 0; +} + +// ### +inline Value Managed::construct(Value *args, int argc) { + return vtbl->construct(this, args, argc); +} +inline Value Managed::call(const Value &thisObject, Value *args, int argc) { + return vtbl->call(this, thisObject, args, argc); +} + +struct PersistentValuePrivate +{ + PersistentValuePrivate(const Value &v, ExecutionEngine *engine = 0, bool weak = false); + virtual ~PersistentValuePrivate(); + Value value; + uint refcount; + QV4::ExecutionEngine *engine; + PersistentValuePrivate **prev; + PersistentValuePrivate *next; + + void removeFromList(); + void ref() { ++refcount; } + void deref(); + PersistentValuePrivate *detach(const QV4::Value &value, bool weak = false); + + bool checkEngine(QV4::ExecutionEngine *otherEngine) { + if (!engine) { + Q_ASSERT(!value.isObject()); + engine = otherEngine; + } + return (engine == otherEngine); + } +}; + +class Q_QML_EXPORT PersistentValue +{ +public: + PersistentValue() : d(0) {} + PersistentValue(const Value &val); + PersistentValue(const PersistentValue &other); + PersistentValue &operator=(const PersistentValue &other); + PersistentValue &operator=(const Value &other); + ~PersistentValue(); + + Value value() const { + return d ? d->value : Value::emptyValue(); + } + + ExecutionEngine *engine() { + if (!d) + return 0; + Managed *m = d->value.asManaged(); + return m ? m->engine() : 0; + } + + operator Value() const { return value(); } + + bool isEmpty() const { return !d || d->value.isEmpty(); } + void clear() { + *this = PersistentValue(); + } + +private: + PersistentValuePrivate *d; +}; + +class Q_QML_EXPORT WeakValue +{ +public: + WeakValue() : d(0) {} + WeakValue(const Value &val); + WeakValue(const WeakValue &other); + WeakValue &operator=(const WeakValue &other); + WeakValue &operator=(const Value &other); + ~WeakValue(); + + Value value() const { + return d ? d->value : Value::emptyValue(); + } + + ExecutionEngine *engine() { + if (!d) + return 0; + Managed *m = d->value.asManaged(); + return m ? m->engine() : 0; + } + + operator Value() const { return value(); } + + bool isEmpty() const { return !d || d->value.isEmpty(); } + void clear() { + *this = WeakValue(); + } + + void markOnce(); + +private: + PersistentValuePrivate *d; +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v4/qv4variantobject.cpp b/src/qml/qml/v4/qv4variantobject.cpp new file mode 100644 index 0000000000..f18c5b582e --- /dev/null +++ b/src/qml/qml/v4/qv4variantobject.cpp @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4variantobject_p.h" +#include "qv4functionobject_p.h" +#include "qv4objectproto_p.h" +#include <private/qqmlvaluetypewrapper_p.h> +#include <private/qv8engine_p.h> + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +DEFINE_MANAGED_VTABLE(VariantObject); + +VariantObject::VariantObject(ExecutionEngine *engine, const QVariant &value) + : Object(engine) + , ExecutionEngine::ScarceResourceData(value) + , m_vmePropertyReferenceCount(0) +{ + vtbl = &static_vtbl; + prototype = engine->variantPrototype; + if (isScarce()) + internalClass->engine->scarceResources.insert(this); +} + +QVariant VariantObject::toVariant(const QV4::Value &v) +{ + if (Object *o = v.asObject()) + return o->engine()->v8Engine->variantFromJS(v); + + if (v.isString()) + return QVariant(v.stringValue()->toQString()); + if (v.isBoolean()) + return QVariant(v.booleanValue()); + if (v.isNumber()) { + QV4::Value val = v; + if (val.isInt32()) + return QVariant(val.integerValue()); + return QVariant(v.asDouble()); + } + if (v.isNull()) + return QVariant(QMetaType::VoidStar, 0); + assert (v.isUndefined() || v.isEmpty()); + return QVariant(); +} + +bool VariantObject::isScarce() const +{ + QVariant::Type t = data.type(); + return t == QVariant::Pixmap || t == QVariant::Image; +} + +void VariantObject::destroy(Managed *that) +{ + VariantObject *v = static_cast<VariantObject *>(that); + if (v->isScarce()) + v->node.remove(); + v->~VariantObject(); +} + +bool VariantObject::isEqualTo(Managed *m, Managed *other) +{ + QV4::VariantObject *lv = m->as<QV4::VariantObject>(); + assert(lv); + + if (QV4::VariantObject *rv = other->as<QV4::VariantObject>()) + return lv->data == rv->data; + + if (QV4::QmlValueTypeWrapper *v = other->as<QmlValueTypeWrapper>()) + return v->isEqual(lv->data); + + return false; +} + +void VariantObject::addVmePropertyReference() +{ + if (isScarce() && ++m_vmePropertyReferenceCount == 1) { + // remove from the ep->scarceResources list + // since it is now no longer eligible to be + // released automatically by the engine. + node.remove(); + } +} + +void VariantObject::removeVmePropertyReference() +{ + if (isScarce() && --m_vmePropertyReferenceCount == 0) { + // and add to the ep->scarceResources list + // since it is now eligible to be released + // automatically by the engine. + internalClass->engine->scarceResources.insert(this); + } +} + + +VariantPrototype::VariantPrototype(ExecutionEngine *engine) + : VariantObject(engine, QVariant()) +{ + prototype = engine->objectPrototype; +} + +void VariantPrototype::init(ExecutionEngine *engine) +{ + defineDefaultProperty(engine, QStringLiteral("preserve"), method_preserve, 0); + defineDefaultProperty(engine, QStringLiteral("destroy"), method_destroy, 0); + defineDefaultProperty(engine, QStringLiteral("valueOf"), method_valueOf, 0); + defineDefaultProperty(engine, QStringLiteral("toString"), method_toString, 0); +} + +QV4::Value VariantPrototype::method_preserve(SimpleCallContext *ctx) +{ + VariantObject *o = ctx->thisObject.as<QV4::VariantObject>(); + if (o && o->isScarce()) + o->node.remove(); + return Value::undefinedValue(); +} + +QV4::Value VariantPrototype::method_destroy(SimpleCallContext *ctx) +{ + VariantObject *o = ctx->thisObject.as<QV4::VariantObject>(); + if (o) { + if (o->isScarce()) + o->node.remove(); + o->data = QVariant(); + } + return QV4::Value::undefinedValue(); +} + +QV4::Value VariantPrototype::method_toString(SimpleCallContext *ctx) +{ + VariantObject *o = ctx->thisObject.as<QV4::VariantObject>(); + if (!o) + return Value::undefinedValue(); + QString result = o->data.toString(); + if (result.isEmpty() && !o->data.canConvert(QVariant::String)) + result = QString::fromLatin1("QVariant(%0)").arg(QString::fromLatin1(o->data.typeName())); + return Value::fromString(ctx->engine->newString(result)); +} + +QV4::Value VariantPrototype::method_valueOf(SimpleCallContext *ctx) +{ + VariantObject *o = ctx->thisObject.as<QV4::VariantObject>(); + if (o) { + QVariant v = o->data; + switch (v.type()) { + case QVariant::Invalid: + return Value::undefinedValue(); + case QVariant::String: + return Value::fromString(ctx->engine->newString(v.toString())); + case QVariant::Int: + return Value::fromInt32(v.toInt()); + case QVariant::Double: + case QVariant::UInt: + return Value::fromDouble(v.toDouble()); + case QVariant::Bool: + return Value::fromBoolean(v.toBool()); + default: + break; + } + } + return ctx->thisObject; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4variantobject_p.h b/src/qml/qml/v4/qv4variantobject_p.h new file mode 100644 index 0000000000..876539aae1 --- /dev/null +++ b/src/qml/qml/v4/qv4variantobject_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4VARIANTOBJECT_P_H +#define QV4VARIANTOBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtQml/qqmllist.h> +#include <QtCore/qvariant.h> + +#include <private/qv4value_p.h> +#include <private/qv4object_p.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct VariantObject : Object, public ExecutionEngine::ScarceResourceData +{ + Q_MANAGED +public: + VariantObject(ExecutionEngine *engine, const QVariant &value); + + static QVariant toVariant(const QV4::Value &v); + + void addVmePropertyReference(); + void removeVmePropertyReference(); + bool isScarce() const; + int m_vmePropertyReferenceCount; + + static void destroy(Managed *that); + static bool isEqualTo(Managed *m, Managed *other); +}; + +struct VariantPrototype : VariantObject +{ +public: + VariantPrototype(ExecutionEngine *engine); + + void init(ExecutionEngine *engine); + + static Value method_preserve(SimpleCallContext *ctx); + static Value method_destroy(SimpleCallContext *ctx); + static Value method_toString(SimpleCallContext *ctx); + static Value method_valueOf(SimpleCallContext *ctx); +}; + +} + +QT_END_NAMESPACE + +#endif + diff --git a/src/qml/qml/v4/v4.pri b/src/qml/qml/v4/v4.pri index b6784851d8..1c63b4962a 100644 --- a/src/qml/qml/v4/v4.pri +++ b/src/qml/qml/v4/v4.pri @@ -1,15 +1,198 @@ +include(llvm_installation.pri) +include(../../../3rdparty/masm/masm-defs.pri) + +CONFIG += exceptions + +!llvm: DEFINES += QMLJS_NO_LLVM + +CONFIG += warn_off + +INCLUDEPATH += $$PWD +INCLUDEPATH += $$OUT_PWD + +SOURCES += \ + $$PWD/qv4codegen.cpp \ + $$PWD/qv4ssa.cpp \ + $$PWD/qv4jsir.cpp \ + $$PWD/qv4engine.cpp \ + $$PWD/qv4context.cpp \ + $$PWD/qv4runtime.cpp \ + $$PWD/qv4value.cpp \ + $$PWD/qv4syntaxchecker.cpp \ + $$PWD/qv4isel_masm.cpp \ + $$PWD/llvm_runtime.cpp \ + $$PWD/qv4isel_p.cpp \ + $$PWD/qv4debugging.cpp \ + $$PWD/qv4lookup.cpp \ + $$PWD/qv4identifier.cpp \ + $$PWD/qv4identifiertable.cpp \ + $$PWD/qv4mm.cpp \ + $$PWD/qv4managed.cpp \ + $$PWD/qv4internalclass.cpp \ + $$PWD/qv4sparsearray.cpp \ + $$PWD/qv4arrayobject.cpp \ + $$PWD/qv4argumentsobject.cpp \ + $$PWD/qv4booleanobject.cpp \ + $$PWD/qv4dateobject.cpp \ + $$PWD/qv4errorobject.cpp \ + $$PWD/qv4function.cpp \ + $$PWD/qv4functionobject.cpp \ + $$PWD/qv4globalobject.cpp \ + $$PWD/qv4jsonobject.cpp \ + $$PWD/qv4mathobject.cpp \ + $$PWD/qv4numberobject.cpp \ + $$PWD/qv4object.cpp \ + $$PWD/qv4objectproto.cpp \ + $$PWD/qv4regexpobject.cpp \ + $$PWD/qv4stringobject.cpp \ + $$PWD/qv4variantobject.cpp \ + $$PWD/qv4string.cpp \ + $$PWD/qv4objectiterator.cpp \ + $$PWD/qv4regexp.cpp \ + $$PWD/qv4unwindhelper.cpp \ + $$PWD/qv4serialize.cpp \ + $$PWD/qv4script.cpp \ + $$PWD/qv4executableallocator.cpp \ + $$PWD/qv4sequenceobject.cpp \ + $$PWD/qv4include.cpp \ + $$PWD/qv4qobjectwrapper.cpp \ + $$PWD/qv4qmlextensions.cpp \ + $$PWD/qv4stacktrace.cpp \ + $$PWD/qv4exception.cpp + HEADERS += \ - $$PWD/qv4compiler_p.h \ - $$PWD/qv4compiler_p_p.h \ - $$PWD/qv4ir_p.h \ - $$PWD/qv4irbuilder_p.h \ - $$PWD/qv4instruction_p.h \ - $$PWD/qv4bindings_p.h \ - $$PWD/qv4program_p.h \ + $$PWD/qv4global_p.h \ + $$PWD/qv4codegen_p.h \ + $$PWD/qv4ssa_p.h \ + $$PWD/qv4jsir_p.h \ + $$PWD/qv4engine_p.h \ + $$PWD/qv4context_p.h \ + $$PWD/qv4runtime_p.h \ + $$PWD/qv4math_p.h \ + $$PWD/qv4value_p.h \ + $$PWD/qv4value_def_p.h \ + $$PWD/qv4syntaxchecker_p.h \ + $$PWD/qv4isel_masm_p.h \ + $$PWD/qv4isel_p.h \ + $$PWD/qv4isel_util_p.h \ + $$PWD/qv4debugging_p.h \ + $$PWD/qv4lookup_p.h \ + $$PWD/qv4identifier_p.h \ + $$PWD/qv4identifiertable_p.h \ + $$PWD/qv4mm_p.h \ + $$PWD/qv4managed_p.h \ + $$PWD/qv4internalclass_p.h \ + $$PWD/qv4sparsearray_p.h \ + $$PWD/qv4arrayobject_p.h \ + $$PWD/qv4argumentsobject_p.h \ + $$PWD/qv4booleanobject_p.h \ + $$PWD/qv4dateobject_p.h \ + $$PWD/qv4errorobject_p.h \ + $$PWD/qv4function_p.h \ + $$PWD/qv4functionobject_p.h \ + $$PWD/qv4globalobject_p.h \ + $$PWD/qv4jsonobject_p.h \ + $$PWD/qv4mathobject_p.h \ + $$PWD/qv4numberobject_p.h \ + $$PWD/qv4object_p.h \ + $$PWD/qv4objectproto_p.h \ + $$PWD/qv4regexpobject_p.h \ + $$PWD/qv4stringobject_p.h \ + $$PWD/qv4variantobject_p.h \ + $$PWD/qv4string_p.h \ + $$PWD/qv4property_p.h \ + $$PWD/qv4objectiterator_p.h \ + $$PWD/qv4regexp_p.h \ + $$PWD/qv4unwindhelper_p.h \ + $$PWD/qv4unwindhelper_p-dw2.h \ + $$PWD/qv4unwindhelper_p-arm.h \ + $$PWD/qv4serialize_p.h \ + $$PWD/qv4script_p.h \ + $$PWD/qv4util_p.h \ + $$PWD/qv4executableallocator_p.h \ + $$PWD/qv4sequenceobject_p.h \ + $$PWD/qv4include_p.h \ + $$PWD/qv4qobjectwrapper_p.h \ + $$PWD/qv4qmlextensions_p.h \ + $$PWD/qv4stacktrace_p.h \ + $$PWD/qv4exception_p.h + +llvm-libs { SOURCES += \ - $$PWD/qv4compiler.cpp \ - $$PWD/qv4ir.cpp \ - $$PWD/qv4irbuilder.cpp \ - $$PWD/qv4instruction.cpp \ - $$PWD/qv4bindings.cpp \ + $$PWD/qv4isel_llvm.cpp + +HEADERS += \ + $$PWD/qv4isel_llvm_p.h \ + $$PWD/qv4_llvm_p.h + +LLVM_RUNTIME_BC = $$PWD/llvm_runtime.bc +DEFINES += LLVM_RUNTIME="\"\\\"$$LLVM_RUNTIME_BC\\\"\"" +DEFINES += QMLJS_WITH_LLVM + +INCLUDEPATH += \ + $$system($$LLVM_CONFIG --includedir) + +QMAKE_CXXFLAGS += $$system($$LLVM_CONFIG --cppflags) -fvisibility-inlines-hidden +QMAKE_CXXFLAGS -= -pedantic +QMAKE_CXXFLAGS -= -Wcovered-switch-default + +LIBS += \ + $$system($$LLVM_CONFIG --ldflags) \ + $$system($$LLVM_CONFIG --libs core jit bitreader linker ipo target x86 arm native) + +QMAKE_EXTRA_TARGETS += gen_llvm_runtime + +GEN_LLVM_RUNTIME_FLAGS = $$system($$LLVM_CONFIG --cppflags) +GEN_LLVM_RUNTIME_FLAGS -= -pedantic + +gen_llvm_runtime.target = llvm_runtime +gen_llvm_runtime.commands = clang -O2 -emit-llvm -c -I$$PWD -I$$PWD/../3rdparty/masm $$join(QT.core.includes, " -I", "-I") $$GEN_LLVM_RUNTIME_FLAGS -DQMLJS_LLVM_RUNTIME llvm_runtime.cpp -o $$LLVM_RUNTIME_BC +} + +# Use SSE2 floating point math on 32 bit instead of the default +# 387 to make test results pass on 32 and on 64 bit builds. +linux-g++*:isEqual(QT_ARCH,i386) { + QMAKE_CFLAGS += -march=pentium4 -msse2 -mfpmath=sse + QMAKE_CXXFLAGS += -march=pentium4 -msse2 -mfpmath=sse +} + +linux*|mac { + LIBS += -ldl +} + +# Only on Android/ARM at the moment, because only there we have issues +# replacing __gnu_Unwind_Find_exidx with our own implementation, +# and thus require static libgcc linkage. +android:equals(QT_ARCH, "arm"):*g++* { + static_libgcc = $$system($$QMAKE_CXX -print-file-name=libgcc.a) + LIBS += $$static_libgcc + SOURCES += $$PWD/qv4exception_gcc.cpp + DEFINES += V4_CXX_ABI_EXCEPTION +} + +debug-with-libunwind { + UW_INC=$$(LIBUNWIND_INCLUDES) + isEmpty(UW_INC): error("Please set LIBUNWIND_INCLUDES") + INCLUDEPATH += $$UW_INC + UW_LIBS=$$(LIBUNWIND_LIBS) + isEmpty(UW_LIBS): error("Please set LIBUNWIND_LIBS") + LIBS += -L$$UW_LIBS + equals(QT_ARCH, arm): LIBS += -lunwind-arm + LIBS += -lunwind-dwarf-common -lunwind-dwarf-local -lunwind-elf32 -lunwind + DEFINES += WTF_USE_LIBUNWIND_DEBUG=1 +} + +valgrind { + DEFINES += V4_USE_VALGRIND +} + +ios: DEFINES += ENABLE_ASSEMBLER_WX_EXCLUSIVE=1 + +win32 { + LIBS_PRIVATE += -lDbgHelp +} + +include(moth/moth.pri) +include(../../../3rdparty/masm/masm.pri) +include(../../../3rdparty/double-conversion/double-conversion.pri) |