diff options
Diffstat (limited to 'src/qml/jsruntime')
92 files changed, 27897 insertions, 0 deletions
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri new file mode 100644 index 0000000000..a8f5f11a50 --- /dev/null +++ b/src/qml/jsruntime/jsruntime.pri @@ -0,0 +1,144 @@ +CONFIG += exceptions + +CONFIG += warn_off + +INCLUDEPATH += $$PWD +INCLUDEPATH += $$OUT_PWD + +SOURCES += \ + $$PWD/qv4engine.cpp \ + $$PWD/qv4context.cpp \ + $$PWD/qv4runtime.cpp \ + $$PWD/qv4value.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/qv4global_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/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 + +# 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(../../3rdparty/double-conversion/double-conversion.pri) diff --git a/src/qml/jsruntime/qv4alloca_p.h b/src/qml/jsruntime/qv4alloca_p.h new file mode 100644 index 0000000000..e4580da3d8 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp new file mode 100644 index 0000000000..6247ef1504 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h new file mode 100644 index 0000000000..3d760b8937 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp new file mode 100644 index 0000000000..b1f25177dd --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h new file mode 100644 index 0000000000..13c3882f4f --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp new file mode 100644 index 0000000000..24678d23dc --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h new file mode 100644 index 0000000000..3e5e7663f2 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp new file mode 100644 index 0000000000..ce947e51e8 --- /dev/null +++ b/src/qml/jsruntime/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(&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/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h new file mode 100644 index 0000000000..dfe02bdcc8 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp new file mode 100644 index 0000000000..3cf6cb1aeb --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h new file mode 100644 index 0000000000..4e833e143f --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4debugging.cpp b/src/qml/jsruntime/qv4debugging.cpp new file mode 100644 index 0000000000..1b182eac89 --- /dev/null +++ b/src/qml/jsruntime/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 "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/jsruntime/qv4debugging_p.h b/src/qml/jsruntime/qv4debugging_p.h new file mode 100644 index 0000000000..d71b25f378 --- /dev/null +++ b/src/qml/jsruntime/qv4debugging_p.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** 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 <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/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp new file mode 100644 index 0000000000..0b7c850aa6 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h new file mode 100644 index 0000000000..a6bf2ef38e --- /dev/null +++ b/src/qml/jsruntime/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 "private/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/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp new file mode 100644 index 0000000000..516a4d37f8 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h new file mode 100644 index 0000000000..d3e0f107bc --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4exception.cpp b/src/qml/jsruntime/qv4exception.cpp new file mode 100644 index 0000000000..9f15c27ffc --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4exception_gcc.cpp b/src/qml/jsruntime/qv4exception_gcc.cpp new file mode 100644 index 0000000000..0324a06e0b --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4exception_p.h b/src/qml/jsruntime/qv4exception_p.h new file mode 100644 index 0000000000..8ba06f57f4 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4executableallocator.cpp b/src/qml/jsruntime/qv4executableallocator.cpp new file mode 100644 index 0000000000..a754663556 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4executableallocator_p.h b/src/qml/jsruntime/qv4executableallocator_p.h new file mode 100644 index 0000000000..2a304baf9c --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp new file mode 100644 index 0000000000..bf633a9b41 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h new file mode 100644 index 0000000000..4dd0533d51 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp new file mode 100644 index 0000000000..ffdd08ed0a --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h new file mode 100644 index 0000000000..8465142616 --- /dev/null +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** 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 "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/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h new file mode 100644 index 0000000000..8e47f3c88a --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp new file mode 100644 index 0000000000..6b279416a3 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4globalobject_p.h b/src/qml/jsruntime/qv4globalobject_p.h new file mode 100644 index 0000000000..11d034b5b4 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp new file mode 100644 index 0000000000..5d8077bfdc --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4identifier_p.h b/src/qml/jsruntime/qv4identifier_p.h new file mode 100644 index 0000000000..7c69e1d8c4 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp new file mode 100644 index 0000000000..5de2f893ef --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h new file mode 100644 index 0000000000..0f9a5921f9 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp new file mode 100644 index 0000000000..4fd7bb14c7 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4include_p.h b/src/qml/jsruntime/qv4include_p.h new file mode 100644 index 0000000000..d6bbcd1a60 --- /dev/null +++ b/src/qml/jsruntime/qv4include_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** 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 QV4INCLUDE_P_H +#define QV4INCLUDE_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/qobject.h> +#include <QtCore/qurl.h> + +#include <private/qqmlcontext_p.h> + +#include <private/qv4value_p.h> +#include <private/qv4context_p.h> + +QT_BEGIN_NAMESPACE + +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 + }; + + static QV4::Value include(QV4::SimpleCallContext *ctx); + +private slots: + void finished(); + +private: + QV4Include(const QUrl &url, QV8Engine *engine, QQmlContextData *context, + const QV4::Value &qmlglobal, const QV4::Value &callback); + ~QV4Include(); + + QV4::Value result(); + + static QV4::Value resultValue(QV4::ExecutionEngine *v4, Status status = Loading); + static void callback(const QV4::Value &callback, const QV4::Value &status); + + 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 + diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp new file mode 100644 index 0000000000..f4edc99545 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h new file mode 100644 index 0000000000..fc6c5352b1 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp new file mode 100644 index 0000000000..782c388e5a --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4jsonobject_p.h b/src/qml/jsruntime/qv4jsonobject_p.h new file mode 100644 index 0000000000..ccd99d5488 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp new file mode 100644 index 0000000000..b5ea877bd4 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h new file mode 100644 index 0000000000..e77552826a --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp new file mode 100644 index 0000000000..19adb354e3 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h new file mode 100644 index 0000000000..5e3c142bb8 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4math_p.h b/src/qml/jsruntime/qv4math_p.h new file mode 100644 index 0000000000..a3a3715545 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp new file mode 100644 index 0000000000..7aa56f51bd --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4mathobject_p.h b/src/qml/jsruntime/qv4mathobject_p.h new file mode 100644 index 0000000000..03c36bcc68 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4mm.cpp b/src/qml/jsruntime/qv4mm.cpp new file mode 100644 index 0000000000..0e53a2088f --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4mm_p.h b/src/qml/jsruntime/qv4mm_p.h new file mode 100644 index 0000000000..f72d23dc9a --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp new file mode 100644 index 0000000000..266fa792dc --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h new file mode 100644 index 0000000000..0c06451c98 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp new file mode 100644 index 0000000000..edfb535e7a --- /dev/null +++ b/src/qml/jsruntime/qv4object.cpp @@ -0,0 +1,1407 @@ +/**************************************************************************** +** +** 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 *, BinOp op, String *name, const Value &rhs) +{ + Value v = get(name); + Value result; + op(&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(&result, v, rhs); + putIndexed(idx, result); + return; + } + String *name = index.toString(ctx); + assert(name); + inplaceBinOp(ctx, op, name, rhs); +} + +void Object::inplaceBinOp(ExecutionContext *ctx, BinOpContext 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, BinOpContext 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/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h new file mode 100644 index 0000000000..a9f947b629 --- /dev/null +++ b/src/qml/jsruntime/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 "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 *, BinOp op, String *name, const Value &rhs); + void inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value &index, const Value &rhs); + void inplaceBinOp(ExecutionContext *ctx, BinOpContext op, String *name, const Value &rhs); + void inplaceBinOp(ExecutionContext *ctx, BinOpContext 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/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp new file mode 100644 index 0000000000..a89bfdb797 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h new file mode 100644 index 0000000000..95439397f5 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp new file mode 100644 index 0000000000..462c9ca81e --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h new file mode 100644 index 0000000000..ca2e77ca42 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4property_p.h b/src/qml/jsruntime/qv4property_p.h new file mode 100644 index 0000000000..024ad3c720 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4qmlextensions.cpp b/src/qml/jsruntime/qv4qmlextensions.cpp new file mode 100644 index 0000000000..a55330ff67 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4qmlextensions_p.h b/src/qml/jsruntime/qv4qmlextensions_p.h new file mode 100644 index 0000000000..cf9e287efe --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp new file mode 100644 index 0000000000..f79675845b --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h new file mode 100644 index 0000000000..6580d19fe9 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp new file mode 100644 index 0000000000..ab01ce7650 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h new file mode 100644 index 0000000000..6edbd4b2ad --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp new file mode 100644 index 0000000000..0dc14e5722 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h new file mode 100644 index 0000000000..0b9b7122a9 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp new file mode 100644 index 0000000000..ed2c1468ee --- /dev/null +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -0,0 +1,1250 @@ +/**************************************************************************** +** +** 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) +{ + Value lhs = ctx->getProperty(name); + Value result; + __qmljs_add(ctx, &result, lhs, value); + ctx->setProperty(name, result); +} + +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_helper(const Value &x, const Value &y) +{ + Q_ASSERT(x.type() != y.type()); + + 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()) { + double dy = __qmljs_to_number(y); + return x.asDouble() == dy; + } else if (x.isString() && y.isNumber()) { + double dx = __qmljs_to_number(x); + return dx == y.asDouble(); + } else if (x.isBoolean()) { + Value nx = Value::fromDouble((double) x.booleanValue()); + return __qmljs_cmp_eq(nx, y); + } else if (y.isBoolean()) { + Value ny = Value::fromDouble((double) y.booleanValue()); + return __qmljs_cmp_eq(x, ny); + } else if ((x.isNumber() || x.isString()) && y.isObject()) { + Value py = __qmljs_to_primitive(y, PREFERREDTYPE_HINT); + return __qmljs_cmp_eq(x, py); + } else if (x.isObject() && (y.isNumber() || y.isString())) { + Value px = __qmljs_to_primitive(x, PREFERREDTYPE_HINT); + return __qmljs_cmp_eq(px, y); + } + + return false; +} + +Bool __qmljs_strict_equal(const Value &x, const Value &y) +{ + TRACE2(x, y); + + if (x.rawValue() == y.rawValue()) + // NaN != NaN + return (x.tag & QV4::Value::NotDouble_Mask) != QV4::Value::NaN_Mask; + + if (x.isNumber()) + return y.isNumber() && x.asDouble() == y.asDouble(); + if (x.isString()) + return y.isString() && 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/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h new file mode 100644 index 0000000000..836aedf298 --- /dev/null +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -0,0 +1,722 @@ +/**************************************************************************** +** +** 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_helper(const Value &x, const 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::Value *result, const QV4::Value &left, const QV4::Value &right); +typedef void (*BinOpContext)(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_add(ExecutionContext *ctx, QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_bit_or(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_bit_xor(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_bit_and(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_sub(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_mul(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_div(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_mod(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_shl(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_shr(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_ushr(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_gt(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_lt(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_ge(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_le(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_eq(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_ne(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_se(QV4::Value *result, const QV4::Value &left, const QV4::Value &right); +void __qmljs_sne(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)(const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_gt(const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_lt(const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_ge(const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_le(const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_eq(const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_ne(const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_se(const QV4::Value &left, const QV4::Value &right); +QV4::Bool __qmljs_cmp_sne(const QV4::Value &left, const QV4::Value &right); + +typedef QV4::Bool (*CmpOpContext)(QV4::ExecutionContext *ctx, 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::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::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::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::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::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::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::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::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::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::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::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(__qmljs_cmp_gt(left, right)); +} + +inline void __qmljs_lt(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(__qmljs_cmp_lt(left, right)); +} + +inline void __qmljs_ge(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(__qmljs_cmp_ge(left, right)); +} + +inline void __qmljs_le(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(__qmljs_cmp_le(left, right)); +} + +inline void __qmljs_eq(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(__qmljs_cmp_eq(left, right)); +} + +inline void __qmljs_ne(QV4::Value *result, const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + *result = QV4::Value::fromBoolean(!__qmljs_cmp_eq(left, right)); +} + +inline void __qmljs_se(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::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(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(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(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(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(const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + if (left.val == right.val) + // NaN != NaN + return (left.tag & QV4::Value::NotDouble_Mask) != QV4::Value::NaN_Mask; + + if (left.type() == right.type()) { + if (left.isManaged()) + return left.managed()->isEqualTo(right.managed()); + return false; + } + + return __qmljs_equal_helper(left, right); +} + +inline QV4::Bool __qmljs_cmp_ne(const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + return !__qmljs_cmp_eq(left, right); +} + +inline QV4::Bool __qmljs_cmp_se(const QV4::Value &left, const QV4::Value &right) +{ + TRACE2(left, right); + + return __qmljs_strict_equal(left, right); +} + +inline QV4::Bool __qmljs_cmp_sne(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/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp new file mode 100644 index 0000000000..3de218a451 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h new file mode 100644 index 0000000000..274a87db26 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp new file mode 100644 index 0000000000..c4d9a71519 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h new file mode 100644 index 0000000000..2cade45092 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp new file mode 100644 index 0000000000..f7389dc6d7 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4serialize_p.h b/src/qml/jsruntime/qv4serialize_p.h new file mode 100644 index 0000000000..5a04c9d25f --- /dev/null +++ b/src/qml/jsruntime/qv4serialize_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** 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 QV4SERIALIZE_P_H +#define QV4SERIALIZE_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/qbytearray.h> +#include <private/qv4value_p.h> + +QT_BEGIN_NAMESPACE + +class QV8Engine; + +namespace QV4 { + +class Serialize { +public: + + static QByteArray serialize(const Value &, QV8Engine *); + static Value deserialize(const QByteArray &, QV8Engine *); + +private: + static void serialize(QByteArray &, const Value &, QV8Engine *); + static Value deserialize(const char *&, QV8Engine *); +}; + +} + +QT_END_NAMESPACE + +#endif // QV8WORKER_P_H diff --git a/src/qml/jsruntime/qv4sparsearray.cpp b/src/qml/jsruntime/qv4sparsearray.cpp new file mode 100644 index 0000000000..835a0d004f --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4sparsearray_p.h b/src/qml/jsruntime/qv4sparsearray_p.h new file mode 100644 index 0000000000..384d2ef045 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4stacktrace.cpp b/src/qml/jsruntime/qv4stacktrace.cpp new file mode 100644 index 0000000000..1cc2e53556 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4stacktrace_p.h b/src/qml/jsruntime/qv4stacktrace_p.h new file mode 100644 index 0000000000..79cb4d1813 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp new file mode 100644 index 0000000000..8b78c40129 --- /dev/null +++ b/src/qml/jsruntime/qv4string.cpp @@ -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$ +** +****************************************************************************/ + +#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*/, + 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; +} + +bool String::isEqualTo(Managed *t, Managed *o) +{ + if (t == o) + return true; + String *that = static_cast<String *>(t); + String *other = static_cast<String *>(o); + if (that->hashValue() != other->hashValue()) + return false; + if (that->identifier && that->identifier == other->identifier) + return true; + if (that->subtype >= StringType_UInt && that->subtype == other->subtype) + return true; + + return that->toQString() == other->toQString(); +} + + +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/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h new file mode 100644 index 0000000000..31e5c2a5f7 --- /dev/null +++ b/src/qml/jsruntime/qv4string_p.h @@ -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$ +** +****************************************************************************/ +#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 bool isEqualTo(Managed *that, Managed *o); + + static const ManagedVTable static_vtbl; +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp new file mode 100644 index 0000000000..5afedd3d4f --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h new file mode 100644 index 0000000000..0ef6596235 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4unwindhelper.cpp b/src/qml/jsruntime/qv4unwindhelper.cpp new file mode 100644 index 0000000000..beb5132626 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4unwindhelper_p-arm.h b/src/qml/jsruntime/qv4unwindhelper_p-arm.h new file mode 100644 index 0000000000..dd1f1e4856 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4unwindhelper_p-dw2.h b/src/qml/jsruntime/qv4unwindhelper_p-dw2.h new file mode 100644 index 0000000000..57615f0999 --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4unwindhelper_p.h b/src/qml/jsruntime/qv4unwindhelper_p.h new file mode 100644 index 0000000000..9ef564449a --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4util_p.h b/src/qml/jsruntime/qv4util_p.h new file mode 100644 index 0000000000..dbd9f89faa --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp new file mode 100644 index 0000000000..a41262f12f --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4value_def_p.h b/src/qml/jsruntime/qv4value_def_p.h new file mode 100644 index 0000000000..a44af16b6a --- /dev/null +++ b/src/qml/jsruntime/qv4value_def_p.h @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** 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 { + NaN_Mask = 0x7ff80000, + NotDouble_Mask = 0x7ffc0000, + Type_Mask = 0xffff8000, + Immediate_Mask = NotDouble_Mask | 0x00008000, + IsManaged_Mask = Type_Mask & ~0x10000, + 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 isManaged() const { return (tag & IsManaged_Mask) == Object_Type; } + 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/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h new file mode 100644 index 0000000000..2a783ed34b --- /dev/null +++ b/src/qml/jsruntime/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 (isManaged()) + 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/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp new file mode 100644 index 0000000000..f18c5b582e --- /dev/null +++ b/src/qml/jsruntime/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/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h new file mode 100644 index 0000000000..876539aae1 --- /dev/null +++ b/src/qml/jsruntime/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 + |