diff options
Diffstat (limited to 'src/qml/jsruntime')
159 files changed, 14019 insertions, 11971 deletions
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri deleted file mode 100644 index 32acc6affc..0000000000 --- a/src/qml/jsruntime/jsruntime.pri +++ /dev/null @@ -1,174 +0,0 @@ -INCLUDEPATH += $$PWD -INCLUDEPATH += $$OUT_PWD - -SOURCES += \ - $$PWD/qv4engine.cpp \ - $$PWD/qv4context.cpp \ - $$PWD/qv4persistent.cpp \ - $$PWD/qv4lookup.cpp \ - $$PWD/qv4identifier.cpp \ - $$PWD/qv4identifiertable.cpp \ - $$PWD/qv4managed.cpp \ - $$PWD/qv4internalclass.cpp \ - $$PWD/qv4sparsearray.cpp \ - $$PWD/qv4atomics.cpp \ - $$PWD/qv4arraydata.cpp \ - $$PWD/qv4arrayobject.cpp \ - $$PWD/qv4arrayiterator.cpp \ - $$PWD/qv4argumentsobject.cpp \ - $$PWD/qv4booleanobject.cpp \ - $$PWD/qv4dateobject.cpp \ - $$PWD/qv4errorobject.cpp \ - $$PWD/qv4function.cpp \ - $$PWD/qv4functionobject.cpp \ - $$PWD/qv4generatorobject.cpp \ - $$PWD/qv4globalobject.cpp \ - $$PWD/qv4iterator.cpp \ - $$PWD/qv4jsonobject.cpp \ - $$PWD/qv4mathobject.cpp \ - $$PWD/qv4memberdata.cpp \ - $$PWD/qv4numberobject.cpp \ - $$PWD/qv4object.cpp \ - $$PWD/qv4objectproto.cpp \ - $$PWD/qv4propertykey.cpp \ - $$PWD/qv4proxy.cpp \ - $$PWD/qv4qmlcontext.cpp \ - $$PWD/qv4reflect.cpp \ - $$PWD/qv4regexpobject.cpp \ - $$PWD/qv4stackframe.cpp \ - $$PWD/qv4string.cpp \ - $$PWD/qv4stringiterator.cpp \ - $$PWD/qv4stringobject.cpp \ - $$PWD/qv4variantobject.cpp \ - $$PWD/qv4objectiterator.cpp \ - $$PWD/qv4regexp.cpp \ - $$PWD/qv4runtimecodegen.cpp \ - $$PWD/qv4script.cpp \ - $$PWD/qv4symbol.cpp \ - $$PWD/qv4setobject.cpp \ - $$PWD/qv4setiterator.cpp \ - $$PWD/qv4include.cpp \ - $$PWD/qv4qobjectwrapper.cpp \ - $$PWD/qv4arraybuffer.cpp \ - $$PWD/qv4typedarray.cpp \ - $$PWD/qv4dataview.cpp \ - $$PWD/qv4vme_moth.cpp \ - $$PWD/qv4mapobject.cpp \ - $$PWD/qv4mapiterator.cpp \ - $$PWD/qv4estable.cpp \ - $$PWD/qv4module.cpp \ - $$PWD/qv4promiseobject.cpp \ - $$PWD/qv4runtime.cpp \ - $$PWD/qv4value.cpp \ - $$PWD/qv4compilationunitmapper.cpp \ - $$PWD/qv4executablecompilationunit.cpp \ - $$PWD/qv4executableallocator.cpp - -qtConfig(qml-debug): SOURCES += $$PWD/qv4profiling.cpp - -HEADERS += \ - $$PWD/qv4global_p.h \ - $$PWD/qv4engine_p.h \ - $$PWD/qv4enginebase_p.h \ - $$PWD/qv4context_p.h \ - $$PWD/qv4math_p.h \ - $$PWD/qv4persistent_p.h \ - $$PWD/qv4debugging_p.h \ - $$PWD/qv4lookup_p.h \ - $$PWD/qv4identifier_p.h \ - $$PWD/qv4identifiertable_p.h \ - $$PWD/qv4managed_p.h \ - $$PWD/qv4internalclass_p.h \ - $$PWD/qv4jscall_p.h \ - $$PWD/qv4sparsearray_p.h \ - $$PWD/qv4atomics_p.h \ - $$PWD/qv4arraydata_p.h \ - $$PWD/qv4arrayobject_p.h \ - $$PWD/qv4arrayiterator_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/qv4generatorobject_p.h \ - $$PWD/qv4globalobject_p.h \ - $$PWD/qv4iterator_p.h \ - $$PWD/qv4jsonobject_p.h \ - $$PWD/qv4mathobject_p.h \ - $$PWD/qv4memberdata_p.h \ - $$PWD/qv4numberobject_p.h \ - $$PWD/qv4object_p.h \ - $$PWD/qv4objectproto_p.h \ - $$PWD/qv4propertykey_p.h \ - $$PWD/qv4proxy_p.h \ - $$PWD/qv4qmlcontext_p.h \ - $$PWD/qv4reflect_p.h \ - $$PWD/qv4regexpobject_p.h \ - $$PWD/qv4runtimecodegen_p.h \ - $$PWD/qv4stackframe_p.h \ - $$PWD/qv4string_p.h \ - $$PWD/qv4stringiterator_p.h \ - $$PWD/qv4stringobject_p.h \ - $$PWD/qv4variantobject_p.h \ - $$PWD/qv4property_p.h \ - $$PWD/qv4objectiterator_p.h \ - $$PWD/qv4regexp_p.h \ - $$PWD/qv4script_p.h \ - $$PWD/qv4symbol_p.h \ - $$PWD/qv4setobject_p.h \ - $$PWD/qv4setiterator_p.h \ - $$PWD/qv4scopedvalue_p.h \ - $$PWD/qv4executableallocator_p.h \ - $$PWD/qv4include_p.h \ - $$PWD/qv4qobjectwrapper_p.h \ - $$PWD/qv4profiling_p.h \ - $$PWD/qv4arraybuffer_p.h \ - $$PWD/qv4typedarray_p.h \ - $$PWD/qv4dataview_p.h \ - $$PWD/qv4vme_moth_p.h \ - $$PWD/qv4mapobject_p.h \ - $$PWD/qv4mapiterator_p.h \ - $$PWD/qv4estable_p.h \ - $$PWD/qv4vtable_p.h \ - $$PWD/qv4module_p.h \ - $$PWD/qv4promiseobject_p.h \ - $$PWD/qv4runtime_p.h \ - $$PWD/qv4value_p.h \ - $$PWD/qv4compilationunitmapper_p.h \ - $$PWD/qv4executablecompilationunit_p.h \ - $$PWD/qv4functiontable_p.h \ - $$PWD/qv4runtimeapi_p.h - -qtConfig(qml-sequence-object) { - HEADERS += \ - $$PWD/qv4sequenceobject_p.h - - SOURCES += \ - $$PWD/qv4sequenceobject.cpp -} - -unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp -else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp - -win32 { - !winrt:equals(QT_ARCH, x86_64) { - SOURCES += \ - $$PWD/qv4functiontable_win64.cpp - } else { - SOURCES += \ - $$PWD/qv4functiontable_noop.cpp - } -} else { - SOURCES += \ - $$PWD/qv4functiontable_unix.cpp -} - - -valgrind { - DEFINES += V4_USE_VALGRIND -} - -heaptrack { - DEFINES += V4_USE_HEAPTRACK -} diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 206e2b9aa4..74b79cb400 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -1,57 +1,23 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include <qv4argumentsobject_p.h> -#include <qv4arrayobject_p.h> -#include <qv4scopedvalue_p.h> -#include <qv4string_p.h> -#include <qv4function_p.h> -#include <qv4jscall_p.h> -#include <qv4symbol_p.h> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qv4argumentsobject_p.h" #include <private/qv4alloca_p.h> +#include <private/qv4arrayobject_p.h> +#include <private/qv4function_p.h> +#include <private/qv4jscall_p.h> +#include <private/qv4scopedvalue_p.h> +#include <private/qv4stackframe_p.h> +#include <private/qv4string_p.h> +#include <private/qv4symbol_p.h> using namespace QV4; DEFINE_OBJECT_VTABLE(ArgumentsObject); DEFINE_OBJECT_VTABLE(StrictArgumentsObject); -void Heap::StrictArgumentsObject::init(QV4::CppStackFrame *frame) +void Heap::StrictArgumentsObject::init(QV4::JSTypesStackFrame *frame) { Q_ASSERT(vtable() == QV4::StrictArgumentsObject::staticVTable()); @@ -68,11 +34,11 @@ void Heap::StrictArgumentsObject::init(QV4::CppStackFrame *frame) Scope scope(v4); Scoped<QV4::StrictArgumentsObject> args(scope, this); - args->arrayReserve(frame->originalArgumentsCount); - args->arrayPut(0, frame->originalArguments, frame->originalArgumentsCount); + args->arrayReserve(frame->argc()); + args->arrayPut(0, frame->argv(), frame->argc()); Q_ASSERT(args->internalClass()->verifyIndex(v4->id_length()->propertyKey(), LengthPropertyIndex)); - setProperty(v4, LengthPropertyIndex, Value::fromInt32(frame->originalArgumentsCount)); + setProperty(v4, LengthPropertyIndex, Value::fromInt32(frame->argc())); } void Heap::ArgumentsObject::init(QV4::CppStackFrame *frame) @@ -93,7 +59,7 @@ void Heap::ArgumentsObject::init(QV4::CppStackFrame *frame) setProperty(v4, SymbolIteratorPropertyIndex, *v4->arrayProtoValues()); fullyCreated = false; - argCount = frame->originalArgumentsCount; + argCount = frame->argc(); uint nFormals = frame->v4Function->nFormals; mapped = nFormals > 63 ? std::numeric_limits<quint64>::max() : (1ull << nFormals) - 1; } diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index f0e2192c7e..0487bd22f8 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4ARGUMENTSOBJECTS_H #define QV4ARGUMENTSOBJECTS_H @@ -51,7 +15,6 @@ // #include "qv4object_p.h" -#include "qv4functionobject_p.h" QT_BEGIN_NAMESPACE @@ -66,7 +29,7 @@ namespace Heap { Member(class, NoMark, quint64, mapped) DECLARE_HEAP_OBJECT(ArgumentsObject, Object) { - DECLARE_MARKOBJECTS(ArgumentsObject); + DECLARE_MARKOBJECTS(ArgumentsObject) enum { LengthPropertyIndex = 0, SymbolIteratorPropertyIndex = 1, @@ -84,7 +47,7 @@ DECLARE_HEAP_OBJECT(StrictArgumentsObject, Object) { CalleePropertyIndex = 2, CalleeSetterPropertyIndex = 3 }; - void init(CppStackFrame *frame); + void init(JSTypesStackFrame *frame); }; } diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp index a99ec16943..a49bd32d66 100644 --- a/src/qml/jsruntime/qv4arraybuffer.cpp +++ b/src/qml/jsruntime/qv4arraybuffer.cpp @@ -1,46 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4arraybuffer_p.h" #include "qv4typedarray_p.h" #include "qv4dataview_p.h" -#include "qv4string_p.h" -#include "qv4jscall_p.h" #include "qv4symbol_p.h" using namespace QV4; @@ -50,14 +12,14 @@ DEFINE_OBJECT_VTABLE(ArrayBufferCtor); DEFINE_OBJECT_VTABLE(SharedArrayBuffer); DEFINE_OBJECT_VTABLE(ArrayBuffer); -void Heap::SharedArrayBufferCtor::init(QV4::ExecutionContext *scope) +void Heap::SharedArrayBufferCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("SharedArrayBuffer")); + Heap::FunctionObject::init(engine, QStringLiteral("SharedArrayBuffer")); } -void Heap::ArrayBufferCtor::init(QV4::ExecutionContext *scope) +void Heap::ArrayBufferCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("ArrayBuffer")); + Heap::FunctionObject::init(engine, QStringLiteral("ArrayBuffer")); } ReturnedValue SharedArrayBufferCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) @@ -66,14 +28,15 @@ ReturnedValue SharedArrayBufferCtor::virtualCallAsConstructor(const FunctionObje if (newTarget->isUndefined()) return scope.engine->throwTypeError(); - qint64 len = argc ? argv[0].toIndex() : 0; - if (scope.engine->hasException) + const double len = argc ? argv[0].toInteger() : 0; + if (scope.hasException()) return Encode::undefined(); - if (len < 0 || len >= INT_MAX) + if (len < 0 || len >= std::numeric_limits<int>::max()) return scope.engine->throwRangeError(QStringLiteral("SharedArrayBuffer: Invalid length.")); - Scoped<SharedArrayBuffer> a(scope, scope.engine->memoryManager->allocate<SharedArrayBuffer>(len)); - if (scope.engine->hasException) + Scoped<SharedArrayBuffer> a( + scope, scope.engine->memoryManager->allocate<SharedArrayBuffer>(size_t(len))); + if (scope.hasException()) return Encode::undefined(); return a->asReturnedValue(); @@ -105,7 +68,7 @@ ReturnedValue ArrayBufferCtor::virtualCallAsConstructor(const FunctionObject *f, if (o) a->setPrototypeOf(o); } - if (scope.engine->hasException) + if (scope.hasException()) return Encode::undefined(); return a->asReturnedValue(); @@ -127,13 +90,18 @@ ReturnedValue ArrayBufferCtor::method_isView(const FunctionObject *, const Value void Heap::SharedArrayBuffer::init(size_t length) { Object::init(); + QPair<QTypedArrayData<char> *, char *> pair; if (length < UINT_MAX) - data = QTypedArrayData<char>::allocate(length + 1); - if (!data) { + pair = QTypedArrayData<char>::allocate(length + 1); + if (!pair.first) { + new (&arrayDataPointerStorage) QArrayDataPointer<char>(); internalClass->engine->throwRangeError(QStringLiteral("ArrayBuffer: out of memory")); return; } - data->size = int(length); + auto data = new (&arrayDataPointerStorage) QArrayDataPointer<char>{ + pair.first, pair.second, qsizetype(length) }; + + // can't use appendInitialize() because we want to set the terminating '\0' memset(data->data(), 0, length + 1); isShared = true; } @@ -141,41 +109,24 @@ void Heap::SharedArrayBuffer::init(size_t length) void Heap::SharedArrayBuffer::init(const QByteArray& array) { Object::init(); - data = const_cast<QByteArray&>(array).data_ptr(); - data->ref.ref(); + new (&arrayDataPointerStorage) QArrayDataPointer<char>(*const_cast<QByteArray &>(array).data_ptr()); isShared = true; } void Heap::SharedArrayBuffer::destroy() { - if (data && !data->ref.deref()) - QTypedArrayData<char>::deallocate(data); + arrayDataPointer().~QArrayDataPointer(); Object::destroy(); } QByteArray ArrayBuffer::asByteArray() const { - QByteArrayDataPtr ba = { d()->data }; - ba.ptr->ref.ref(); - return QByteArray(ba); + return QByteArray(constArrayData(), arrayDataLength()); } -void ArrayBuffer::detach() { - if (!d()->data->ref.isShared()) - return; - - QTypedArrayData<char> *oldData = d()->data; - - d()->data = QTypedArrayData<char>::allocate(oldData->size + 1); - if (!d()->data) { - engine()->throwRangeError(QStringLiteral("ArrayBuffer: out of memory")); - return; - } - - memcpy(d()->data->data(), oldData->data(), oldData->size + 1); - - if (!oldData->ref.deref()) - QTypedArrayData<char>::deallocate(oldData); +void ArrayBuffer::detach() +{ + detachArrayData(); } @@ -197,10 +148,10 @@ void SharedArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor) ReturnedValue SharedArrayBufferPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int) { const SharedArrayBuffer *a = thisObject->as<SharedArrayBuffer>(); - if (!a || a->isDetachedBuffer() || !a->isSharedArrayBuffer()) + if (!a || a->hasDetachedArrayData() || !a->isSharedArrayBuffer()) return b->engine()->throwTypeError(); - return Encode(a->d()->data->size); + return Encode(a->arrayDataLength()); } ReturnedValue SharedArrayBufferPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) @@ -212,17 +163,18 @@ ReturnedValue SharedArrayBufferPrototype::slice(const FunctionObject *b, const V { Scope scope(b); const SharedArrayBuffer *a = thisObject->as<SharedArrayBuffer>(); - if (!a || a->isDetachedBuffer() || (a->isSharedArrayBuffer() != shared)) + if (!a || a->hasDetachedArrayData() || (a->isSharedArrayBuffer() != shared)) return scope.engine->throwTypeError(); + const uint aDataLength = a->arrayDataLength(); + double start = argc > 0 ? argv[0].toInteger() : 0; - double end = (argc < 2 || argv[1].isUndefined()) ? - a->d()->data->size : argv[1].toInteger(); + double end = (argc < 2 || argv[1].isUndefined()) ? aDataLength : argv[1].toInteger(); if (scope.hasException()) return QV4::Encode::undefined(); - double first = (start < 0) ? qMax(a->d()->data->size + start, 0.) : qMin(start, (double)a->d()->data->size); - double final = (end < 0) ? qMax(a->d()->data->size + end, 0.) : qMin(end, (double)a->d()->data->size); + double first = (start < 0) ? qMax(aDataLength + start, 0.) : qMin(start, double(aDataLength)); + double final = (end < 0) ? qMax(aDataLength + end, 0.) : qMin(end, double(aDataLength)); const FunctionObject *constructor = a->speciesConstructor(scope, shared ? scope.engine->sharedArrayBufferCtor() : scope.engine->arrayBufferCtor()); if (!constructor) @@ -231,13 +183,13 @@ ReturnedValue SharedArrayBufferPrototype::slice(const FunctionObject *b, const V double newLen = qMax(final - first, 0.); ScopedValue argument(scope, QV4::Encode(newLen)); QV4::Scoped<SharedArrayBuffer> newBuffer(scope, constructor->callAsConstructor(argument, 1)); - if (!newBuffer || newBuffer->d()->data->size < (int)newLen || - newBuffer->isDetachedBuffer() || (newBuffer->isSharedArrayBuffer() != shared) || + if (!newBuffer || newBuffer->arrayDataLength() < newLen || + newBuffer->hasDetachedArrayData() || (newBuffer->isSharedArrayBuffer() != shared) || newBuffer->sameValue(*a) || - a->isDetachedBuffer()) + a->hasDetachedArrayData()) return scope.engine->throwTypeError(); - memcpy(newBuffer->d()->data->data(), a->d()->data->data() + (uint)first, newLen); + memcpy(newBuffer->arrayData(), a->constArrayData() + (uint)first, newLen); return newBuffer->asReturnedValue(); } @@ -262,10 +214,13 @@ void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor) ReturnedValue ArrayBufferPrototype::method_get_byteLength(const FunctionObject *f, const Value *thisObject, const Value *, int) { const ArrayBuffer *a = thisObject->as<ArrayBuffer>(); - if (!a || a->isDetachedBuffer() || a->isSharedArrayBuffer()) + if (!a || a->isSharedArrayBuffer()) return f->engine()->throwTypeError(); - return Encode(a->d()->data->size); + if (a->hasDetachedArrayData()) + return Encode(0); + + return Encode(a->arrayDataLength()); } ReturnedValue ArrayBufferPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h index 8344fa2554..af1195a947 100644 --- a/src/qml/jsruntime/qv4arraybuffer_p.h +++ b/src/qml/jsruntime/qv4arraybuffer_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4ARRAYBUFFER_H #define QV4ARRAYBUFFER_H @@ -52,6 +16,7 @@ #include "qv4object_p.h" #include "qv4functionobject_p.h" +#include <QtCore/qarraydatapointer.h> QT_BEGIN_NAMESPACE @@ -60,43 +25,60 @@ namespace QV4 { namespace Heap { struct SharedArrayBufferCtor : FunctionObject { - void init(QV4::ExecutionContext *scope); + void init(QV4::ExecutionEngine *engine); }; struct ArrayBufferCtor : SharedArrayBufferCtor { - void init(QV4::ExecutionContext *scope); + void init(QV4::ExecutionEngine *engine); }; -struct Q_QML_PRIVATE_EXPORT SharedArrayBuffer : Object { +struct Q_QML_EXPORT SharedArrayBuffer : Object { void init(size_t length); void init(const QByteArray& array); void destroy(); - QTypedArrayData<char> *data; - bool isShared; - uint byteLength() const { return data ? data->size : 0; } + void setSharedArrayBuffer(bool shared) noexcept { isShared = shared; } + bool isSharedArrayBuffer() const noexcept { return isShared; } + + char *arrayData() noexcept { return arrayDataPointer()->data(); } + const char *constArrayData() const noexcept { return constArrayDataPointer()->data(); } + uint arrayDataLength() const noexcept { return constArrayDataPointer().size; } + + bool hasSharedArrayData() const noexcept { return constArrayDataPointer().isShared(); } + bool hasDetachedArrayData() const noexcept { return constArrayDataPointer().isNull(); } + void detachArrayData() noexcept { arrayDataPointer().clear(); } + + bool arrayDataNeedsDetach() const noexcept { return constArrayDataPointer().needsDetach(); } + +private: + const QArrayDataPointer<const char> &constArrayDataPointer() const noexcept + { + return *reinterpret_cast<const QArrayDataPointer<const char> *>(&arrayDataPointerStorage); + } + QArrayDataPointer<char> &arrayDataPointer() noexcept + { + return *reinterpret_cast<QArrayDataPointer<char> *>(&arrayDataPointerStorage); + } + + template <typename T> + struct storage_t { alignas(T) unsigned char data[sizeof(T)]; }; - bool isDetachedBuffer() const { return !data; } - bool isSharedArrayBuffer() const { return isShared; } + storage_t<QArrayDataPointer<char>> + arrayDataPointerStorage; + bool isShared; }; -struct Q_QML_PRIVATE_EXPORT ArrayBuffer : SharedArrayBuffer { +struct Q_QML_EXPORT ArrayBuffer : SharedArrayBuffer { void init(size_t length) { SharedArrayBuffer::init(length); - isShared = false; + setSharedArrayBuffer(false); } void init(const QByteArray& array) { SharedArrayBuffer::init(array); - isShared = false; - } - void detachArrayBuffer() { - if (data && !data->ref.deref()) - QTypedArrayData<char>::deallocate(data); - data = nullptr; + setSharedArrayBuffer(false); } }; - } struct SharedArrayBufferCtor : FunctionObject @@ -116,37 +98,38 @@ struct ArrayBufferCtor : SharedArrayBufferCtor static ReturnedValue method_isView(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; -struct Q_QML_PRIVATE_EXPORT SharedArrayBuffer : Object +struct Q_QML_EXPORT SharedArrayBuffer : Object { V4_OBJECT2(SharedArrayBuffer, Object) V4_NEEDS_DESTROY V4_PROTOTYPE(sharedArrayBufferPrototype) QByteArray asByteArray() const; - uint byteLength() const { return d()->byteLength(); } - char *data() { Q_ASSERT(d()->data); return d()->data->data(); } - const char *constData() { Q_ASSERT(d()->data); return d()->data->data(); } - bool isShared() { return d()->data->ref.isShared(); } - bool isDetachedBuffer() const { return !d()->data; } - bool isSharedArrayBuffer() const { return d()->isShared; } + uint arrayDataLength() const { return d()->arrayDataLength(); } + char *arrayData() { return d()->arrayData(); } + const char *constArrayData() const { return d()->constArrayData(); } + + bool hasSharedArrayData() { return d()->hasSharedArrayData(); } + bool hasDetachedArrayData() const { return d()->hasDetachedArrayData(); } + bool isSharedArrayBuffer() const { return d()->isSharedArrayBuffer(); } }; -struct Q_QML_PRIVATE_EXPORT ArrayBuffer : SharedArrayBuffer +struct Q_QML_EXPORT ArrayBuffer : SharedArrayBuffer { V4_OBJECT2(ArrayBuffer, SharedArrayBuffer) V4_NEEDS_DESTROY V4_PROTOTYPE(arrayBufferPrototype) QByteArray asByteArray() const; - uint byteLength() const { return d()->byteLength(); } - char *data() { detach(); return d()->data ? d()->data->data() : nullptr; } + uint arrayDataLength() const { return d()->arrayDataLength(); } + char *dataData() { if (d()->arrayDataNeedsDetach()) detach(); return d()->arrayData(); } // ### is that detach needed? - const char *constData() { detach(); return d()->data ? d()->data->data() : nullptr; } + const char *constArrayData() const { return d()->constArrayData(); } + bool hasSharedArrayData() { return d()->hasSharedArrayData(); } + void detachArrayData() { d()->detachArrayData(); } - bool isShared() { return d()->data && d()->data->ref.isShared(); } void detach(); - void detachArrayBuffer() { d()->detachArrayBuffer(); } }; struct SharedArrayBufferPrototype : Object diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index 654d33b8d1..724f6fbfa3 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4arraydata_p.h" #include "qv4object_p.h" #include "qv4functionobject_p.h" @@ -563,7 +527,7 @@ uint ArrayData::append(Object *obj, ArrayObject *otherObj, uint n) ScopedValue v(scope); for (uint i = 0; i < n; ++i) obj->arraySet(oldSize + i, (v = otherObj->get(i))); - } else if (other && other->isSparse()) { + } else if (other->isSparse()) { Heap::SparseArrayData *os = static_cast<Heap::SparseArrayData *>(other->d()); if (other->hasAttributes()) { ScopedValue v(scope); @@ -623,21 +587,6 @@ void ArrayData::insert(Object *o, uint index, const Value *v, bool isAccessor) s->setArrayData(o->engine(), n->value + Object::SetterOffset, v[Object::SetterOffset]); } - -class ArrayElementLessThan -{ -public: - inline ArrayElementLessThan(ExecutionEngine *engine, const Value &comparefn) - : m_engine(engine), m_comparefn(comparefn) {} - - bool operator()(Value v1, Value v2) const; - -private: - ExecutionEngine *m_engine; - const Value &m_comparefn; -}; - - bool ArrayElementLessThan::operator()(Value v1, Value v2) const { Scope scope(m_engine); @@ -650,72 +599,26 @@ bool ArrayElementLessThan::operator()(Value v1, Value v2) const if (o) { Scope scope(o->engine()); ScopedValue result(scope); - JSCallData jsCallData(scope, 2); - jsCallData->args[0] = v1; - jsCallData->args[1] = v2; + JSCallArguments jsCallData(scope, 2); + jsCallData.args[0] = v1; + jsCallData.args[1] = v2; result = o->call(jsCallData); + if (scope.hasException()) + return false; return result->toNumber() < 0; } ScopedString p1s(scope, v1.toString(scope.engine)); ScopedString p2s(scope, v2.toString(scope.engine)); - return p1s->toQString() < p2s->toQString(); -} - -template <typename RandomAccessIterator, typename T, typename LessThan> -void sortHelper(RandomAccessIterator start, RandomAccessIterator end, const T &t, LessThan lessThan) -{ -top: - int span = int(end - start); - if (span < 2) - return; - - --end; - RandomAccessIterator low = start, high = end - 1; - RandomAccessIterator pivot = start + span / 2; - - if (lessThan(*end, *start)) - qSwap(*end, *start); - if (span == 2) - return; - - if (lessThan(*pivot, *start)) - qSwap(*pivot, *start); - if (lessThan(*end, *pivot)) - qSwap(*end, *pivot); - if (span == 3) - return; - - qSwap(*pivot, *end); - - while (low < high) { - while (low < high && lessThan(*low, *end)) - ++low; - while (high > low && lessThan(*end, *high)) - --high; - - if (low < high) { - qSwap(*low, *high); - ++low; - --high; - } else { - break; - } - } - - if (lessThan(*low, *end)) - ++low; - - qSwap(*end, *low); - sortHelper(start, low, t, lessThan); + if (!p1s) + return false; + if (!p2s) + return true; - start = low + 1; - ++end; - goto top; + return p1s->toQString() < p2s->toQString(); } - void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &comparefn, uint len) { if (!len) @@ -727,11 +630,6 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c if (!arrayData || !arrayData->length()) return; - if (!comparefn.isUndefined() && !comparefn.isFunctionObject()) { - engine->throwTypeError(); - return; - } - // 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. @@ -806,10 +704,38 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c } - ArrayElementLessThan lessThan(engine, static_cast<const FunctionObject &>(comparefn)); + ArrayElementLessThan lessThan(engine, comparefn); - Value *begin = thisObject->arrayData()->values.values; - sortHelper(begin, begin + len, *begin, lessThan); + const auto thisArrayData = thisObject->arrayData(); + uint startIndex = thisArrayData->mappedIndex(0); + uint endIndex = thisArrayData->mappedIndex(len - 1) + 1; + if (startIndex < endIndex) { + // Values are contiguous. Sort right away. + sortHelper( + thisArrayData->values.values + startIndex, + thisArrayData->values.values + endIndex, + lessThan); + } else { + // Values wrap around the end of the allocation. Close the gap to form a contiguous array. + // We're going to sort anyway. So we don't need to care about order. + + // ArrayElementLessThan sorts empty and undefined to the end of the array anyway, but we + // probably shouldn't rely on the unused slots to be actually undefined or empty. + + const uint gap = startIndex - endIndex; + const uint allocEnd = thisArrayData->values.alloc - 1; + for (uint i = 0; i < gap; ++i) { + const uint from = allocEnd - i; + const uint to = endIndex + i; + if (from < startIndex) + break; + + std::swap(thisArrayData->values.values[from], thisArrayData->values.values[to]); + } + + thisArrayData->offset = 0; + sortHelper(thisArrayData->values.values, thisArrayData->values.values + len, lessThan); + } #ifdef CHECK_SPARSE_ARRAYS thisObject->initSparseArray(); diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h index 64b2161ef5..a7ab1f4a71 100644 --- a/src/qml/jsruntime/qv4arraydata_p.h +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4ARRAYDATA_H #define QV4ARRAYDATA_H @@ -129,7 +93,7 @@ DECLARE_HEAP_OBJECT(ArrayData, Base) { uint mappedIndex(uint index) const; }; -Q_STATIC_ASSERT(std::is_trivial< ArrayData >::value); +Q_STATIC_ASSERT(std::is_trivial_v<ArrayData>); struct SimpleArrayData : public ArrayData { uint mappedIndex(uint index) const { index += offset; if (index >= values.alloc) index -= values.alloc; return index; } @@ -142,7 +106,7 @@ struct SimpleArrayData : public ArrayData { return attrs ? attrs[i] : Attr_Data; } }; -Q_STATIC_ASSERT(std::is_trivial< SimpleArrayData >::value); +Q_STATIC_ASSERT(std::is_trivial_v<SimpleArrayData>); struct SparseArrayData : public ArrayData { void destroy() { @@ -267,6 +231,74 @@ struct Q_QML_EXPORT SparseArrayData : public ArrayData static uint length(const Heap::ArrayData *d); }; +class ArrayElementLessThan +{ +public: + inline ArrayElementLessThan(ExecutionEngine *engine, const Value &comparefn) + : m_engine(engine), m_comparefn(comparefn) {} + + bool operator()(Value v1, Value v2) const; + +private: + ExecutionEngine *m_engine; + const Value &m_comparefn; +}; + +template <typename RandomAccessIterator, typename LessThan> +void sortHelper(RandomAccessIterator start, RandomAccessIterator end, LessThan lessThan) +{ +top: + using std::swap; + + int span = int(end - start); + if (span < 2) + return; + + --end; + RandomAccessIterator low = start, high = end - 1; + RandomAccessIterator pivot = start + span / 2; + + if (lessThan(*end, *start)) + swap(*end, *start); + if (span == 2) + return; + + if (lessThan(*pivot, *start)) + swap(*pivot, *start); + if (lessThan(*end, *pivot)) + swap(*end, *pivot); + if (span == 3) + return; + + swap(*pivot, *end); + + while (low < high) { + while (low < high && lessThan(*low, *end)) + ++low; + + while (high > low && lessThan(*end, *high)) + --high; + + if (low < high) { + swap(*low, *high); + ++low; + --high; + } else { + break; + } + } + + if (lessThan(*low, *end)) + ++low; + + swap(*end, *low); + sortHelper(start, low, lessThan); + + start = low + 1; + ++end; + goto top; +} + namespace Heap { inline uint ArrayData::mappedIndex(uint index) const diff --git a/src/qml/jsruntime/qv4arrayiterator.cpp b/src/qml/jsruntime/qv4arrayiterator.cpp index 199b1a728a..15e0bf4e4c 100644 --- a/src/qml/jsruntime/qv4arrayiterator.cpp +++ b/src/qml/jsruntime/qv4arrayiterator.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Crimson AS <info@crimson.no> -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 Crimson AS <info@crimson.no> +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <private/qv4iterator_p.h> #include <private/qv4arrayiterator_p.h> @@ -72,7 +36,6 @@ ReturnedValue ArrayIteratorPrototype::method_next(const FunctionObject *b, const quint32 index = thisObject->d()->nextIndex; IteratorKind itemKind = thisObject->d()->iterationKind; - Scoped<TypedArray> ta(scope, a->as<TypedArray>()); quint32 len = a->getLength(); if (index >= len) { @@ -86,18 +49,18 @@ ReturnedValue ArrayIteratorPrototype::method_next(const FunctionObject *b, const return IteratorPrototype::createIterResultObject(scope.engine, Value::fromInt32(index), false); } - ReturnedValue elementValue = a->get(index); + QV4::ScopedValue elementValue(scope, a->get(index)); CHECK_EXCEPTION(); if (itemKind == ValueIteratorKind) { - return IteratorPrototype::createIterResultObject(scope.engine, Value::fromReturnedValue(elementValue), false); + return IteratorPrototype::createIterResultObject(scope.engine, elementValue, false); } else { Q_ASSERT(itemKind == KeyValueIteratorKind); ScopedArrayObject resultArray(scope, scope.engine->newArrayObject()); resultArray->arrayReserve(2); resultArray->arrayPut(0, Value::fromInt32(index)); - resultArray->arrayPut(1, Value::fromReturnedValue(elementValue)); + resultArray->arrayPut(1, elementValue); resultArray->setArrayLengthUnchecked(2); return IteratorPrototype::createIterResultObject(scope.engine, resultArray, false); diff --git a/src/qml/jsruntime/qv4arrayiterator_p.h b/src/qml/jsruntime/qv4arrayiterator_p.h index 6d6bb466f1..79f0898fb6 100644 --- a/src/qml/jsruntime/qv4arrayiterator_p.h +++ b/src/qml/jsruntime/qv4arrayiterator_p.h @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Crimson AS <info@crimson.no> -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 Crimson AS <info@crimson.no> +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4ARRAYITERATOR_P_H #define QV4ARRAYITERATOR_P_H @@ -54,7 +18,6 @@ #include "qv4object_p.h" #include "qv4iterator_p.h" -#include "qv4arraydata_p.h" QT_BEGIN_NAMESPACE @@ -69,7 +32,7 @@ namespace Heap { Member(class, NoMark, quint32, nextIndex) DECLARE_HEAP_OBJECT(ArrayIteratorObject, Object) { - DECLARE_MARKOBJECTS(ArrayIteratorObject); + DECLARE_MARKOBJECTS(ArrayIteratorObject) void init(Object *obj, QV4::ExecutionEngine *engine) { Object::init(); diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index af1a2d1de0..40a7123232 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -1,63 +1,22 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Crimson AS <info@crimson.no> -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 Crimson AS <info@crimson.no> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4arrayobject_p.h" -#include "qv4objectiterator_p.h" #include "qv4arrayiterator_p.h" -#include "qv4sparsearray_p.h" #include "qv4objectproto_p.h" -#include "qv4jscall_p.h" #include "qv4argumentsobject_p.h" #include "qv4runtime_p.h" -#include "qv4string_p.h" #include "qv4symbol_p.h" #include <QtCore/qscopedvaluerollback.h> -#include "qv4proxy_p.h" using namespace QV4; DEFINE_OBJECT_VTABLE(ArrayCtor); -void Heap::ArrayCtor::init(QV4::ExecutionContext *scope) +void Heap::ArrayCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("Array")); + Heap::FunctionObject::init(engine, QStringLiteral("Array")); } ReturnedValue ArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) @@ -235,9 +194,8 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V // sets them into the created array. forever { if (k > (static_cast<qint64>(1) << 53) - 1) { - ScopedValue falsey(scope, Encode(false)); ScopedValue error(scope, scope.engine->throwTypeError()); - return Runtime::IteratorClose::call(scope.engine, iterator, falsey); + return Runtime::IteratorClose::call(scope.engine, iterator); } // Retrieve the next value. If the iteration ends, we're done here. @@ -254,11 +212,12 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V } if (mapfn) { + Q_ASSERT(mapArguments); // if mapfn is set, we always setup mapArguments with scope.alloc mapArguments[0] = *nextValue; mapArguments[1] = Value::fromDouble(k); mappedValue = mapfn->call(thisArg, mapArguments, 2); - if (scope.engine->hasException) - return Runtime::IteratorClose::call(scope.engine, iterator, Value::fromBoolean(false)); + if (scope.hasException()) + return Runtime::IteratorClose::call(scope.engine, iterator); } else { mappedValue = *nextValue; } @@ -270,10 +229,8 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k)); } - if (scope.engine->hasException) { - ScopedValue falsey(scope, Encode(false)); - return Runtime::IteratorClose::call(scope.engine, iterator, falsey); - } + if (scope.hasException()) + return Runtime::IteratorClose::call(scope.engine, iterator); k++; } @@ -297,6 +254,7 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V CHECK_EXCEPTION(); if (mapfn) { + Q_ASSERT(mapArguments); // if mapfn is set, we always setup mapArguments with scope.alloc mapArguments[0] = kValue; mapArguments[1] = Value::fromDouble(k); mappedValue = mapfn->call(thisArg, mapArguments, 2); @@ -362,7 +320,7 @@ ReturnedValue ArrayPrototype::method_toString(const FunctionObject *builtin, con ScopedString string(scope, scope.engine->newString(QStringLiteral("join"))); ScopedFunctionObject f(scope, that->get(string)); if (f) - return f->call(that, argv, argc); + return checkedResult(scope.engine, f->call(that, argv, argc)); return ObjectPrototype::method_toString(builtin, that, argv, argc); } @@ -381,6 +339,9 @@ ReturnedValue ArrayPrototype::method_toLocaleString(const FunctionObject *b, con ScopedValue v(scope); ScopedString s(scope); + ScopedPropertyKey tolocaleString(scope, scope.engine->id_toLocaleString()->toPropertyKey()); + Q_ASSERT(!scope.engine->hasException); + for (uint k = 0; k < len; ++k) { if (k) R += separator; @@ -388,7 +349,18 @@ ReturnedValue ArrayPrototype::method_toLocaleString(const FunctionObject *b, con v = instance->get(k); if (v->isNullOrUndefined()) continue; - v = Runtime::CallElement::call(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0); + + ScopedObject valueAsObject(scope, v->toObject(scope.engine)); + Q_ASSERT(valueAsObject); // null and undefined handled above + + ScopedFunctionObject function(scope, valueAsObject->get(tolocaleString)); + if (!function) + return scope.engine->throwTypeError(); + + v = function->call(valueAsObject, nullptr, 0); + if (scope.hasException()) + return Encode::undefined(); + s = v->toString(scope.engine); if (scope.hasException()) return Encode::undefined(); @@ -421,7 +393,7 @@ ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value } else if (eltAsObj && eltAsObj->isConcatSpreadable()) { const uint startIndex = result->getLength(); const uint len = eltAsObj->getLength(); - if (scope.engine->hasException) + if (scope.hasException()) return Encode::undefined(); for (uint i = 0; i < len; ++i) { @@ -432,7 +404,7 @@ ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value return scope.engine->throwTypeError(); } } - } else if (eltAsObj && eltAsObj->isListType()) { + } else if (eltAsObj && eltAsObj->isArrayLike()) { const uint startIndex = result->getLength(); for (int i = 0, len = eltAsObj->getLength(); i < len; ++i) { entry = eltAsObj->get(i); @@ -597,65 +569,63 @@ ReturnedValue ArrayPrototype::method_findIndex(const FunctionObject *b, const Va return Encode(-1); } -ReturnedValue ArrayPrototype::method_join(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +ReturnedValue ArrayPrototype::method_join(const FunctionObject *functionObject, + const Value *thisObject, const Value *argv, int argc) { - Scope scope(b); + Scope scope(functionObject); ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) return Encode(scope.engine->newString()); - ScopedValue arg(scope, argc ? argv[0] : Value::undefinedValue()); - - QString r4; - if (arg->isUndefined()) - r4 = QStringLiteral(","); - else - r4 = arg->toQString(); + // We cannot optimize the resolution of the argument away in case of length == 0 + // It may have side effects. + ScopedValue argument(scope, argc ? argv[0] : Value::undefinedValue()); + const QString separator = argument->isUndefined() + ? QStringLiteral(",") + : argument->toQString(); - ScopedValue length(scope, instance->get(scope.engine->id_length())); - const quint32 r2 = length->isUndefined() ? 0 : length->toUInt32(); - - if (!r2) + ScopedValue scopedLength(scope, instance->get(scope.engine->id_length())); + const quint32 genericLength = scopedLength->isUndefined() ? 0 : scopedLength->toUInt32(); + if (!genericLength) return Encode(scope.engine->newString()); - QString R; - - // ### FIXME - if (ArrayObject *a = instance->as<ArrayObject>()) { - ScopedValue e(scope); - for (uint i = 0; i < a->getLength(); ++i) { + QString result; + if (auto *arrayObject = instance->as<ArrayObject>()) { + ScopedValue entry(scope); + const qint64 arrayLength = arrayObject->getLength(); + Q_ASSERT(arrayLength >= 0); + Q_ASSERT(arrayLength <= std::numeric_limits<quint32>::max()); + for (quint32 i = 0; i < quint32(arrayLength); ++i) { if (i) - R += r4; + result += separator; - e = a->get(i); + entry = arrayObject->get(i); CHECK_EXCEPTION(); - if (!e->isNullOrUndefined()) - R += e->toQString(); + if (!entry->isNullOrUndefined()) + result += entry->toQString(); } } else { - // - // crazy! - // ScopedString name(scope, scope.engine->newString(QStringLiteral("0"))); - ScopedValue r6(scope, instance->get(name)); - if (!r6->isNullOrUndefined()) - R = r6->toQString(); + ScopedValue value(scope, instance->get(name)); + CHECK_EXCEPTION(); - ScopedValue r12(scope); - for (quint32 k = 1; k < r2; ++k) { - R += r4; + if (!value->isNullOrUndefined()) + result = value->toQString(); - name = Value::fromDouble(k).toString(scope.engine); - r12 = instance->get(name); + for (quint32 i = 1; i < genericLength; ++i) { + result += separator; + + name = Value::fromDouble(i).toString(scope.engine); + value = instance->get(name); CHECK_EXCEPTION(); - if (!r12->isNullOrUndefined()) - R += r12->toQString(); + if (!value->isNullOrUndefined()) + result += value->toQString(); } } - return Encode(scope.engine->newString(R)); + return Encode(scope.engine->newString(result)); } ReturnedValue ArrayPrototype::method_pop(const FunctionObject *b, const Value *thisObject, const Value *, int) @@ -878,15 +848,69 @@ ReturnedValue ArrayPrototype::method_slice(const FunctionObject *b, const Value ReturnedValue ArrayPrototype::method_sort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { + // Based on https://tc39.es/ecma262/#sec-array.prototype.sort + Scope scope(b); + + ScopedValue comparefn(scope, argc ? argv[0] : Value::undefinedValue()); + + // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception. + if (!comparefn->isUndefined() && !comparefn->isFunctionObject()) + return scope.engine->throwTypeError(QStringLiteral("The provided comparison function is not callable.")); + + // 2. Let obj be ? ToObject(this value). ScopedObject instance(scope, thisObject->toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); + // 3. Let len be ? LengthOfArrayLike(obj). uint len = instance->getLength(); - ScopedValue comparefn(scope, argc ? argv[0] : Value::undefinedValue()); - ArrayData::sort(scope.engine, instance, comparefn, len); + if (instance->arrayData() && instance->arrayData()->length()) { + ArrayData::sort(scope.engine, instance, comparefn, len); + } else { + // Generic implementation that does not require a populated + // ArrayData, this is used, for example, by `Sequences` which + // store their data in a different way. + + // 5. Let sortedList be ? SortIndexedProperties(obj, len, SortCompare, skip-holes) + Value* sorted = scope.alloc(scope.engine->safeForAllocLength(len)); + CHECK_EXCEPTION(); + + uint written = 0; + for (uint index = 0; index < len; ++index) { + bool hasProperty = false; + auto element = instance->get(index, &hasProperty); + + if (hasProperty) { + sorted[written] = element; + ++written; + } + } + + std::stable_sort(sorted, sorted + written, ArrayElementLessThan(scope.engine, comparefn)); + + // [...] + // 8. Repeat, while j < itemCount, + // a. Perform ? Set(obj, ! ToString(𝔽(j)), sortedList[j], true). + // [...] + for (uint index = 0; index < written; ++index) { + instance->setIndexed(index, sorted[index], QV4::Object::DoThrowOnRejection); + CHECK_EXCEPTION(); + } + + // [...] + // 10. Repeat, while j < len, + // a. Perform ? DeletePropertyOrThrow(obj, ! ToString(𝔽(j))). + // [...] + while (written < len) { + if (!instance->deleteProperty(PropertyKey::fromArrayIndex(written))) + return scope.engine->throwTypeError(); + ++written; + } + } + + // 11. Return obj return thisObject->asReturnedValue(); } @@ -1050,8 +1074,9 @@ ReturnedValue ArrayPrototype::method_includes(const FunctionObject *b, const Val } } + ScopedValue val(scope); while (k < len) { - ScopedValue val(scope, instance->get(k)); + val = instance->get(k); if (val->sameValueZero(argv[0])) { return Encode(true); } @@ -1210,6 +1235,7 @@ ReturnedValue ArrayPrototype::method_every(const FunctionObject *b, const Value arguments[1] = Value::fromDouble(k); arguments[2] = instance; r = callback->call(that, arguments, 3); + CHECK_EXCEPTION(); ok = r->toBoolean(); } return Encode(ok); @@ -1222,31 +1248,38 @@ ReturnedValue ArrayPrototype::method_fill(const FunctionObject *b, const Value * if (!instance) RETURN_UNDEFINED(); - uint len = instance->getLength(); - int relativeStart = argc > 1 ? argv[1].toInteger() : 0; - int relativeEnd = len; - if (argc > 2 && !argv[2].isUndefined()) { + const qsizetype len = instance->getLength(); + Q_ASSERT(len >= 0); + + const qsizetype relativeStart = argc > 1 ? argv[1].toInteger() : 0; + qsizetype relativeEnd = len; + if (argc > 2 && !argv[2].isUndefined()) relativeEnd = argv[2].toInteger(); - } - uint k = 0; - uint fin = 0; + + qsizetype k = 0; + qsizetype fin = 0; if (relativeStart < 0) { - k = std::max(len+relativeStart, uint(0)); + if (relativeStart > -len) + k = std::max(len + relativeStart, qsizetype(0)); } else { - k = std::min(uint(relativeStart), len); + k = std::min(relativeStart, len); } + Q_ASSERT(k >= 0); if (relativeEnd < 0) { - fin = std::max(len + relativeEnd, uint(0)); + if (relativeEnd > -len) + fin = std::max(len + relativeEnd, qsizetype(0)); } else { - fin = std::min(uint(relativeEnd), len); + fin = std::min(relativeEnd, len); } + Q_ASSERT(fin >= 0); - while (k < fin) { - instance->setIndexed(k, argv[0], QV4::Object::DoThrowOnRejection); - k++; - } + if (sizeof(qsizetype) > sizeof(uint) && fin > qsizetype(std::numeric_limits<uint>::max())) + return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range.")); + + for (; k < fin; ++k) + instance->setIndexed(uint(k), argv[0], QV4::Object::DoThrowOnRejection); return instance.asReturnedValue(); } @@ -1277,6 +1310,7 @@ ReturnedValue ArrayPrototype::method_some(const FunctionObject *b, const Value * arguments[1] = Value::fromDouble(k); arguments[2] = instance; result = callback->call(that, arguments, 3); + CHECK_EXCEPTION(); if (result->toBoolean()) return Encode(true); } @@ -1346,6 +1380,7 @@ ReturnedValue ArrayPrototype::method_map(const FunctionObject *b, const Value *t arguments[1] = Value::fromDouble(k); arguments[2] = instance; mapped = callback->call(that, arguments, 3); + CHECK_EXCEPTION(); a->arraySet(k, mapped); } return a.asReturnedValue(); @@ -1381,6 +1416,7 @@ ReturnedValue ArrayPrototype::method_filter(const FunctionObject *b, const Value arguments[1] = Value::fromDouble(k); arguments[2] = instance; selected = callback->call(that, arguments, 3); + CHECK_EXCEPTION(); if (selected->toBoolean()) { a->arraySet(to, arguments[0]); ++to; @@ -1431,6 +1467,7 @@ ReturnedValue ArrayPrototype::method_reduce(const FunctionObject *b, const Value arguments[2] = Value::fromDouble(k); arguments[3] = instance; acc = callback->call(nullptr, arguments, 4); + CHECK_EXCEPTION(); } ++k; } @@ -1484,6 +1521,7 @@ ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const arguments[2] = Value::fromDouble(k - 1); arguments[3] = instance; acc = callback->call(nullptr, arguments, 4); + CHECK_EXCEPTION(); } --k; } @@ -1506,4 +1544,3 @@ ReturnedValue ArrayPrototype::method_get_species(const FunctionObject *, const V { return thisObject->asReturnedValue(); } - diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h index c959b71bc6..a68068937f 100644 --- a/src/qml/jsruntime/qv4arrayobject_p.h +++ b/src/qml/jsruntime/qv4arrayobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4ARRAYOBJECT_H #define QV4ARRAYOBJECT_H @@ -56,12 +20,37 @@ QT_BEGIN_NAMESPACE +inline bool qIsAtMostUintLimit(qsizetype length, uint limit = std::numeric_limits<uint>::max()) +{ + // Use the type with the larger positive range to do the comparison. + + Q_ASSERT(length >= 0); + if constexpr (sizeof(qsizetype) > sizeof(uint)) { + return length <= qsizetype(limit); + } else { + return uint(length) <= limit; + } +} + +inline bool qIsAtMostSizetypeLimit(uint length, qsizetype limit = std::numeric_limits<qsizetype>::max()) +{ + // Use the type with the larger positive range to do the comparison. + + Q_ASSERT(limit >= 0); + if constexpr (sizeof(qsizetype) > sizeof(uint)) { + return qsizetype(length) <= limit; + } else { + return length <= uint(limit); + } +} + + namespace QV4 { namespace Heap { struct ArrayCtor : FunctionObject { - void init(QV4::ExecutionContext *scope); + void init(QV4::ExecutionEngine *engine); }; } diff --git a/src/qml/jsruntime/qv4atomics.cpp b/src/qml/jsruntime/qv4atomics.cpp index 4299aef859..ccbdef145b 100644 --- a/src/qml/jsruntime/qv4atomics.cpp +++ b/src/qml/jsruntime/qv4atomics.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4arraybuffer_p.h" #include "qv4typedarray_p.h" #include "qv4atomics_p.h" @@ -87,7 +51,7 @@ static SharedArrayBuffer *validateSharedIntegerTypedArray(Scope &scope, const Va scope.engine->throwTypeError(); return nullptr; } - Q_ASSERT(!buffer->isDetachedBuffer()); + Q_ASSERT(!buffer->hasDetachedArrayData()); return buffer; } @@ -125,7 +89,7 @@ ReturnedValue atomicReadModifyWrite(const FunctionObject *f, const Value *argv, int bytesPerElement = a.d()->type->bytesPerElement; int byteOffset = a.d()->byteOffset + index * bytesPerElement; - return a.d()->type->atomicModifyOps[modify](buffer->data() + byteOffset, v); + return a.d()->type->atomicModifyOps[modify](buffer->arrayData() + byteOffset, v); } ReturnedValue Atomics::method_add(const FunctionObject *f, const Value *, const Value *argv, int argc) @@ -162,7 +126,7 @@ ReturnedValue Atomics::method_compareExchange(const FunctionObject *f, const Val int bytesPerElement = a.d()->type->bytesPerElement; int byteOffset = a.d()->byteOffset + index * bytesPerElement; - return a.d()->type->atomicCompareExchange(buffer->data() + byteOffset, expected, v); + return a.d()->type->atomicCompareExchange(buffer->arrayData() + byteOffset, expected, v); } ReturnedValue Atomics::method_exchange(const FunctionObject *f, const Value *, const Value *argv, int argc) @@ -203,7 +167,7 @@ ReturnedValue Atomics::method_load(const FunctionObject *f, const Value *, const int bytesPerElement = a.d()->type->bytesPerElement; int byteOffset = a.d()->byteOffset + index * bytesPerElement; - return a.d()->type->atomicLoad(buffer->data() + byteOffset); + return a.d()->type->atomicLoad(buffer->arrayData() + byteOffset); } ReturnedValue Atomics::method_or(const FunctionObject *f, const Value *, const Value *argv, int argc) @@ -232,7 +196,7 @@ ReturnedValue Atomics::method_store(const FunctionObject *f, const Value *, cons int bytesPerElement = a.d()->type->bytesPerElement; int byteOffset = a.d()->byteOffset + index * bytesPerElement; - return a.d()->type->atomicStore(buffer->data() + byteOffset, v); + return a.d()->type->atomicStore(buffer->arrayData() + byteOffset, v); } ReturnedValue Atomics::method_sub(const FunctionObject *f, const Value *, const Value *argv, int argc) diff --git a/src/qml/jsruntime/qv4atomics_p.h b/src/qml/jsruntime/qv4atomics_p.h index 35b64bf4fe..d55e6bb983 100644 --- a/src/qml/jsruntime/qv4atomics_p.h +++ b/src/qml/jsruntime/qv4atomics_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4ATOMICS_H #define QV4ATOMICS_H diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index a9b4ecb607..5c1d50e753 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -1,53 +1,16 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4booleanobject_p.h" -#include "qv4string_p.h" using namespace QV4; DEFINE_OBJECT_VTABLE(BooleanCtor); DEFINE_OBJECT_VTABLE(BooleanObject); -void Heap::BooleanCtor::init(QV4::ExecutionContext *scope) +void Heap::BooleanCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("Boolean")); + Heap::FunctionObject::init(engine, QStringLiteral("Boolean")); } ReturnedValue BooleanCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *newTarget) diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h index 276ec8393b..1b2d3914ac 100644 --- a/src/qml/jsruntime/qv4booleanobject_p.h +++ b/src/qml/jsruntime/qv4booleanobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4BOOLEANOBJECT_H #define QV4BOOLEANOBJECT_H @@ -61,7 +25,7 @@ namespace QV4 { namespace Heap { struct BooleanCtor : FunctionObject { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; } diff --git a/src/qml/jsruntime/qv4compilationunitmapper.cpp b/src/qml/jsruntime/qv4compilationunitmapper.cpp index 74f34a284d..e9915c7d26 100644 --- a/src/qml/jsruntime/qv4compilationunitmapper.cpp +++ b/src/qml/jsruntime/qv4compilationunitmapper.cpp @@ -1,62 +1,81 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4compilationunitmapper_p.h" #include <private/qv4compileddata_p.h> -#include <QFileInfo> -#include <QDateTime> -#include <QCoreApplication> + +#include <QtCore/qdatetime.h> +#include <QtCore/qmutex.h> +#include <QtCore/qhash.h> QT_BEGIN_NAMESPACE using namespace QV4; -CompilationUnitMapper::CompilationUnitMapper() - : dataPtr(nullptr) +class StaticUnitCache +{ +public: + StaticUnitCache() : m_lock(&s_mutex) {} + + CompilationUnitMapper get(const QString &file) + { + const auto it = s_staticUnits.constFind(file); + return it == s_staticUnits.constEnd() ? CompilationUnitMapper() : *it; + } + + void set(const QString &file, const CompilationUnitMapper &staticUnit) { + s_staticUnits.insert(file, staticUnit); + } + + void remove(const QString &file) + { + s_staticUnits.remove(file); + } + +private: + QMutexLocker<QMutex> m_lock; + + static QMutex s_mutex; + + // We can copy the mappers around because they're all static. + // We never unmap the files. + static QHash<QString, CompilationUnitMapper> s_staticUnits; +}; + +QHash<QString, CompilationUnitMapper> StaticUnitCache::s_staticUnits; +QMutex StaticUnitCache::s_mutex; + +CompiledData::Unit *CompilationUnitMapper::get( + const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString) { + StaticUnitCache cache; + + CompilationUnitMapper mapper = cache.get(cacheFilePath); + if (mapper.dataPtr) { + auto *unit = reinterpret_cast<CompiledData::Unit *>(mapper.dataPtr); + if (unit->verifyHeader(sourceTimeStamp, errorString)) { + *this = mapper; + return unit; + } + + return nullptr; + } + CompiledData::Unit *data = open(cacheFilePath, sourceTimeStamp, errorString); + if (data && (data->flags & CompiledData::Unit::StaticData)) { + cache.set(cacheFilePath, *this); + return data; + } else { + close(); + return nullptr; + } } -CompilationUnitMapper::~CompilationUnitMapper() +void CompilationUnitMapper::invalidate(const QString &cacheFilePath) { - close(); + StaticUnitCache cache; + cache.remove(cacheFilePath); } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4compilationunitmapper_p.h b/src/qml/jsruntime/qv4compilationunitmapper_p.h index 80f914c141..c214141804 100644 --- a/src/qml/jsruntime/qv4compilationunitmapper_p.h +++ b/src/qml/jsruntime/qv4compilationunitmapper_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4COMPILATIONUNITMAPPER_H #define QV4COMPILATIONUNITMAPPER_H @@ -65,17 +29,19 @@ struct Unit; class CompilationUnitMapper { public: - CompilationUnitMapper(); - ~CompilationUnitMapper(); + CompiledData::Unit *get( + const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString); + static void invalidate(const QString &cacheFilePath); - CompiledData::Unit *open(const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString); +private: + CompiledData::Unit *open( + const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString); void close(); -private: #if defined(Q_OS_UNIX) - size_t length; + size_t length = 0; #endif - void *dataPtr; + void *dataPtr = nullptr; }; } diff --git a/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp b/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp index a9ab2f5ccb..204e222121 100644 --- a/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp +++ b/src/qml/jsruntime/qv4compilationunitmapper_unix.cpp @@ -1,51 +1,16 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4compilationunitmapper_p.h" -#include <sys/mman.h> -#include <functional> #include <private/qcore_unix_p.h> -#include <QScopeGuard> -#include <QDateTime> +#include <private/qv4compileddata_p.h> -#include "qv4executablecompilationunit_p.h" +#include <QtCore/qscopeguard.h> +#include <QtCore/qdatetime.h> + +#include <functional> +#include <sys/mman.h> QT_BEGIN_NAMESPACE @@ -73,12 +38,22 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co return nullptr; } - if (!ExecutableCompilationUnit::verifyHeader(&header, sourceTimeStamp, errorString)) + if (!header.verifyHeader(sourceTimeStamp, errorString)) return nullptr; // Data structure and qt version matched, so now we can access the rest of the file safely. length = static_cast<size_t>(lseek(fd, 0, SEEK_END)); + /* Error out early on file corruption. We assume we can read header.unitSize bytes + later (even before verifying the checksum), potentially causing out-of-bound + reads + Also, no need to wait until checksum verification if we know beforehand + that the cached unit is bogus + */ + if (length != header.unitSize) { + *errorString = QStringLiteral("Potential file corruption, file too small"); + return nullptr; + } void *ptr = mmap(nullptr, length, PROT_READ, MAP_SHARED, fd, /*offset*/0); if (ptr == MAP_FAILED) { diff --git a/src/qml/jsruntime/qv4compilationunitmapper_win.cpp b/src/qml/jsruntime/qv4compilationunitmapper_win.cpp index 9e8babc5e6..73096207b4 100644 --- a/src/qml/jsruntime/qv4compilationunitmapper_win.cpp +++ b/src/qml/jsruntime/qv4compilationunitmapper_win.cpp @@ -1,48 +1,14 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4compilationunitmapper_p.h" -#include "qv4executablecompilationunit_p.h" -#include <QScopeGuard> -#include <QFileInfo> -#include <QDateTime> +#include <private/qv4compileddata_p.h> + +#include <QtCore/qdatetime.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qscopeguard.h> + #include <qt_windows.h> QT_BEGIN_NAMESPACE @@ -56,16 +22,10 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co // ### TODO: fix up file encoding/normalization/unc handling once QFileSystemEntry // is exported from QtCore. HANDLE handle = -#if defined(Q_OS_WINRT) - CreateFile2(reinterpret_cast<const wchar_t*>(cacheFileName.constData()), - GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, - OPEN_EXISTING, nullptr); -#else CreateFile(reinterpret_cast<const wchar_t*>(cacheFileName.constData()), GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); -#endif if (handle == INVALID_HANDLE_VALUE) { *errorString = qt_error_string(GetLastError()); return nullptr; @@ -87,11 +47,28 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co return nullptr; } - if (!ExecutableCompilationUnit::verifyHeader(&header, sourceTimeStamp, errorString)) + if (!header.verifyHeader(sourceTimeStamp, errorString)) return nullptr; // Data structure and qt version matched, so now we can access the rest of the file safely. + /* Error out early on file corruption. We assume we can read header.unitSize bytes + later (even before verifying the checksum), potentially causing out-of-bound + reads + Also, no need to wait until checksum verification if we know beforehand + that the cached unit is bogus + */ + LARGE_INTEGER fileSize; + if (!GetFileSizeEx(handle, &fileSize)) { + *errorString = QStringLiteral("Could not determine file size"); + return nullptr; + } + if (header.unitSize != fileSize.QuadPart) { + *errorString = QStringLiteral("Potential file corruption, file too small"); + return nullptr; + } + + HANDLE fileMappingHandle = CreateFileMapping(handle, 0, PAGE_READONLY, 0, 0, 0); if (!fileMappingHandle) { *errorString = qt_error_string(GetLastError()); diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 018571e325..01f9b4adf3 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -1,53 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QString> -#include "qv4debugging_p.h" #include <qv4context_p.h> #include <qv4object_p.h> #include <qv4objectproto_p.h> #include <private/qv4mm_p.h> #include <qv4argumentsobject_p.h> #include "qv4function_p.h" -#include "qv4errorobject_p.h" -#include "qv4string_p.h" -#include "qv4qmlcontext_p.h" #include "qv4stackframe_p.h" #include "qv4symbol_p.h" @@ -71,8 +31,13 @@ Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int b Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m()); c->outer.set(v4, outer); - c->function.set(v4, static_cast<Heap::FunctionObject *>( - Value::fromStaticValue(frame->jsFrame->function).m())); + if (frame->isJSTypesFrame()) { + c->function.set(v4, static_cast<Heap::JavaScriptFunctionObject *>( + Value::fromStaticValue( + static_cast<JSTypesStackFrame *>(frame)->jsFrame->function).m())); + } else { + c->function.set(v4, nullptr); + } c->locals.size = nLocals; c->locals.alloc = nLocals; @@ -95,12 +60,12 @@ Heap::CallContext *ExecutionContext::cloneBlockContext(ExecutionEngine *engine, return c; } -Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame) +Heap::CallContext *ExecutionContext::newCallContext(JSTypesStackFrame *frame) { Function *function = frame->v4Function; Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m()); - uint nFormals = qMax(static_cast<uint>(frame->originalArgumentsCount), function->nFormals); + uint nFormals = qMax(static_cast<uint>(frame->argc()), function->nFormals); uint localsAndFormals = function->compiledFunction->nLocals + nFormals; size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * (localsAndFormals); @@ -109,7 +74,7 @@ Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame) c->init(); c->outer.set(v4, outer); - c->function.set(v4, static_cast<Heap::FunctionObject *>( + c->function.set(v4, static_cast<Heap::JavaScriptFunctionObject *>( Value::fromStaticValue(frame->jsFrame->function).m())); const CompiledData::Function *compiledFunction = function->compiledFunction; @@ -122,9 +87,9 @@ Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame) c->setupLocalTemporalDeadZone(compiledFunction); Value *args = c->locals.values + nLocals; - ::memcpy(args, frame->originalArguments, frame->originalArgumentsCount * sizeof(Value)); - c->nArgs = frame->originalArgumentsCount; - for (uint i = frame->originalArgumentsCount; i < function->nFormals; ++i) + ::memcpy(args, frame->argv(), frame->argc() * sizeof(Value)); + c->nArgs = frame->argc(); + for (uint i = frame->argc(); i < function->nFormals; ++i) args[i] = Encode::undefined(); return c; @@ -334,9 +299,14 @@ ReturnedValue ExecutionContext::getProperty(String *name) case Heap::ExecutionContext::Type_CallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); - uint index = c->internalClass->indexOfValueOrGetter(id); - if (index < UINT_MAX) + const uint index = c->internalClass->indexOfValueOrGetter(id); + if (index < c->locals.alloc) return c->locals[index].asReturnedValue(); + + // TODO: We should look up the module imports here, but those are part of the CU: + // imports[index - c->locals.size]; + // See QTBUG-118478 + Q_FALLTHROUGH(); } case Heap::ExecutionContext::Type_WithContext: @@ -384,9 +354,14 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) case Heap::ExecutionContext::Type_CallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); - uint index = c->internalClass->indexOfValueOrGetter(id); - if (index < UINT_MAX) + const uint index = c->internalClass->indexOfValueOrGetter(id); + if (index < c->locals.alloc) return c->locals[index].asReturnedValue(); + + // TODO: We should look up the module imports here, but those are part of the CU: + // imports[index - c->locals.size]; + // See QTBUG-118478 + Q_FALLTHROUGH(); } case Heap::ExecutionContext::Type_GlobalContext: { diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index 75fa2d08e6..48b6e04025 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QMLJS_ENVIRONMENT_H #define QMLJS_ENVIRONMENT_H @@ -65,7 +29,7 @@ namespace Heap { Member(class, Pointer, Object *, activation) DECLARE_HEAP_OBJECT(ExecutionContext, Base) { - DECLARE_MARKOBJECTS(ExecutionContext); + DECLARE_MARKOBJECTS(ExecutionContext) enum ContextType { Type_GlobalContext = 0x1, @@ -92,7 +56,7 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) { quint8 padding_[4]; #endif }; -Q_STATIC_ASSERT(std::is_trivial< ExecutionContext >::value); +Q_STATIC_ASSERT(std::is_trivial_v<ExecutionContext>); Q_STATIC_ASSERT(sizeof(ExecutionContext) == sizeof(Base) + sizeof(ExecutionContextData) + QT_POINTER_SIZE); Q_STATIC_ASSERT(std::is_standard_layout<ExecutionContextData>::value); @@ -100,11 +64,11 @@ Q_STATIC_ASSERT(offsetof(ExecutionContextData, outer) == 0); Q_STATIC_ASSERT(offsetof(ExecutionContextData, activation) == offsetof(ExecutionContextData, outer) + QT_POINTER_SIZE); #define CallContextMembers(class, Member) \ - Member(class, Pointer, FunctionObject *, function) \ + Member(class, Pointer, JavaScriptFunctionObject *, function) \ Member(class, ValueArray, ValueArray, locals) DECLARE_HEAP_OBJECT(CallContext, ExecutionContext) { - DECLARE_MARKOBJECTS(CallContext); + DECLARE_MARKOBJECTS(CallContext) void init() { @@ -125,7 +89,7 @@ DECLARE_HEAP_OBJECT(CallContext, ExecutionContext) { locals.values[i] = Value::emptyValue(); } }; -Q_STATIC_ASSERT(std::is_trivial< CallContext >::value); +Q_STATIC_ASSERT(std::is_trivial_v<CallContext>); Q_STATIC_ASSERT(std::is_standard_layout<CallContextData>::value); Q_STATIC_ASSERT(offsetof(CallContextData, function) == 0); //### The following size check fails on Win8. With the ValueArray at the end of the @@ -152,7 +116,7 @@ struct Q_QML_EXPORT ExecutionContext : public Managed static Heap::CallContext *newBlockContext(QV4::CppStackFrame *frame, int blockIndex); static Heap::CallContext *cloneBlockContext(ExecutionEngine *engine, Heap::CallContext *callContext); - static Heap::CallContext *newCallContext(QV4::CppStackFrame *frame); + static Heap::CallContext *newCallContext(JSTypesStackFrame *frame); Heap::ExecutionContext *newWithContext(Heap::Object *with) const; static Heap::ExecutionContext *newCatchContext(CppStackFrame *frame, int blockIndex, Heap::String *exceptionVarName); diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp index 5ab8cf2dcb..689eb9232b 100644 --- a/src/qml/jsruntime/qv4dataview.cpp +++ b/src/qml/jsruntime/qv4dataview.cpp @@ -1,45 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4dataview_p.h" #include "qv4arraybuffer_p.h" -#include "qv4string_p.h" #include "qv4symbol_p.h" #include <QtCore/private/qnumeric_p.h> @@ -50,9 +13,9 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(DataViewCtor); DEFINE_OBJECT_VTABLE(DataView); -void Heap::DataViewCtor::init(QV4::ExecutionContext *scope) +void Heap::DataViewCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("DataView")); + Heap::FunctionObject::init(engine, QStringLiteral("DataView")); } static uint toIndex(ExecutionEngine *e, const Value &v) @@ -79,20 +42,20 @@ ReturnedValue DataViewCtor::virtualCallAsConstructor(const FunctionObject *f, co if (!newTarget || !buffer) return scope.engine->throwTypeError(); - uint offset = ::toIndex(scope.engine, argc > 1 ? argv[1]: Value::undefinedValue()); + uint offset = ::toIndex(scope.engine, argc > 1 ? argv[1] : Value::undefinedValue()); if (scope.hasException()) return Encode::undefined(); - if (buffer->isDetachedBuffer()) + if (buffer->hasDetachedArrayData()) return scope.engine->throwTypeError(); - uint bufferLength = buffer->d()->data->size; + uint bufferLength = buffer->arrayDataLength(); if (offset > bufferLength) return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range")); uint byteLength = (argc < 3 || argv[2].isUndefined()) ? (bufferLength - offset) : ::toIndex(scope.engine, argv[2]); if (scope.hasException()) return Encode::undefined(); - if (offset + byteLength > bufferLength) + if (offset > bufferLength || byteLength > bufferLength - offset) return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range")); Scoped<DataView> a(scope, scope.engine->memoryManager->allocate<DataView>()); @@ -163,7 +126,7 @@ ReturnedValue DataViewPrototype::method_get_byteLength(const FunctionObject *b, if (!v) return b->engine()->throwTypeError(); - if (v->d()->buffer->isDetachedBuffer()) + if (v->d()->buffer->hasDetachedArrayData()) return b->engine()->throwTypeError(); return Encode(v->d()->byteLength); @@ -175,7 +138,7 @@ ReturnedValue DataViewPrototype::method_get_byteOffset(const FunctionObject *b, if (!v) return b->engine()->throwTypeError(); - if (v->d()->buffer->isDetachedBuffer()) + if (v->d()->buffer->hasDetachedArrayData()) return b->engine()->throwTypeError(); return Encode(v->d()->byteOffset); @@ -191,13 +154,13 @@ ReturnedValue DataViewPrototype::method_getChar(const FunctionObject *b, const V uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue()); if (e->hasException) return Encode::undefined(); - if (v->d()->buffer->isDetachedBuffer()) + if (v->d()->buffer->hasDetachedArrayData()) return e->throwTypeError(); if (idx + sizeof(T) > v->d()->byteLength) return e->throwRangeError(QStringLiteral("index out of range")); idx += v->d()->byteOffset; - T t = T(v->d()->buffer->data->data()[idx]); + T t = T(v->d()->buffer->constArrayData()[idx]); return Encode((int)t); } @@ -212,7 +175,7 @@ ReturnedValue DataViewPrototype::method_get(const FunctionObject *b, const Value uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue()); if (e->hasException) return Encode::undefined(); - if (v->d()->buffer->isDetachedBuffer()) + if (v->d()->buffer->hasDetachedArrayData()) return e->throwTypeError(); if (idx + sizeof(T) > v->d()->byteLength) return e->throwRangeError(QStringLiteral("index out of range")); @@ -221,8 +184,8 @@ ReturnedValue DataViewPrototype::method_get(const FunctionObject *b, const Value bool littleEndian = argc < 2 ? false : argv[1].toBoolean(); T t = littleEndian - ? qFromLittleEndian<T>((uchar *)v->d()->buffer->data->data() + idx) - : qFromBigEndian<T>((uchar *)v->d()->buffer->data->data() + idx); + ? qFromLittleEndian<T>((const uchar *)v->d()->buffer->constArrayData() + idx) + : qFromBigEndian<T>((const uchar *)v->d()->buffer->constArrayData() + idx); return Encode(t); } @@ -237,7 +200,7 @@ ReturnedValue DataViewPrototype::method_getFloat(const FunctionObject *b, const uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue()); if (e->hasException) return Encode::undefined(); - if (v->d()->buffer->isDetachedBuffer()) + if (v->d()->buffer->hasDetachedArrayData()) return e->throwTypeError(); if (idx + sizeof(T) > v->d()->byteLength) return e->throwRangeError(QStringLiteral("index out of range")); @@ -252,8 +215,8 @@ ReturnedValue DataViewPrototype::method_getFloat(const FunctionObject *b, const float f; } u; u.i = littleEndian - ? qFromLittleEndian<uint>((uchar *)v->d()->buffer->data->data() + idx) - : qFromBigEndian<uint>((uchar *)v->d()->buffer->data->data() + idx); + ? qFromLittleEndian<uint>((const uchar *)v->d()->buffer->constArrayData() + idx) + : qFromBigEndian<uint>((const uchar *)v->d()->buffer->constArrayData() + idx); return Encode(u.f); } else { Q_ASSERT(sizeof(T) == 8); @@ -262,8 +225,8 @@ ReturnedValue DataViewPrototype::method_getFloat(const FunctionObject *b, const double d; } u; u.i = littleEndian - ? qFromLittleEndian<quint64>((uchar *)v->d()->buffer->data->data() + idx) - : qFromBigEndian<quint64>((uchar *)v->d()->buffer->data->data() + idx); + ? qFromLittleEndian<quint64>((const uchar *)v->d()->buffer->constArrayData() + idx) + : qFromBigEndian<quint64>((const uchar *)v->d()->buffer->constArrayData() + idx); return Encode(u.d); } } @@ -281,14 +244,14 @@ ReturnedValue DataViewPrototype::method_setChar(const FunctionObject *b, const V int val = argc >= 2 ? argv[1].toInt32() : 0; - if (v->d()->buffer->isDetachedBuffer()) + if (v->d()->buffer->hasDetachedArrayData()) return e->throwTypeError(); if (idx + sizeof(T) > v->d()->byteLength) return e->throwRangeError(QStringLiteral("index out of range")); idx += v->d()->byteOffset; - v->d()->buffer->data->data()[idx] = (char)val; + v->d()->buffer->arrayData()[idx] = (char)val; RETURN_UNDEFINED(); } @@ -307,7 +270,7 @@ ReturnedValue DataViewPrototype::method_set(const FunctionObject *b, const Value int val = argc >= 2 ? argv[1].toInt32() : 0; bool littleEndian = argc < 3 ? false : argv[2].toBoolean(); - if (v->d()->buffer->isDetachedBuffer()) + if (v->d()->buffer->hasDetachedArrayData()) return e->throwTypeError(); if (idx + sizeof(T) > v->d()->byteLength) @@ -316,9 +279,9 @@ ReturnedValue DataViewPrototype::method_set(const FunctionObject *b, const Value if (littleEndian) - qToLittleEndian<T>(val, (uchar *)v->d()->buffer->data->data() + idx); + qToLittleEndian<T>(val, (uchar *)v->d()->buffer->arrayData() + idx); else - qToBigEndian<T>(val, (uchar *)v->d()->buffer->data->data() + idx); + qToBigEndian<T>(val, (uchar *)v->d()->buffer->arrayData() + idx); RETURN_UNDEFINED(); } @@ -337,7 +300,7 @@ ReturnedValue DataViewPrototype::method_setFloat(const FunctionObject *b, const double val = argc >= 2 ? argv[1].toNumber() : qt_qnan(); bool littleEndian = argc < 3 ? false : argv[2].toBoolean(); - if (v->d()->buffer->isDetachedBuffer()) + if (v->d()->buffer->hasDetachedArrayData()) return e->throwTypeError(); if (idx + sizeof(T) > v->d()->byteLength) @@ -352,9 +315,9 @@ ReturnedValue DataViewPrototype::method_setFloat(const FunctionObject *b, const } u; u.f = val; if (littleEndian) - qToLittleEndian(u.i, (uchar *)v->d()->buffer->data->data() + idx); + qToLittleEndian(u.i, (uchar *)v->d()->buffer->arrayData() + idx); else - qToBigEndian(u.i, (uchar *)v->d()->buffer->data->data() + idx); + qToBigEndian(u.i, (uchar *)v->d()->buffer->arrayData() + idx); } else { Q_ASSERT(sizeof(T) == 8); union { @@ -363,9 +326,9 @@ ReturnedValue DataViewPrototype::method_setFloat(const FunctionObject *b, const } u; u.d = val; if (littleEndian) - qToLittleEndian(u.i, (uchar *)v->d()->buffer->data->data() + idx); + qToLittleEndian(u.i, (uchar *)v->d()->buffer->arrayData() + idx); else - qToBigEndian(u.i, (uchar *)v->d()->buffer->data->data() + idx); + qToBigEndian(u.i, (uchar *)v->d()->buffer->arrayData() + idx); } RETURN_UNDEFINED(); } diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h index 199a6f9f80..b5fa41d964 100644 --- a/src/qml/jsruntime/qv4dataview_p.h +++ b/src/qml/jsruntime/qv4dataview_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4DATAVIEW_H #define QV4DATAVIEW_H @@ -60,7 +24,7 @@ namespace QV4 { namespace Heap { struct DataViewCtor : FunctionObject { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; #define DataViewMembers(class, Member) \ @@ -69,7 +33,7 @@ struct DataViewCtor : FunctionObject { Member(class, NoMark, uint, byteOffset) DECLARE_HEAP_OBJECT(DataView, Object) { - DECLARE_MARKOBJECTS(DataView); + DECLARE_MARKOBJECTS(DataView) void init() { Object::init(); } }; diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index cc89947cec..2cb020e495 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -1,83 +1,18 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4dateobject_p.h" -#include "qv4objectproto_p.h" -#include "qv4scopedvalue_p.h" #include "qv4runtime_p.h" -#include "qv4string_p.h" -#include "qv4jscall_p.h" #include "qv4symbol_p.h" #include <QtCore/QDebug> #include <QtCore/QDateTime> +#include <QtCore/private/qlocaltime_p.h> #include <QtCore/QStringList> - -#include <time.h> +#include <QtCore/QTimeZone> #include <wtf/MathExtras.h> -#if defined(Q_OS_LINUX) && QT_CONFIG(timezone) && !defined(Q_OS_ANDROID) -/* - See QTBUG-56899. Although we don't (yet) have a proper way to reset the - system zone, the code below, that uses QTimeZone::systemTimeZone(), works - adequately on Linux, when the TZ environment variable is changed. - */ -#define USE_QTZ_SYSTEM_ZONE -#endif - -#ifdef USE_QTZ_SYSTEM_ZONE -#include <QtCore/QTimeZone> -#else -# ifdef Q_OS_WIN -# include <windows.h> -# else -# ifndef Q_OS_VXWORKS -# include <sys/time.h> -# else -# include "qplatformdefs.h" -# endif -# include <unistd.h> // for _POSIX_THREAD_SAFE_FUNCTIONS -# endif -#endif // USE_QTZ_SYSTEM_ZONE - using namespace QV4; static const double HoursPerDay = 24.0; @@ -323,7 +258,6 @@ static inline double MakeDate(double day, double time) return day * msPerDay + time; } -#ifdef USE_QTZ_SYSTEM_ZONE /* ECMAScript specifies use of a fixed (current, standard) time-zone offset, LocalTZA; and LocalTZA + DaylightSavingTA(t) is taken to be (see LocalTime and @@ -339,37 +273,12 @@ static inline double MakeDate(double day, double time) mean a whole day of DST offset for some zones, that have crossed the international date line. This shall confuse client code.) The bug report against the ECMAScript spec is https://github.com/tc39/ecma262/issues/725 + and they've now changed the spec so that the following conforms to it ;^> */ - static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC time { - return QTimeZone::systemTimeZone().offsetFromUtc( - QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC)) * 1e3 - localTZA; -} -#else -// This implementation fails to take account of past changes in standard offset. -static inline double DaylightSavingTA(double t, double /*localTZA*/) -{ - struct tm tmtm; -#if defined(Q_CC_MSVC) - __time64_t tt = (__time64_t)(t / msPerSecond); - // _localtime_64_s returns non-zero on failure - if (_localtime64_s(&tmtm, &tt) != 0) -#elif !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) - long int tt = (long int)(t / msPerSecond); - if (!localtime_r((const time_t*) &tt, &tmtm)) -#else - // Returns shared static data which may be overwritten at any time - // (for MinGW/Windows localtime is luckily thread safe) - long int tt = (long int)(t / msPerSecond); - if (struct tm *tmtm_p = localtime((const time_t*) &tt)) - tmtm = *tmtm_p; - else -#endif - return 0; - return (tmtm.tm_isdst > 0) ? msPerHour : 0; + return QLocalTime::getUtcOffset(qint64(t)) * 1e3 - localTZA; } -#endif // USE_QTZ_SYSTEM_ZONE static inline double LocalTime(double t, double localTZA) { @@ -393,7 +302,7 @@ static inline double currentTime() static inline double TimeClip(double t) { - if (! qt_is_finite(t) || fabs(t) > 8.64e15) + if (!qt_is_finite(t) || fabs(t) > Date::MaxDateVal) return qt_qnan(); // +0 looks weird, but is correct. See ES6 20.3.1.15. We must not return -0. @@ -406,14 +315,14 @@ static inline double ParseString(const QString &s, double localTZA) First, try the format defined in ECMA 262's "Date Time String Format"; only if that fails, fall back to QDateTime for parsing - The defined string format is YYYY-MM-DDTHH:mm:ss.sssZ; the time (T and all - after it) may be omitted; in each part, the second and later components - are optional; and there's an extended syntax for negative and large - positive years: +/-YYYYYY; the leading sign, even when +, isn't optional. - If month or day is omitted, it is 01; if minute or second is omitted, it's - 00; if milliseconds are omitted, they're 000. + The defined string format is yyyy-MM-ddTHH:mm:ss.zzzt; the time (T and all + after it) may be omitted. In each part, the second and later components + are optional. There's an extended syntax for negative and large positive + years: ±yyyyyy; the leading sign, even when +, isn't optional. If month + (MM) or day (dd) is omitted, it is 01; if minute (mm) or second (ss) is + omitted, it's 00; if milliseconds (zzz) are omitted, they're 000. - When the time zone offset is absent, date-only forms are interpreted as + When the time zone offset (t) is absent, date-only forms are interpreted as indicating a UTC time and date-time forms are interpreted in local time. */ @@ -431,7 +340,7 @@ static inline double ParseString(const QString &s, double localTZA) }; const QChar *ch = s.constData(); - const QChar *end = ch + s.length(); + const QChar *end = ch + s.size(); uint format = Year; int current = 0; @@ -452,16 +361,16 @@ static inline double ParseString(const QString &s, double localTZA) bool seenZ = false; // Have seen zone, i.e. +HH:mm or literal Z. bool error = false; - if (*ch == '+' || *ch == '-') { + if (*ch == u'+' || *ch == u'-') { extendedYear = true; - if (*ch == '-') + if (*ch == u'-') yearSign = -1; ++ch; } for (; ch <= end && !error && format != Done; ++ch) { - if (*ch >= '0' && *ch <= '9') { + if (*ch >= u'0' && *ch <= u'9') { current *= 10; - current += ch->unicode() - '0'; + current += ch->unicode() - u'0'; ++currentSize; } else { // other char, delimits field switch (format) { @@ -507,12 +416,12 @@ static inline double ParseString(const QString &s, double localTZA) error = (currentSize != 2) || current >= 60; break; } - if (*ch == 'T') { + if (*ch == u'T') { if (format >= Hour) error = true; format = Hour; seenT = true; - } else if (*ch == '-') { + } else if (*ch == u'-') { if (format < Day) ++format; else if (format < Minute) @@ -524,19 +433,19 @@ static inline double ParseString(const QString &s, double localTZA) offsetSign = -1; format = TimezoneHour; } - } else if (*ch == ':') { + } else if (*ch == u':') { if (format != Hour && format != Minute && format != TimezoneHour) error = true; ++format; - } else if (*ch == '.') { + } else if (*ch == u'.') { if (format != Second) error = true; ++format; - } else if (*ch == '+') { + } else if (*ch == u'+') { if (seenZ || format < Minute || format >= TimezoneHour) error = true; format = TimezoneHour; - } else if (*ch == 'Z') { + } else if (*ch == u'Z') { if (seenZ || format < Minute || format >= TimezoneHour) error = true; else @@ -614,13 +523,16 @@ static inline double ParseString(const QString &s, double localTZA) QStringLiteral("d MMMM, yyyy"), QStringLiteral("d MMMM, yyyy hh:mm"), QStringLiteral("d MMMM, yyyy hh:mm:ss"), + + // ISO 8601 and RFC 2822 with a GMT as prefix on its offset, or GMT as zone. + QStringLiteral("yyyy-MM-dd hh:mm:ss t"), + QStringLiteral("ddd, d MMM yyyy hh:mm:ss t"), }; for (const QString &format : formats) { dt = format.indexOf(QLatin1String("hh:mm")) < 0 - ? QDateTime(QDate::fromString(s, format), - QTime(0, 0, 0), Qt::UTC) - : QDateTime::fromString(s, format); // as local time + ? QDate::fromString(s, format).startOfDay(QTimeZone::UTC) + : QDateTime::fromString(s, format); // as local time if (dt.isValid()) break; } @@ -633,21 +545,21 @@ static inline double ParseString(const QString &s, double localTZA) /*! \internal - Converts the ECMA Date value \tt (in UTC form) to QDateTime + Converts the ECMA Date value \a t (in UTC form) to QDateTime according to \a spec. */ -static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec) +static inline QDateTime ToDateTime(double t, QTimeZone zone) { if (std::isnan(t)) - return QDateTime(); - return QDateTime::fromMSecsSinceEpoch(t, Qt::UTC).toTimeSpec(spec); + return QDateTime().toTimeZone(zone); + return QDateTime::fromMSecsSinceEpoch(t, zone); } static inline QString ToString(double t, double localTZA) { if (std::isnan(t)) return QStringLiteral("Invalid Date"); - QString str = ToDateTime(t, Qt::LocalTime).toString() + QLatin1String(" GMT"); + QString str = ToDateTime(t, QTimeZone::LocalTime).toString() + QLatin1String(" GMT"); double tzoffset = localTZA + DaylightSavingTA(t, localTZA); if (tzoffset) { int hours = static_cast<int>(::fabs(tzoffset) / 1000 / 60 / 60); @@ -667,74 +579,78 @@ static inline QString ToUTCString(double t) { if (std::isnan(t)) return QStringLiteral("Invalid Date"); - return ToDateTime(t, Qt::UTC).toString(); + return ToDateTime(t, QTimeZone::UTC).toString(); } static inline QString ToDateString(double t) { - return ToDateTime(t, Qt::LocalTime).date().toString(); + return ToDateTime(t, QTimeZone::LocalTime).date().toString(); } static inline QString ToTimeString(double t) { - return ToDateTime(t, Qt::LocalTime).time().toString(); + return ToDateTime(t, QTimeZone::LocalTime).time().toString(); } static inline QString ToLocaleString(double t) { - return ToDateTime(t, Qt::LocalTime).toString(Qt::DefaultLocaleShortDate); + return QLocale().toString(ToDateTime(t, QTimeZone::LocalTime), QLocale::ShortFormat); } static inline QString ToLocaleDateString(double t) { - return ToDateTime(t, Qt::LocalTime).date().toString(Qt::DefaultLocaleShortDate); + return QLocale().toString(ToDateTime(t, QTimeZone::LocalTime).date(), QLocale::ShortFormat); } static inline QString ToLocaleTimeString(double t) { - return ToDateTime(t, Qt::LocalTime).time().toString(Qt::DefaultLocaleShortDate); + return QLocale().toString(ToDateTime(t, QTimeZone::LocalTime).time(), QLocale::ShortFormat); } static double getLocalTZA() { -#ifndef Q_OS_WIN - tzset(); -#endif -#ifdef USE_QTZ_SYSTEM_ZONE - // TODO: QTimeZone::resetSystemTimeZone(), see QTBUG-56899 and comment above. - // Standard offset, with no daylight-savings adjustment, in ms: - return QTimeZone::systemTimeZone().standardTimeOffset(QDateTime::currentDateTime()) * 1e3; -#else -# ifdef Q_OS_WIN - TIME_ZONE_INFORMATION tzInfo; - GetTimeZoneInformation(&tzInfo); - return -tzInfo.Bias * 60.0 * 1000.0; -# else - struct tm t; - time_t curr; - time(&curr); - localtime_r(&curr, &t); // Wrong: includes DST offset - time_t locl = mktime(&t); - gmtime_r(&curr, &t); - time_t globl = mktime(&t); - return (double(locl) - double(globl)) * 1000.0; -# endif -#endif // USE_QTZ_SYSTEM_ZONE + return QLocalTime::getCurrentStandardUtcOffset() * 1e3; } DEFINE_OBJECT_VTABLE(DateObject); -void Heap::DateObject::init(const QDateTime &date) +quint64 Date::encode(double value) +{ + if (std::isnan(value) || fabs(value) > MaxDateVal) + return InvalidDateVal; + + // Do the addition in qint64. This way we won't overflow if value is negative + // and we will round value in the right direction. + // We know we can do this because we know we have more than one bit left in quint64. + const quint64 encoded = quint64(qint64(value) + qint64(MaxDateVal)); + + return encoded + Extra; +} + +quint64 Date::encode(const QDateTime &dateTime) +{ + return encode(dateTime.isValid() ? dateTime.toMSecsSinceEpoch() : qt_qnan()); +} + +void Date::init(double value) +{ + storage = encode(value); +} + +void Date::init(const QDateTime &when) +{ + storage = encode(when) | HasQDate | HasQTime; +} + +void Date::init(QDate date) { - Object::init(); - this->date = date.isValid() ? TimeClip(date.toMSecsSinceEpoch()) : qt_qnan(); + storage = encode(date.startOfDay(QTimeZone::UTC)) | HasQDate; } -void Heap::DateObject::init(const QTime &time) +void Date::init(QTime time, ExecutionEngine *engine) { - Object::init(); if (!time.isValid()) { - date = qt_qnan(); + storage = encode(qt_qnan()) | HasQTime; return; } @@ -754,19 +670,105 @@ void Heap::DateObject::init(const QTime &time) */ static const double d = MakeDay(1971, 3, 1); double t = MakeTime(time.hour(), time.minute(), time.second(), time.msec()); - date = TimeClip(UTC(MakeDate(d, t), internalClass->engine->localTZA)); + storage = encode(UTC(MakeDate(d, t), engine->localTZA)) | HasQTime; +} + +QDate Date::toQDate() const +{ + return toQDateTime().date(); +} + +QTime Date::toQTime() const +{ + return toQDateTime().time(); +} + +QDateTime Date::toQDateTime() const +{ + return ToDateTime(operator double(), QTimeZone::LocalTime); +} + +QVariant Date::toVariant() const +{ + switch (storage & (HasQDate | HasQTime)) { + case HasQDate: + return toQDate(); + case HasQTime: + return toQTime(); + case (HasQDate | HasQTime): + return toQDateTime(); + default: + return QVariant(); + } } QDateTime DateObject::toQDateTime() const { - return ToDateTime(date(), Qt::LocalTime); + return d()->toQDateTime(); +} + +QString DateObject::toString() const +{ + return ToString(d()->date(), engine()->localTZA); +} + +QString DateObject::dateTimeToString(const QDateTime &dateTime, ExecutionEngine *engine) +{ + if (!dateTime.isValid()) + return QStringLiteral("Invalid Date"); + return ToString(TimeClip(dateTime.toMSecsSinceEpoch()), engine->localTZA); +} + +double DateObject::dateTimeToNumber(const QDateTime &dateTime) +{ + if (!dateTime.isValid()) + return qQNaN(); + return TimeClip(dateTime.toMSecsSinceEpoch()); +} + +QDateTime DateObject::stringToDateTime(const QString &string, ExecutionEngine *engine) +{ + return ToDateTime(ParseString(string, engine->localTZA), QTimeZone::LocalTime); +} + +QDateTime DateObject::timestampToDateTime(double timestamp, QTimeZone zone) +{ + return ToDateTime(timestamp, zone); +} + +double DateObject::componentsToTimestamp( + double year, double month, double day, double hours, + double mins, double secs, double ms, ExecutionEngine *v4) +{ + if (year >= 0 && year <= 99) + year += 1900; + const double t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); + return UTC(t, v4->localTZA); +} + +QDate DateObject::dateTimeToDate(const QDateTime &dateTime) +{ + // If the Date object was parse()d from a string with no time part + // or zone specifier it's really the UTC start of the relevant day, + // but it's here represented as a local time, which may fall in the + // preceding day. See QTBUG-92466 for the gory details. + const auto utc = dateTime.toUTC(); + if (utc.date() != dateTime.date() && utc.addSecs(-1).date() == dateTime.date()) + return utc.date(); + + // This may, of course, be The Wrong Thing if the date was + // constructed as a full local date-time that happens to coincide + // with the start of a UTC day; however, that would be an odd value + // to give to something that, apparently, someone thinks belongs in + // a QDate. + return dateTime.date(); } DEFINE_OBJECT_VTABLE(DateCtor); -void Heap::DateCtor::init(QV4::ExecutionContext *scope) +void Heap::DateCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("Date")); + Heap::FunctionObject::init(engine, QStringLiteral("Date")); } ReturnedValue DateCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *newTarget) @@ -788,25 +790,22 @@ ReturnedValue DateCtor::virtualCallAsConstructor(const FunctionObject *that, con if (String *s = arg->stringValue()) t = ParseString(s->toQString(), v4->localTZA); else - t = TimeClip(arg->toNumber()); + t = arg->toNumber(); } } else { // d.argc > 1 - double year = argv[0].toNumber(); - double month = argv[1].toNumber(); - double day = argc >= 3 ? argv[2].toNumber() : 1; - double hours = argc >= 4 ? argv[3].toNumber() : 0; - double mins = argc >= 5 ? argv[4].toNumber() : 0; - double secs = argc >= 6 ? argv[5].toNumber() : 0; - double ms = argc >= 7 ? argv[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, v4->localTZA)); + const double year = argv[0].toNumber(); + const double month = argv[1].toNumber(); + const double day = argc >= 3 ? argv[2].toNumber() : 1; + const double hours = argc >= 4 ? argv[3].toNumber() : 0; + const double mins = argc >= 5 ? argv[4].toNumber() : 0; + const double secs = argc >= 6 ? argv[5].toNumber() : 0; + const double ms = argc >= 7 ? argv[6].toNumber() : 0; + t = DateObject::componentsToTimestamp(year, month, day, hours, mins, secs, ms, v4); } - ReturnedValue o = Encode(v4->newDateObject(Value::fromDouble(t))); + ReturnedValue o = Encode(v4->newDateObject(t)); if (!newTarget) return o; Scope scope(v4); @@ -1179,7 +1178,7 @@ ReturnedValue DatePrototype::method_setTime(const FunctionObject *b, const Value double t = argc ? argv[0].toNumber() : qt_qnan(); if (v4->hasException) return QV4::Encode::undefined(); - self->setDate(TimeClip(t)); + self->setDate(t); return Encode(self->date()); } @@ -1196,7 +1195,7 @@ ReturnedValue DatePrototype::method_setMilliseconds(const FunctionObject *b, con double ms = argc ? argv[0].toNumber() : qt_qnan(); if (v4->hasException) return QV4::Encode::undefined(); - self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)), v4->localTZA))); + self->setDate(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)), v4->localTZA)); return Encode(self->date()); } @@ -1213,7 +1212,7 @@ ReturnedValue DatePrototype::method_setUTCMilliseconds(const FunctionObject *b, double ms = argc ? argv[0].toNumber() : qt_qnan(); if (v4->hasException) return QV4::Encode::undefined(); - self->setDate(TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); + self->setDate(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))); return Encode(self->date()); } @@ -1233,7 +1232,7 @@ ReturnedValue DatePrototype::method_setSeconds(const FunctionObject *b, const Va double ms = (argc < 2) ? msFromTime(t) : argv[1].toNumber(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)), v4->localTZA)); + t = UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)), v4->localTZA); self->setDate(t); return Encode(self->date()); } @@ -1248,7 +1247,7 @@ ReturnedValue DatePrototype::method_setUTCSeconds(const FunctionObject *b, const double t = self->date(); double sec = argc ? argv[0].toNumber() : qt_qnan(); double ms = (argc < 2) ? msFromTime(t) : argv[1].toNumber(); - t = TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms))); + t = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)); self->setDate(t); return Encode(self->date()); } @@ -1272,7 +1271,7 @@ ReturnedValue DatePrototype::method_setMinutes(const FunctionObject *b, const Va double ms = (argc < 3) ? msFromTime(t) : argv[2].toNumber(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)), v4->localTZA)); + t = UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)), v4->localTZA); self->setDate(t); return Encode(self->date()); } @@ -1288,7 +1287,7 @@ ReturnedValue DatePrototype::method_setUTCMinutes(const FunctionObject *b, const double min = argc ? argv[0].toNumber() : qt_qnan(); double sec = (argc < 2) ? SecFromTime(t) : argv[1].toNumber(); double ms = (argc < 3) ? msFromTime(t) : argv[2].toNumber(); - t = TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms))); + t = MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)); self->setDate(t); return Encode(self->date()); } @@ -1315,7 +1314,7 @@ ReturnedValue DatePrototype::method_setHours(const FunctionObject *b, const Valu double ms = (argc < 4) ? msFromTime(t) : argv[3].toNumber(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)), v4->localTZA)); + t = UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)), v4->localTZA); self->setDate(t); return Encode(self->date()); } @@ -1332,7 +1331,7 @@ ReturnedValue DatePrototype::method_setUTCHours(const FunctionObject *b, const V double min = (argc < 2) ? MinFromTime(t) : argv[1].toNumber(); double sec = (argc < 3) ? SecFromTime(t) : argv[2].toNumber(); double ms = (argc < 4) ? msFromTime(t) : argv[3].toNumber(); - t = TimeClip(MakeDate(Day(t), MakeTime(hour, min, sec, ms))); + t = MakeDate(Day(t), MakeTime(hour, min, sec, ms)); self->setDate(t); return Encode(self->date()); } @@ -1350,7 +1349,7 @@ ReturnedValue DatePrototype::method_setDate(const FunctionObject *b, const Value double date = argc ? argv[0].toNumber() : qt_qnan(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)), v4->localTZA)); + t = UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)), v4->localTZA); self->setDate(t); return Encode(self->date()); } @@ -1368,7 +1367,7 @@ ReturnedValue DatePrototype::method_setUTCDate(const FunctionObject *b, const Va double date = argc ? argv[0].toNumber() : qt_qnan(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t))); + t = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)); self->setDate(t); return Encode(self->date()); } @@ -1389,7 +1388,7 @@ ReturnedValue DatePrototype::method_setMonth(const FunctionObject *b, const Valu double date = (argc < 2) ? DateFromTime(t) : argv[1].toNumber(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)), v4->localTZA)); + t = UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)), v4->localTZA); self->setDate(t); return Encode(self->date()); } @@ -1404,7 +1403,7 @@ ReturnedValue DatePrototype::method_setUTCMonth(const FunctionObject *b, const V double t = self->date(); double month = argc ? argv[0].toNumber() : qt_qnan(); double date = (argc < 2) ? DateFromTime(t) : argv[1].toNumber(); - t = TimeClip(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t))); + t = MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)); self->setDate(t); return Encode(self->date()); } @@ -1430,7 +1429,6 @@ ReturnedValue DatePrototype::method_setYear(const FunctionObject *b, const Value year += 1900; r = MakeDay(year, MonthFromTime(t), DateFromTime(t)); r = UTC(MakeDate(r, TimeWithinDay(t)), v4->localTZA); - r = TimeClip(r); } self->setDate(r); return Encode(self->date()); @@ -1447,7 +1445,7 @@ ReturnedValue DatePrototype::method_setUTCFullYear(const FunctionObject *b, cons double year = argc ? argv[0].toNumber() : qt_qnan(); double month = (argc < 2) ? MonthFromTime(t) : argv[1].toNumber(); double date = (argc < 3) ? DateFromTime(t) : argv[2].toNumber(); - t = TimeClip(MakeDate(MakeDay(year, month, date), TimeWithinDay(t))); + t = MakeDate(MakeDay(year, month, date), TimeWithinDay(t)); self->setDate(t); return Encode(self->date()); } @@ -1473,7 +1471,7 @@ ReturnedValue DatePrototype::method_setFullYear(const FunctionObject *b, const V double date = (argc < 3) ? DateFromTime(t) : argv[2].toNumber(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)), v4->localTZA)); + t = UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)), v4->localTZA); self->setDate(t); return Encode(self->date()); } @@ -1561,7 +1559,7 @@ ReturnedValue DatePrototype::method_toJSON(const FunctionObject *b, const Value if (!toIso) return v4->throwTypeError(); - return toIso->call(O, nullptr, 0); + return checkedResult(v4, toIso->call(O, nullptr, 0)); } ReturnedValue DatePrototype::method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index a87eb92caf..4c184de897 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4DATEOBJECT_P_H #define QV4DATEOBJECT_P_H @@ -52,7 +16,9 @@ #include "qv4object_p.h" #include "qv4functionobject_p.h" +#include "qv4referenceobject_p.h" #include <QtCore/private/qnumeric_p.h> +#include <QtCore/qdatetime.h> QT_BEGIN_NAMESPACE @@ -60,43 +26,194 @@ class QDateTime; namespace QV4 { +struct Date +{ + static constexpr quint64 MaxDateVal = 8.64e15; + + void init() { storage = InvalidDateVal; } + void init(double value); + void init(const QDateTime &dateTime); + void init(QDate date); + void init(QTime time, ExecutionEngine *engine); + + Date &operator=(double value) + { + storage = (storage & (HasQDate | HasQTime)) | encode(value); + return *this; + } + + operator double() const + { + const quint64 raw = (storage & ~(HasQDate | HasQTime)); + if (raw == 0) + return qt_qnan(); + + if (raw > MaxDateVal) + return double(raw - MaxDateVal - Extra); + + return double(raw) - double(MaxDateVal) - double(Extra); + } + + QDate toQDate() const; + QTime toQTime() const; + QDateTime toQDateTime() const; + QVariant toVariant() const; + + template<typename Function> + bool withStoragePointer(Function function) + { + switch (storage & (HasQDate | HasQTime)) { + case HasQDate: { + QDate date = toQDate(); + return function(&date); + } + case HasQTime: { + QTime time = toQTime(); + return function(&time); + } + case (HasQTime | HasQDate): { + QDateTime dateTime = toQDateTime(); + return function(&dateTime); + } + default: + return false; + } + } + +private: + static constexpr quint64 InvalidDateVal = 0; + static constexpr quint64 Extra = 1; + static constexpr quint64 HasQDate = 1ull << 63; + static constexpr quint64 HasQTime = 1ull << 62; + + // Make all our dates fit into quint64, leaving space for the flags + static_assert(((MaxDateVal * 2 + Extra) & (HasQDate | HasQTime)) == 0ull); + + static quint64 encode(double value); + static quint64 encode(const QDateTime &dateTime); + + quint64 storage; +}; + namespace Heap { -struct DateObject : Object { +#define DateObjectMembers(class, Member) +DECLARE_HEAP_OBJECT(DateObject, ReferenceObject) { + DECLARE_MARKOBJECTS(DateObject); + + void doSetLocation() + { + if (CppStackFrame *frame = internalClass->engine->currentStackFrame) + setLocation(frame->v4Function, frame->statementNumber()); + } + void init() { - Object::init(); - date = qt_qnan(); + ReferenceObject::init(nullptr, -1, {}); + m_date.init(); + } + + void init(double dateTime) + { + ReferenceObject::init(nullptr, -1, {}); + m_date.init(dateTime); + } + + void init(const QDateTime &dateTime) + { + ReferenceObject::init(nullptr, -1, {}); + m_date.init(dateTime); } - void init(const Value &date) + void init(const QDateTime &dateTime, Heap::Object *parent, int property, Flags flags) + { + ReferenceObject::init(parent, property, flags | EnforcesLocation); + doSetLocation(); + m_date.init(dateTime); + }; + + void init(QDate date, Heap::Object *parent, int property, Flags flags) { - Object::init(); - this->date = date.toNumber(); + ReferenceObject::init(parent, property, flags | EnforcesLocation); + doSetLocation(); + m_date.init(date); + }; + + void init(QTime time, Heap::Object *parent, int property, Flags flags) + { + ReferenceObject::init(parent, property, flags | EnforcesLocation); + doSetLocation(); + m_date.init(time, internalClass->engine); + }; + + void setDate(double newDate) + { + m_date = newDate; + if (isAttachedToProperty()) + writeBack(); + } + + double date() const + { + return m_date; + } + + QVariant toVariant() const { return m_date.toVariant(); } + QDateTime toQDateTime() const { return m_date.toQDateTime(); } + +private: + bool writeBack() + { + if (!object() || !canWriteBack()) + return false; + + QV4::Scope scope(internalClass->engine); + QV4::ScopedObject o(scope, object()); + + int flags = 0; + int status = -1; + if (isVariant()) { + QVariant variant = toVariant(); + void *a[] = { &variant, nullptr, &status, &flags }; + return o->metacall(QMetaObject::WriteProperty, property(), a); + } + + return m_date.withStoragePointer([&](void *storagePointer) { + void *a[] = { storagePointer, nullptr, &status, &flags }; + return o->metacall(QMetaObject::WriteProperty, property(), a); + }); } - void init(const QDateTime &date); - void init(const QTime &time); - double date; + Date m_date; }; struct DateCtor : FunctionObject { - void init(QV4::ExecutionContext *scope); + void init(QV4::ExecutionEngine *engine); }; } -struct DateObject: Object { - V4_OBJECT2(DateObject, Object) +struct DateObject: ReferenceObject { + V4_OBJECT2(DateObject, ReferenceObject) Q_MANAGED_TYPE(DateObject) V4_PROTOTYPE(datePrototype) + void setDate(double date) { d()->setDate(date); } + double date() const { return d()->date(); } - double date() const { return d()->date; } - void setDate(double date) { d()->date = date; } + Q_QML_EXPORT QDateTime toQDateTime() const; + QString toString() const; - Q_QML_PRIVATE_EXPORT QDateTime toQDateTime() const; + static QString dateTimeToString(const QDateTime &dateTime, ExecutionEngine *engine); + static double dateTimeToNumber(const QDateTime &dateTime); + static QDate dateTimeToDate(const QDateTime &dateTime); + static QDateTime stringToDateTime(const QString &string, ExecutionEngine *engine); + static QDateTime timestampToDateTime(double timestamp, QTimeZone zone = QTimeZone::LocalTime); + static double componentsToTimestamp( + double year, double month, double day, + double hours, double mins, double secs, double ms, + ExecutionEngine *v4); }; template<> diff --git a/src/qml/jsruntime/qv4debugging.cpp b/src/qml/jsruntime/qv4debugging.cpp new file mode 100644 index 0000000000..57ddaaa2f1 --- /dev/null +++ b/src/qml/jsruntime/qv4debugging.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qv4debugging_p.h" + +#if QT_CONFIG(qml_debug) + +QT_BEGIN_NAMESPACE + +QV4::Debugging::Debugger::~Debugger() + = default; + +QT_END_NAMESPACE + +#include "moc_qv4debugging_p.cpp" + +#endif // QT_CONFIG(qml_debug) diff --git a/src/qml/jsruntime/qv4debugging_p.h b/src/qml/jsruntime/qv4debugging_p.h index 52263105fa..b80567b339 100644 --- a/src/qml/jsruntime/qv4debugging_p.h +++ b/src/qml/jsruntime/qv4debugging_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4DEBUGGING_H #define QV4DEBUGGING_H @@ -51,8 +15,8 @@ // We mean it. // -#include "qv4global_p.h" -#include <private/qv4staticvalue_p.h> +#include <QtQml/private/qv4global_p.h> +#include <QtQml/private/qv4staticvalue_p.h> #include <QtCore/qobject.h> QT_BEGIN_NAMESPACE @@ -79,7 +43,7 @@ class Q_QML_EXPORT Debugger : public QObject Q_OBJECT public: - ~Debugger() override {} + ~Debugger() override; virtual bool pauseAtNextOpportunity() const = 0; virtual void maybeBreakAtInstruction() = 0; virtual void enteringFunction() = 0; diff --git a/src/qml/jsruntime/qv4domerrors.cpp b/src/qml/jsruntime/qv4domerrors.cpp new file mode 100644 index 0000000000..cd26858aa3 --- /dev/null +++ b/src/qml/jsruntime/qv4domerrors.cpp @@ -0,0 +1,35 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qv4domerrors_p.h" +#include "qv4object_p.h" + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +void qt_add_domexceptions(ExecutionEngine *e) +{ + Scope scope(e); + ScopedObject domexception(scope, e->newObject()); + domexception->defineReadonlyProperty(QStringLiteral("INDEX_SIZE_ERR"), Value::fromInt32(DOMEXCEPTION_INDEX_SIZE_ERR)); + domexception->defineReadonlyProperty(QStringLiteral("DOMSTRING_SIZE_ERR"), Value::fromInt32(DOMEXCEPTION_DOMSTRING_SIZE_ERR)); + domexception->defineReadonlyProperty(QStringLiteral("HIERARCHY_REQUEST_ERR"), Value::fromInt32(DOMEXCEPTION_HIERARCHY_REQUEST_ERR)); + domexception->defineReadonlyProperty(QStringLiteral("WRONG_DOCUMENT_ERR"), Value::fromInt32(DOMEXCEPTION_WRONG_DOCUMENT_ERR)); + domexception->defineReadonlyProperty(QStringLiteral("INVALID_CHARACTER_ERR"), Value::fromInt32(DOMEXCEPTION_INVALID_CHARACTER_ERR)); + domexception->defineReadonlyProperty(QStringLiteral("NO_DATA_ALLOWED_ERR"), Value::fromInt32(DOMEXCEPTION_NO_DATA_ALLOWED_ERR)); + domexception->defineReadonlyProperty(QStringLiteral("NO_MODIFICATION_ALLOWED_ERR"), Value::fromInt32(DOMEXCEPTION_NO_MODIFICATION_ALLOWED_ERR)); + domexception->defineReadonlyProperty(QStringLiteral("NOT_FOUND_ERR"), Value::fromInt32(DOMEXCEPTION_NOT_FOUND_ERR)); + domexception->defineReadonlyProperty(QStringLiteral("NOT_SUPPORTED_ERR"), Value::fromInt32(DOMEXCEPTION_NOT_SUPPORTED_ERR)); + domexception->defineReadonlyProperty(QStringLiteral("INUSE_ATTRIBUTE_ERR"), Value::fromInt32(DOMEXCEPTION_INUSE_ATTRIBUTE_ERR)); + domexception->defineReadonlyProperty(QStringLiteral("INVALID_STATE_ERR"), Value::fromInt32(DOMEXCEPTION_INVALID_STATE_ERR)); + domexception->defineReadonlyProperty(QStringLiteral("SYNTAX_ERR"), Value::fromInt32(DOMEXCEPTION_SYNTAX_ERR)); + domexception->defineReadonlyProperty(QStringLiteral("INVALID_MODIFICATION_ERR"), Value::fromInt32(DOMEXCEPTION_INVALID_MODIFICATION_ERR)); + domexception->defineReadonlyProperty(QStringLiteral("NAMESPACE_ERR"), Value::fromInt32(DOMEXCEPTION_NAMESPACE_ERR)); + domexception->defineReadonlyProperty(QStringLiteral("INVALID_ACCESS_ERR"), Value::fromInt32(DOMEXCEPTION_INVALID_ACCESS_ERR)); + domexception->defineReadonlyProperty(QStringLiteral("VALIDATION_ERR"), Value::fromInt32(DOMEXCEPTION_VALIDATION_ERR)); + domexception->defineReadonlyProperty(QStringLiteral("TYPE_MISMATCH_ERR"), Value::fromInt32(DOMEXCEPTION_TYPE_MISMATCH_ERR)); + e->globalObject->defineDefaultProperty(QStringLiteral("DOMException"), domexception); +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4domerrors_p.h b/src/qml/jsruntime/qv4domerrors_p.h new file mode 100644 index 0000000000..2238418404 --- /dev/null +++ b/src/qml/jsruntime/qv4domerrors_p.h @@ -0,0 +1,57 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QV8DOMERRORS_P_H +#define QV8DOMERRORS_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/private/qglobal_p.h> + +QT_BEGIN_NAMESPACE +// From DOM-Level-3-Core spec +// http://www.w3.org/TR/DOM-Level-3-Core/core.html +#define DOMEXCEPTION_INDEX_SIZE_ERR 1 +#define DOMEXCEPTION_DOMSTRING_SIZE_ERR 2 +#define DOMEXCEPTION_HIERARCHY_REQUEST_ERR 3 +#define DOMEXCEPTION_WRONG_DOCUMENT_ERR 4 +#define DOMEXCEPTION_INVALID_CHARACTER_ERR 5 +#define DOMEXCEPTION_NO_DATA_ALLOWED_ERR 6 +#define DOMEXCEPTION_NO_MODIFICATION_ALLOWED_ERR 7 +#define DOMEXCEPTION_NOT_FOUND_ERR 8 +#define DOMEXCEPTION_NOT_SUPPORTED_ERR 9 +#define DOMEXCEPTION_INUSE_ATTRIBUTE_ERR 10 +#define DOMEXCEPTION_INVALID_STATE_ERR 11 +#define DOMEXCEPTION_SYNTAX_ERR 12 +#define DOMEXCEPTION_INVALID_MODIFICATION_ERR 13 +#define DOMEXCEPTION_NAMESPACE_ERR 14 +#define DOMEXCEPTION_INVALID_ACCESS_ERR 15 +#define DOMEXCEPTION_VALIDATION_ERR 16 +#define DOMEXCEPTION_TYPE_MISMATCH_ERR 17 + +#define THROW_DOM(error, string) { \ + QV4::ScopedValue v(scope, scope.engine->newString(QStringLiteral(string))); \ + QV4::ScopedObject ex(scope, scope.engine->newErrorObject(v)); \ + ex->put(QV4::ScopedString(scope, scope.engine->newIdentifier(QStringLiteral("code"))), QV4::ScopedValue(scope, QV4::Value::fromInt32(error))); \ + return scope.engine->throwError(ex); \ +} + +namespace QV4 { +struct ExecutionEngine; +} + + +void qt_add_domexceptions(QV4::ExecutionEngine *e); + +QT_END_NAMESPACE + +#endif // QV8DOMERRORS_P_H diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 590cebfa7c..6754c3c887 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <qv4engine_p.h> #include <private/qv4compileddata_p.h> @@ -50,6 +14,8 @@ #if QT_CONFIG(regularexpression) #include <QRegularExpression> #endif +#include <QtCore/QTimeZone> +#include <QtCore/qiterable.h> #include <qv4qmlcontext_p.h> #include <qv4value_p.h> @@ -89,13 +55,13 @@ #include "qv4reflect_p.h" #include "qv4proxy_p.h" #include "qv4stackframe_p.h" +#include "qv4stacklimits_p.h" #include "qv4atomics_p.h" - -#if QT_CONFIG(qml_sequence_object) +#include "qv4urlobject_p.h" +#include "qv4variantobject_p.h" #include "qv4sequenceobject_p.h" -#endif - #include "qv4qobjectwrapper_p.h" +#include "qv4qmetaobjectwrapper_p.h" #include "qv4memberdata_p.h" #include "qv4arraybuffer_p.h" #include "qv4dataview_p.h" @@ -118,71 +84,250 @@ #endif #include <private/qv4sqlerrors_p.h> #include <qqmlfile.h> +#include <qmetatype.h> +#include <qsequentialiterable.h> -#if USE(PTHREADS) -# include <pthread.h> -#if !defined(Q_OS_INTEGRITY) -# include <sys/resource.h> -#endif -#if HAVE(PTHREAD_NP_H) -# include <pthread_np.h> -#endif -#endif +#include <private/qqmlengine_p.h> #ifdef V4_USE_VALGRIND #include <valgrind/memcheck.h> #endif -Q_DECLARE_METATYPE(QList<int>) - QT_BEGIN_NAMESPACE +DEFINE_BOOL_CONFIG_OPTION(disableDiskCache, QML_DISABLE_DISK_CACHE); +DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE); + using namespace QV4; +// While engineSerial is odd the statics haven't been initialized. The engine that receives ID 1 +// initializes the statics and sets engineSerial to 2 afterwards. +// Each engine does engineSerial.fetchAndAddOrdered(2) on creation. Therefore engineSerial stays +// odd while the statics are being initialized, and stays even afterwards. +// Any further engines created while the statics are being initialized busy-wait until engineSerial +// is even. static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1); +int ExecutionEngine::s_maxCallDepth = -1; +int ExecutionEngine::s_jitCallCountThreshold = 3; +int ExecutionEngine::s_maxJSStackSize = 4 * 1024 * 1024; +int ExecutionEngine::s_maxGCStackSize = 2 * 1024 * 1024; ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const QV4::Value *, int) { return b->engine()->throwTypeError(); } -qint32 ExecutionEngine::maxCallDepth = -1; template <typename ReturnType> ReturnType convertJSValueToVariantType(const QJSValue &value) { - return value.toVariant().value<ReturnType>(); + const QVariant variant = value.toVariant(); + return variant.metaType() == QMetaType::fromType<QJSValue>() + ? ReturnType() + : variant.value<ReturnType>(); } -static void saveJSValue(QDataStream &stream, const void *data) +struct JSArrayIterator { + QJSValue const* data; + quint32 index; +}; + +namespace { +void createNewIteratorIfNonExisting(void **iterator) { + if (*iterator == nullptr) + *iterator = new JSArrayIterator; +} +} + +static QtMetaContainerPrivate::QMetaSequenceInterface emptySequenceInterface() { - const QJSValue *jsv = reinterpret_cast<const QJSValue *>(data); - quint32 isNullOrUndefined = 0; - if (jsv->isNull()) - isNullOrUndefined |= 0x1; - if (jsv->isUndefined()) - isNullOrUndefined |= 0x2; - stream << isNullOrUndefined; - if (!isNullOrUndefined) - reinterpret_cast<const QJSValue*>(data)->toVariant().save(stream); + // set up some functions so that non-array QSequentialIterables do not crash + // but instead appear as an empty sequence + + using namespace QtMetaContainerPrivate; + QMetaSequenceInterface iface; + iface.sizeFn = [](const void *) { return qsizetype(0); }; + iface.valueAtIndexFn = [](const void *, qsizetype, void *) {}; + iface.createIteratorFn = [](void *, QMetaSequenceInterface::Position) -> void * { + return nullptr; + }; + iface.advanceIteratorFn = [](void *, qsizetype) {}; + iface.compareIteratorFn = [](const void *, const void *) { + return true; /*all iterators are nullptr*/ + }; + iface.destroyIteratorFn = [](const void *) {}; + iface.copyIteratorFn = [](void *, const void *) {}; + iface.diffIteratorFn = [](const void *, const void *) { return qsizetype(0); }; + return iface; } -static void restoreJSValue(QDataStream &stream, void *data) +static QtMetaContainerPrivate::QMetaSequenceInterface sequenceInterface() { - QJSValue *jsv = reinterpret_cast<QJSValue*>(data); + using namespace QtMetaContainerPrivate; + QMetaSequenceInterface iface; + iface.valueMetaType = QtPrivate::qMetaTypeInterfaceForType<QVariant>(); + iface.iteratorCapabilities = RandomAccessCapability | BiDirectionalCapability | ForwardCapability; + iface.addRemoveCapabilities = CanAddAtEnd; + iface.sizeFn = [](const void *p) -> qsizetype { + return static_cast<QJSValue const *>(p)->property(QString::fromLatin1("length")).toInt(); + }; + + /* Lifetime management notes: + * valueAtIndexFn and valueAtIteratorFn return a pointer to a JSValue allocated via + * QMetaType::create Because we set QVariantConstructionFlags::ShouldDeleteVariantData, + * QSequentialIterable::at and QSequentialIterable::operator*() will free that memory + */ + + iface.valueAtIndexFn = [](const void *iterable, qsizetype index, void *dataPtr) -> void { + auto *data = static_cast<QVariant *>(dataPtr); + *data = static_cast<QJSValue const *>(iterable)->property(quint32(index)).toVariant(); + }; + iface.createIteratorFn = [](void *iterable, QMetaSequenceInterface::Position pos) { + void *iterator = nullptr; + createNewIteratorIfNonExisting(&iterator); + auto jsArrayIterator = static_cast<JSArrayIterator *>(iterator); + jsArrayIterator->index = 0; + jsArrayIterator->data = reinterpret_cast<QJSValue const*>(iterable); + if (pos == QMetaSequenceInterface::AtEnd) { + auto length = static_cast<QJSValue const *>(iterable)->property( + QString::fromLatin1("length")).toInt(); + jsArrayIterator->index = quint32(length); + } + return iterator; + }; + iface.createConstIteratorFn = [](const void *iterable, QMetaSequenceInterface::Position pos) { + void *iterator = nullptr; + createNewIteratorIfNonExisting(&iterator); + auto jsArrayIterator = static_cast<JSArrayIterator *>(iterator); + jsArrayIterator->index = 0; + jsArrayIterator->data = reinterpret_cast<QJSValue const*>(iterable); + if (pos == QMetaSequenceInterface::AtEnd) { + auto length = static_cast<QJSValue const *>(iterable)->property( + QString::fromLatin1("length")).toInt(); + jsArrayIterator->index = quint32(length); + } + return iterator; + }; + iface.advanceIteratorFn = [](void *iterator, qsizetype advanceBy) { + static_cast<JSArrayIterator *>(iterator)->index += quint32(advanceBy); + }; + iface.advanceConstIteratorFn = [](void *iterator, qsizetype advanceBy) { + static_cast<JSArrayIterator *>(iterator)->index += quint32(advanceBy); + }; + iface.valueAtIteratorFn = [](const void *iterator, void *dataPtr) -> void { + const auto *arrayIterator = static_cast<const JSArrayIterator *>(iterator); + const QJSValue *jsArray = arrayIterator->data; + auto *data = static_cast<QVariant *>(dataPtr); + *data = jsArray->property(arrayIterator->index).toVariant(); + }; + iface.valueAtConstIteratorFn = [](const void *iterator, void *dataPtr) -> void { + const auto *arrayIterator = static_cast<const JSArrayIterator *>(iterator); + const QJSValue *jsArray = arrayIterator->data; + auto *data = static_cast<QVariant *>(dataPtr); + *data = jsArray->property(arrayIterator->index).toVariant(); + }; + iface.destroyIteratorFn = [](const void *iterator) { + delete static_cast<const JSArrayIterator *>(iterator); + }; + iface.destroyConstIteratorFn = [](const void *iterator) { + delete static_cast<const JSArrayIterator *>(iterator); + }; + iface.compareIteratorFn = [](const void *p, const void *other) { + auto this_ = static_cast<const JSArrayIterator *>(p); + auto that_ = static_cast<const JSArrayIterator *>(other); + return this_->index == that_->index && this_->data == that_->data; + }; + iface.compareConstIteratorFn = [](const void *p, const void *other) { + auto this_ = static_cast<const JSArrayIterator *>(p); + auto that_ = static_cast<const JSArrayIterator *>(other); + return this_->index == that_->index && this_->data == that_->data; + }; + iface.copyIteratorFn = [](void *iterator, const void *otherIterator) { + auto *otherIter = (static_cast<JSArrayIterator const *>(otherIterator)); + static_cast<JSArrayIterator *>(iterator)->index = otherIter->index; + static_cast<JSArrayIterator *>(iterator)->data = otherIter->data; + }; + iface.copyConstIteratorFn = [](void *iterator, const void *otherIterator) { + auto *otherIter = (static_cast<JSArrayIterator const *>(otherIterator)); + static_cast<JSArrayIterator *>(iterator)->index = otherIter->index; + static_cast<JSArrayIterator *>(iterator)->data = otherIter->data; + }; + iface.diffIteratorFn = [](const void *iterator, const void *otherIterator) -> qsizetype { + const auto *self = static_cast<const JSArrayIterator *>(iterator); + const auto *other = static_cast<const JSArrayIterator *>(otherIterator); + return self->index - other->index; + }; + iface.diffConstIteratorFn = [](const void *iterator, const void *otherIterator) -> qsizetype { + const auto *self = static_cast<const JSArrayIterator *>(iterator); + const auto *other = static_cast<const JSArrayIterator *>(otherIterator); + return self->index - other->index; + }; + iface.addValueFn = [](void *iterable, const void *data, QMetaSequenceInterface::Position) { + auto *jsvalue = static_cast<QJSValue *>(iterable); + QV4::Scope scope(QJSValuePrivate::engine(jsvalue)); + QV4::ScopedArrayObject a(scope, QJSValuePrivate::asManagedType<QV4::ArrayObject>(jsvalue)); + QV4::ScopedValue v(scope, scope.engine->fromVariant(*static_cast<const QVariant *>(data))); + if (!a) + return; + int len = a->getLength(); + a->setIndexed(len, v, QV4::Object::DoNotThrow); + }; + return iface; +} + +static QSequentialIterable jsvalueToSequence (const QJSValue& value) { + using namespace QtMetaTypePrivate; + using namespace QtMetaContainerPrivate; - quint32 isNullOrUndefined; - stream >> isNullOrUndefined; - if (isNullOrUndefined & 0x1) { - *jsv = QJSValue(QJSValue::NullValue); - } else if (isNullOrUndefined & 0x2) { - *jsv = QJSValue(); + if (!value.isArray()) { + static QMetaSequenceInterface emptySequence = emptySequenceInterface(); + return QSequentialIterable(QMetaSequence(&emptySequence), nullptr); + } + + static QMetaSequenceInterface sequence = sequenceInterface(); + return QSequentialIterable(QMetaSequence(&sequence), &value); +} + +void ExecutionEngine::initializeStaticMembers() +{ + bool ok = false; + + const int envMaxJSStackSize = qEnvironmentVariableIntValue("QV4_JS_MAX_STACK_SIZE", &ok); + if (ok && envMaxJSStackSize > 0) + s_maxJSStackSize = envMaxJSStackSize; + + const int envMaxGCStackSize = qEnvironmentVariableIntValue("QV4_GC_MAX_STACK_SIZE", &ok); + if (ok && envMaxGCStackSize > 0) + s_maxGCStackSize = envMaxGCStackSize; + + if (qEnvironmentVariableIsSet("QV4_CRASH_ON_STACKOVERFLOW")) { + s_maxCallDepth = std::numeric_limits<qint32>::max(); } else { - QVariant v; - v.load(stream); - QJSValuePrivate::setVariant(jsv, v); + ok = false; + s_maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok); + if (!ok || s_maxCallDepth <= 0) + s_maxCallDepth = -1; } + + ok = false; + s_jitCallCountThreshold = qEnvironmentVariableIntValue("QV4_JIT_CALL_THRESHOLD", &ok); + if (!ok) + s_jitCallCountThreshold = 3; + if (qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER")) + s_jitCallCountThreshold = std::numeric_limits<int>::max(); + + qMetaTypeId<QJSValue>(); + qMetaTypeId<QList<int> >(); + + if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantMap>()) + QMetaType::registerConverter<QJSValue, QVariantMap>(convertJSValueToVariantType<QVariantMap>); + if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantList>()) + QMetaType::registerConverter<QJSValue, QVariantList>(convertJSValueToVariantType<QVariantList>); + if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QStringList>()) + QMetaType::registerConverter<QJSValue, QStringList>(convertJSValueToVariantType<QStringList>); + if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QSequentialIterable>()) + QMetaType::registerConverter<QJSValue, QSequentialIterable>(jsvalueToSequence); } ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) @@ -193,7 +338,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) , gcStack(new WTF::PageAllocation) , globalCode(nullptr) , publicEngine(jsEngine) - , m_engineId(engineSerial.fetchAndAddOrdered(1)) + , m_engineId(engineSerial.fetchAndAddOrdered(2)) , regExpCache(nullptr) , m_multiplyWrappedQObjects(nullptr) #if QT_CONFIG(qml_jit) @@ -204,41 +349,36 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) #endif , m_qmlEngine(nullptr) { - bool ok = false; - const int envMaxJSStackSize = qEnvironmentVariableIntValue("QV4_JS_MAX_STACK_SIZE", &ok); - if (ok && envMaxJSStackSize > 0) - m_maxJSStackSize = envMaxJSStackSize; - - const int envMaxGCStackSize = qEnvironmentVariableIntValue("QV4_GC_MAX_STACK_SIZE", &ok); - if (ok && envMaxGCStackSize > 0) - m_maxGCStackSize = envMaxGCStackSize; - - memoryManager = new QV4::MemoryManager(this); - - if (maxCallDepth == -1) { - if (qEnvironmentVariableIsSet("QV4_CRASH_ON_STACKOVERFLOW")) { - maxCallDepth = std::numeric_limits<qint32>::max(); - } else { - ok = false; - maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok); - if (!ok || maxCallDepth <= 0) { -#if defined(QT_NO_DEBUG) && !defined(__SANITIZE_ADDRESS__) && !QT_HAS_FEATURE(address_sanitizer) - maxCallDepth = 1234; -#else - // no (tail call) optimization is done, so there'll be a lot mare stack frames active - maxCallDepth = 200; -#endif - } + if (m_engineId == 1) { + initializeStaticMembers(); + engineSerial.storeRelease(2); // make it even + } else if (Q_UNLIKELY(m_engineId & 1)) { + // This should be rare. You usually don't create lots of engines at the same time. + while (engineSerial.loadAcquire() & 1) { + QThread::yieldCurrentThread(); } } - Q_ASSERT(maxCallDepth > 0); + if (s_maxCallDepth < 0) { + const StackProperties stack = stackProperties(); + cppStackBase = stack.base; + cppStackLimit = stack.softLimit; + } else { + callDepth = 0; + } + + // We allocate guard pages around our stacks. + const size_t guardPages = 2 * WTF::pageSize(); + + memoryManager = new QV4::MemoryManager(this); + // we don't want to run the gc while the initial setup is not done; not even in aggressive mode + GCCriticalSection gcCriticalSection(this); // reserve space for the JS stack // we allow it to grow to a bit more than m_maxJSStackSize, as we can overshoot due to ScopedValues // allocated outside of JIT'ed methods. - *jsStack = WTF::PageAllocation::allocate(m_maxJSStackSize + 256*1024, WTF::OSAllocator::JSVMStackPages, - /* writable */ true, /* executable */ false, - /* includesGuardPages */ true); + *jsStack = WTF::PageAllocation::allocate( + s_maxJSStackSize + 256*1024 + guardPages, WTF::OSAllocator::JSVMStackPages, + /* writable */ true, /* executable */ false, /* includesGuardPages */ true); jsStackBase = (Value *)jsStack->base(); #ifdef V4_USE_VALGRIND VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, m_maxJSStackSize + 256*1024); @@ -246,18 +386,9 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) jsStackTop = jsStackBase; - *gcStack = WTF::PageAllocation::allocate(m_maxGCStackSize, WTF::OSAllocator::JSVMStackPages, - /* writable */ true, /* executable */ false, - /* includesGuardPages */ true); - - { - ok = false; - jitCallCountThreshold = qEnvironmentVariableIntValue("QV4_JIT_CALL_THRESHOLD", &ok); - if (!ok) - jitCallCountThreshold = 3; - if (qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER")) - jitCallCountThreshold = std::numeric_limits<int>::max(); - } + *gcStack = WTF::PageAllocation::allocate( + s_maxGCStackSize + guardPages, WTF::OSAllocator::JSVMStackPages, + /* writable */ true, /* executable */ false, /* includesGuardPages */ true); exceptionValue = jsAlloca(1); *exceptionValue = Encode::undefined(); @@ -269,7 +400,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) jsSymbols = jsAlloca(NJSSymbols); // set up stack limits - jsStackLimit = jsStackBase + m_maxJSStackSize/sizeof(Value); + jsStackLimit = jsStackBase + s_maxJSStackSize/sizeof(Value); identifierTable = new IdentifierTable(this); @@ -493,30 +624,26 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) jsObjects[VariantProto] = memoryManager->allocate<VariantPrototype>(); Q_ASSERT(variantPrototype()->getPrototypeOf() == objectPrototype()->d()); -#if QT_CONFIG(qml_sequence_object) ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this)); jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic->d())); -#endif - ExecutionContext *global = rootContext(); - - jsObjects[Object_Ctor] = memoryManager->allocate<ObjectCtor>(global); - jsObjects[String_Ctor] = memoryManager->allocate<StringCtor>(global); - jsObjects[Symbol_Ctor] = memoryManager->allocate<SymbolCtor>(global); - jsObjects[Number_Ctor] = memoryManager->allocate<NumberCtor>(global); - jsObjects[Boolean_Ctor] = memoryManager->allocate<BooleanCtor>(global); - jsObjects[Array_Ctor] = memoryManager->allocate<ArrayCtor>(global); - jsObjects[Function_Ctor] = memoryManager->allocate<FunctionCtor>(global); - jsObjects[GeneratorFunction_Ctor] = memoryManager->allocate<GeneratorFunctionCtor>(global); - jsObjects[Date_Ctor] = memoryManager->allocate<DateCtor>(global); - jsObjects[RegExp_Ctor] = memoryManager->allocate<RegExpCtor>(global); - jsObjects[Error_Ctor] = memoryManager->allocate<ErrorCtor>(global); - jsObjects[EvalError_Ctor] = memoryManager->allocate<EvalErrorCtor>(global); - jsObjects[RangeError_Ctor] = memoryManager->allocate<RangeErrorCtor>(global); - jsObjects[ReferenceError_Ctor] = memoryManager->allocate<ReferenceErrorCtor>(global); - jsObjects[SyntaxError_Ctor] = memoryManager->allocate<SyntaxErrorCtor>(global); - jsObjects[TypeError_Ctor] = memoryManager->allocate<TypeErrorCtor>(global); - jsObjects[URIError_Ctor] = memoryManager->allocate<URIErrorCtor>(global); + jsObjects[Object_Ctor] = memoryManager->allocate<ObjectCtor>(this); + jsObjects[String_Ctor] = memoryManager->allocate<StringCtor>(this); + jsObjects[Symbol_Ctor] = memoryManager->allocate<SymbolCtor>(this); + jsObjects[Number_Ctor] = memoryManager->allocate<NumberCtor>(this); + jsObjects[Boolean_Ctor] = memoryManager->allocate<BooleanCtor>(this); + jsObjects[Array_Ctor] = memoryManager->allocate<ArrayCtor>(this); + jsObjects[Function_Ctor] = memoryManager->allocate<FunctionCtor>(this); + jsObjects[GeneratorFunction_Ctor] = memoryManager->allocate<GeneratorFunctionCtor>(this); + jsObjects[Date_Ctor] = memoryManager->allocate<DateCtor>(this); + jsObjects[RegExp_Ctor] = memoryManager->allocate<RegExpCtor>(this); + jsObjects[Error_Ctor] = memoryManager->allocate<ErrorCtor>(this); + jsObjects[EvalError_Ctor] = memoryManager->allocate<EvalErrorCtor>(this); + jsObjects[RangeError_Ctor] = memoryManager->allocate<RangeErrorCtor>(this); + jsObjects[ReferenceError_Ctor] = memoryManager->allocate<ReferenceErrorCtor>(this); + jsObjects[SyntaxError_Ctor] = memoryManager->allocate<SyntaxErrorCtor>(this); + jsObjects[TypeError_Ctor] = memoryManager->allocate<TypeErrorCtor>(this); + jsObjects[URIError_Ctor] = memoryManager->allocate<URIErrorCtor>(this); jsObjects[IteratorProto] = memoryManager->allocate<IteratorPrototype>(); ic = newInternalClass(ForInIteratorPrototype::staticVTable(), iteratorPrototype()); @@ -530,6 +657,15 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) ic = newInternalClass(StringIteratorPrototype::staticVTable(), iteratorPrototype()); jsObjects[StringIteratorProto] = memoryManager->allocObject<StringIteratorPrototype>(ic); + // + // url + // + + jsObjects[Url_Ctor] = memoryManager->allocate<UrlCtor>(this); + jsObjects[UrlProto] = memoryManager->allocate<UrlPrototype>(); + jsObjects[UrlSearchParams_Ctor] = memoryManager->allocate<UrlSearchParamsCtor>(this); + jsObjects[UrlSearchParamsProto] = memoryManager->allocate<UrlSearchParamsPrototype>(); + str = newString(QStringLiteral("get [Symbol.species]")); jsObjects[GetSymbolSpecies] = FunctionObject::createBuiltinFunction(this, str, ArrayPrototype::method_get_species, 0); @@ -539,7 +675,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) static_cast<NumberPrototype *>(numberPrototype())->init(this, numberCtor()); static_cast<BooleanPrototype *>(booleanPrototype())->init(this, booleanCtor()); static_cast<ArrayPrototype *>(arrayPrototype())->init(this, arrayCtor()); - static_cast<PropertyListPrototype *>(propertyListPrototype())->init(this); + static_cast<PropertyListPrototype *>(propertyListPrototype())->init(); static_cast<DatePrototype *>(datePrototype())->init(this, dateCtor()); static_cast<FunctionPrototype *>(functionPrototype())->init(this, functionCtor()); static_cast<GeneratorPrototype *>(generatorPrototype())->init(this, generatorFunctionCtor()); @@ -551,6 +687,8 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) static_cast<SyntaxErrorPrototype *>(syntaxErrorPrototype())->init(this, syntaxErrorCtor()); static_cast<TypeErrorPrototype *>(typeErrorPrototype())->init(this, typeErrorCtor()); static_cast<URIErrorPrototype *>(uRIErrorPrototype())->init(this, uRIErrorCtor()); + static_cast<UrlPrototype *>(urlPrototype())->init(this, urlCtor()); + static_cast<UrlSearchParamsPrototype *>(urlSearchParamsPrototype())->init(this, urlSearchParamsCtor()); static_cast<IteratorPrototype *>(iteratorPrototype())->init(this); static_cast<ForInIteratorPrototype *>(forInIteratorPrototype())->init(this); @@ -561,23 +699,21 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) static_cast<VariantPrototype *>(variantPrototype())->init(); -#if QT_CONFIG(qml_sequence_object) sequencePrototype()->cast<SequencePrototype>()->init(); -#endif - jsObjects[WeakMap_Ctor] = memoryManager->allocate<WeakMapCtor>(global); + jsObjects[WeakMap_Ctor] = memoryManager->allocate<WeakMapCtor>(this); jsObjects[WeakMapProto] = memoryManager->allocate<WeakMapPrototype>(); static_cast<WeakMapPrototype *>(weakMapPrototype())->init(this, weakMapCtor()); - jsObjects[Map_Ctor] = memoryManager->allocate<MapCtor>(global); + jsObjects[Map_Ctor] = memoryManager->allocate<MapCtor>(this); jsObjects[MapProto] = memoryManager->allocate<MapPrototype>(); static_cast<MapPrototype *>(mapPrototype())->init(this, mapCtor()); - jsObjects[WeakSet_Ctor] = memoryManager->allocate<WeakSetCtor>(global); + jsObjects[WeakSet_Ctor] = memoryManager->allocate<WeakSetCtor>(this); jsObjects[WeakSetProto] = memoryManager->allocate<WeakSetPrototype>(); static_cast<WeakSetPrototype *>(weakSetPrototype())->init(this, weakSetCtor()); - jsObjects[Set_Ctor] = memoryManager->allocate<SetCtor>(global); + jsObjects[Set_Ctor] = memoryManager->allocate<SetCtor>(this); jsObjects[SetProto] = memoryManager->allocate<SetPrototype>(); static_cast<SetPrototype *>(setPrototype())->init(this, setCtor()); @@ -585,33 +721,34 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) // promises // - jsObjects[Promise_Ctor] = memoryManager->allocate<PromiseCtor>(global); + jsObjects[Promise_Ctor] = memoryManager->allocate<PromiseCtor>(this); jsObjects[PromiseProto] = memoryManager->allocate<PromisePrototype>(); static_cast<PromisePrototype *>(promisePrototype())->init(this, promiseCtor()); // typed arrays - jsObjects[SharedArrayBuffer_Ctor] = memoryManager->allocate<SharedArrayBufferCtor>(global); + jsObjects[SharedArrayBuffer_Ctor] = memoryManager->allocate<SharedArrayBufferCtor>(this); jsObjects[SharedArrayBufferProto] = memoryManager->allocate<SharedArrayBufferPrototype>(); static_cast<SharedArrayBufferPrototype *>(sharedArrayBufferPrototype())->init(this, sharedArrayBufferCtor()); - jsObjects[ArrayBuffer_Ctor] = memoryManager->allocate<ArrayBufferCtor>(global); + jsObjects[ArrayBuffer_Ctor] = memoryManager->allocate<ArrayBufferCtor>(this); jsObjects[ArrayBufferProto] = memoryManager->allocate<ArrayBufferPrototype>(); static_cast<ArrayBufferPrototype *>(arrayBufferPrototype())->init(this, arrayBufferCtor()); - jsObjects[DataView_Ctor] = memoryManager->allocate<DataViewCtor>(global); + jsObjects[DataView_Ctor] = memoryManager->allocate<DataViewCtor>(this); jsObjects[DataViewProto] = memoryManager->allocate<DataViewPrototype>(); static_cast<DataViewPrototype *>(dataViewPrototype())->init(this, dataViewCtor()); jsObjects[ValueTypeProto] = (Heap::Base *) nullptr; jsObjects[SignalHandlerProto] = (Heap::Base *) nullptr; + jsObjects[TypeWrapperProto] = (Heap::Base *) nullptr; - jsObjects[IntrinsicTypedArray_Ctor] = memoryManager->allocate<IntrinsicTypedArrayCtor>(global); + jsObjects[IntrinsicTypedArray_Ctor] = memoryManager->allocate<IntrinsicTypedArrayCtor>(this); jsObjects[IntrinsicTypedArrayProto] = memoryManager->allocate<IntrinsicTypedArrayPrototype>(); static_cast<IntrinsicTypedArrayPrototype *>(intrinsicTypedArrayPrototype()) ->init(this, static_cast<IntrinsicTypedArrayCtor *>(intrinsicTypedArrayCtor())); for (int i = 0; i < NTypedArrayTypes; ++i) { - static_cast<Value &>(typedArrayCtors[i]) = memoryManager->allocate<TypedArrayCtor>(global, Heap::TypedArray::Type(i)); + static_cast<Value &>(typedArrayCtors[i]) = memoryManager->allocate<TypedArrayCtor>(this, Heap::TypedArray::Type(i)); static_cast<Value &>(typedArrayPrototype[i]) = memoryManager->allocate<TypedArrayPrototype>(Heap::TypedArray::Type(i)); typedArrayPrototype[i].as<TypedArrayPrototype>()->init(this, static_cast<TypedArrayCtor *>(typedArrayCtors[i].as<Object>())); } @@ -640,6 +777,8 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) globalObject->defineDefaultProperty(QStringLiteral("TypeError"), *typeErrorCtor()); globalObject->defineDefaultProperty(QStringLiteral("URIError"), *uRIErrorCtor()); globalObject->defineDefaultProperty(QStringLiteral("Promise"), *promiseCtor()); + globalObject->defineDefaultProperty(QStringLiteral("URL"), *urlCtor()); + globalObject->defineDefaultProperty(QStringLiteral("URLSearchParams"), *urlSearchParamsCtor()); globalObject->defineDefaultProperty(QStringLiteral("SharedArrayBuffer"), *sharedArrayBufferCtor()); globalObject->defineDefaultProperty(QStringLiteral("ArrayBuffer"), *arrayBufferCtor()); @@ -656,14 +795,14 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocate<MathObject>())); globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocate<JsonObject>())); globalObject->defineDefaultProperty(QStringLiteral("Reflect"), (o = memoryManager->allocate<Reflect>())); - globalObject->defineDefaultProperty(QStringLiteral("Proxy"), (o = memoryManager->allocate<Proxy>(rootContext()))); + globalObject->defineDefaultProperty(QStringLiteral("Proxy"), (o = memoryManager->allocate<Proxy>(this))); globalObject->defineReadonlyProperty(QStringLiteral("undefined"), Value::undefinedValue()); globalObject->defineReadonlyProperty(QStringLiteral("NaN"), Value::fromDouble(std::numeric_limits<double>::quiet_NaN())); globalObject->defineReadonlyProperty(QStringLiteral("Infinity"), Value::fromDouble(Q_INFINITY)); - jsObjects[Eval_Function] = memoryManager->allocate<EvalFunction>(global); + jsObjects[Eval_Function] = memoryManager->allocate<EvalFunction>(this); globalObject->defineDefaultProperty(QStringLiteral("eval"), *evalFunction()); // ES6: 20.1.2.12 & 20.1.2.13: @@ -692,7 +831,9 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) globalObject->defineDefaultProperty(QStringLiteral("escape"), GlobalFunctions::method_escape, 1); globalObject->defineDefaultProperty(QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1); - ScopedFunctionObject t(scope, memoryManager->allocate<FunctionObject>(rootContext(), nullptr, ::throwTypeError)); + ScopedFunctionObject t( + scope, + memoryManager->allocate<DynamicFunctionObject>(this, nullptr, ::throwTypeError)); t->defineReadonlyProperty(id_length(), Value::fromInt32(0)); t->setInternalClass(t->internalClass()->cryopreserved()); jsObjects[ThrowerObject] = t; @@ -703,33 +844,30 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) functionPrototype()->insertMember(id_caller(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable); functionPrototype()->insertMember(id_arguments(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable); - qMetaTypeId<QJSValue>(); - qMetaTypeId<QList<int> >(); - - if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantMap>()) - QMetaType::registerConverter<QJSValue, QVariantMap>(convertJSValueToVariantType<QVariantMap>); - if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QVariantList>()) - QMetaType::registerConverter<QJSValue, QVariantList>(convertJSValueToVariantType<QVariantList>); - if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QStringList>()) - QMetaType::registerConverter<QJSValue, QStringList>(convertJSValueToVariantType<QStringList>); - QMetaType::registerStreamOperators(qMetaTypeId<QJSValue>(), saveJSValue, restoreJSValue); - QV4::QObjectWrapper::initializeBindings(this); m_delayedCallQueue.init(this); + isInitialized = true; } ExecutionEngine::~ExecutionEngine() { - modules.clear(); + for (auto val : nativeModules) { + PersistentValueStorage::free(val); + } + nativeModules.clear(); qDeleteAll(m_extensionData); delete m_multiplyWrappedQObjects; m_multiplyWrappedQObjects = nullptr; delete identifierTable; delete memoryManager; - while (!compilationUnits.isEmpty()) - (*compilationUnits.begin())->unlink(); + for (const auto &cu : std::as_const(m_compilationUnits)) { + Q_ASSERT(cu->engine == this); + cu->clear(); + cu->engine = nullptr; + } + m_compilationUnits.clear(); delete bumperPointerAllocator; delete regExpCache; @@ -746,11 +884,6 @@ ExecutionEngine::~ExecutionEngine() #endif } -ExecutionContext *ExecutionEngine::currentContext() const -{ - return static_cast<ExecutionContext *>(¤tStackFrame->jsFrame->context); -} - #if QT_CONFIG(qml_debug) void ExecutionEngine::setDebugger(Debugging::Debugger *debugger) { @@ -768,7 +901,7 @@ void ExecutionEngine::setProfiler(Profiling::Profiler *profiler) void ExecutionEngine::initRootContext() { Scope scope(this); - Scoped<ExecutionContext> r(scope, memoryManager->allocManaged<ExecutionContext>(sizeof(ExecutionContext::Data))); + Scoped<ExecutionContext> r(scope, memoryManager->allocManaged<ExecutionContext>()); r->d_unchecked()->init(Heap::ExecutionContext::Type_GlobalContext); r->d()->activation.set(this, globalObject->d()); jsObjects[RootContext] = r; @@ -802,13 +935,13 @@ Heap::Object *ExecutionEngine::newObject(Heap::InternalClass *internalClass) Heap::String *ExecutionEngine::newString(const QString &s) { - return memoryManager->allocWithStringData<String>(s.length() * sizeof(QChar), s); + return memoryManager->allocWithStringData<String>(s.size() * sizeof(QChar), s); } Heap::String *ExecutionEngine::newIdentifier(const QString &text) { Scope scope(this); - ScopedString s(scope, memoryManager->allocWithStringData<String>(text.length() * sizeof(QChar), text)); + ScopedString s(scope, memoryManager->allocWithStringData<String>(text.size() * sizeof(QChar), text)); s->toPropertyKey(); return s->d(); } @@ -888,24 +1021,35 @@ Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(size_t length) return memoryManager->allocate<ArrayBuffer>(length); } +Heap::DateObject *ExecutionEngine::newDateObject(double dateTime) +{ + return memoryManager->allocate<DateObject>(dateTime); +} -Heap::DateObject *ExecutionEngine::newDateObject(const Value &value) +Heap::DateObject *ExecutionEngine::newDateObject(const QDateTime &dateTime) { - return memoryManager->allocate<DateObject>(value); + return memoryManager->allocate<DateObject>(dateTime); } -Heap::DateObject *ExecutionEngine::newDateObject(const QDateTime &dt) +Heap::DateObject *ExecutionEngine::newDateObject( + QDate date, Heap::Object *parent, int index, uint flags) { - Scope scope(this); - Scoped<DateObject> object(scope, memoryManager->allocate<DateObject>(dt)); - return object->d(); + return memoryManager->allocate<DateObject>( + date, parent, index, Heap::ReferenceObject::Flags(flags)); } -Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t) +Heap::DateObject *ExecutionEngine::newDateObject( + QTime time, Heap::Object *parent, int index, uint flags) { - Scope scope(this); - Scoped<DateObject> object(scope, memoryManager->allocate<DateObject>(t)); - return object->d(); + return memoryManager->allocate<DateObject>( + time, parent, index, Heap::ReferenceObject::Flags(flags)); +} + +Heap::DateObject *ExecutionEngine::newDateObject( + QDateTime dateTime, Heap::Object *parent, int index, uint flags) +{ + return memoryManager->allocate<DateObject>( + dateTime, parent, index, Heap::ReferenceObject::Flags(flags)); } Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) @@ -920,11 +1064,6 @@ Heap::RegExpObject *ExecutionEngine::newRegExpObject(RegExp *re) return memoryManager->allocate<RegExpObject>(re); } -Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re) -{ - return memoryManager->allocate<RegExpObject>(re); -} - #if QT_CONFIG(regularexpression) Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegularExpression &re) { @@ -932,6 +1071,24 @@ Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegularExpression &r } #endif +Heap::UrlObject *ExecutionEngine::newUrlObject() +{ + return memoryManager->allocate<UrlObject>(); +} + +Heap::UrlObject *ExecutionEngine::newUrlObject(const QUrl &url) +{ + Scope scope(this); + Scoped<UrlObject> urlObject(scope, newUrlObject()); + urlObject->setUrl(url); + return urlObject->d(); +} + +Heap::UrlSearchParamsObject *ExecutionEngine::newUrlSearchParamsObject() +{ + return memoryManager->allocate<UrlSearchParamsObject>(); +} + Heap::Object *ExecutionEngine::newErrorObject(const Value &value) { return ErrorObject::create<ErrorObject>(this, value, errorCtor()); @@ -1021,9 +1178,9 @@ Heap::Object *ExecutionEngine::newEvalErrorObject(const QString &message) return ErrorObject::create<EvalErrorObject>(this, message); } -Heap::Object *ExecutionEngine::newVariantObject(const QVariant &v) +Heap::Object *ExecutionEngine::newVariantObject(const QMetaType type, const void *data) { - return memoryManager->allocate<VariantObject>(v); + return memoryManager->allocate<VariantObject>(type, data); } Heap::Object *ExecutionEngine::newForInIteratorObject(Object *o) @@ -1050,21 +1207,9 @@ Heap::Object *ExecutionEngine::newArrayIteratorObject(Object *o) Heap::QmlContext *ExecutionEngine::qmlContext() const { - if (!currentStackFrame) - return nullptr; - Heap::ExecutionContext *ctx = currentContext()->d(); - - if (ctx->type != Heap::ExecutionContext::Type_QmlContext && !ctx->outer) - return nullptr; - - while (ctx->outer && ctx->outer->type != Heap::ExecutionContext::Type_GlobalContext) - ctx = ctx->outer; - - Q_ASSERT(ctx); - if (ctx->type != Heap::ExecutionContext::Type_QmlContext) - return nullptr; - - return static_cast<Heap::QmlContext *>(ctx); + return currentStackFrame + ? static_cast<Heap::QmlContext *>(qmlContext(currentContext()->d())) + : nullptr; } QObject *ExecutionEngine::qmlScopeObject() const @@ -1076,19 +1221,17 @@ QObject *ExecutionEngine::qmlScopeObject() const return ctx->qml()->scopeObject; } -QQmlContextData *ExecutionEngine::callingQmlContext() const +QQmlRefPointer<QQmlContextData> ExecutionEngine::callingQmlContext() const { Heap::QmlContext *ctx = qmlContext(); if (!ctx) return nullptr; - return ctx->qml()->context->contextData(); + return ctx->qml()->context; } StackTrace ExecutionEngine::stackTrace(int frameLimit) const { - Scope scope(const_cast<ExecutionEngine *>(this)); - ScopedString name(scope); StackTrace stack; CppStackFrame *f = currentStackFrame; @@ -1096,16 +1239,18 @@ StackTrace ExecutionEngine::stackTrace(int frameLimit) const QV4::StackFrame frame; frame.source = f->source(); frame.function = f->function(); - frame.line = qAbs(f->lineNumber()); - frame.column = -1; + frame.line = f->lineNumber(); + stack.append(frame); - if (f->isTailCalling) { - QV4::StackFrame frame; - frame.function = QStringLiteral("[elided tail calls]"); - stack.append(frame); + if (f->isJSTypesFrame()) { + if (static_cast<JSTypesStackFrame *>(f)->isTailCalling()) { + QV4::StackFrame frame; + frame.function = QStringLiteral("[elided tail calls]"); + stack.append(frame); + } } --frameLimit; - f = f->parent; + f = f->parentFrame(); } return stack; @@ -1132,7 +1277,7 @@ static inline char *v4StackTrace(const ExecutionContext *context) const QString fileName = url.isLocalFile() ? url.toLocalFile() : url.toString(); str << "frame={level=\"" << i << "\",func=\"" << stackTrace.at(i).function << "\",file=\"" << fileName << "\",fullname=\"" << fileName - << "\",line=\"" << stackTrace.at(i).line << "\",language=\"js\"}"; + << "\",line=\"" << qAbs(stackTrace.at(i).line) << "\",language=\"js\"}"; } } str << ']'; @@ -1163,7 +1308,7 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file) base = f->v4Function->finalUrl(); break; } - f = f->parent; + f = f->parentFrame(); } if (base.isEmpty() && globalCode) @@ -1177,17 +1322,15 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file) void ExecutionEngine::markObjects(MarkStack *markStack) { - for (int i = 0; i < NClasses; ++i) - if (classes[i]) - classes[i]->mark(markStack); - markStack->drain(); + for (int i = 0; i < NClasses; ++i) { + if (Heap::InternalClass *c = classes[i]) + c->mark(markStack); + } identifierTable->markObjects(markStack); - for (auto compilationUnit: compilationUnits) { + for (const auto &compilationUnit : std::as_const(m_compilationUnits)) compilationUnit->markObjects(markStack); - markStack->drain(); - } } ReturnedValue ExecutionEngine::throwError(const Value &value) @@ -1329,7 +1472,7 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError() if (!trace.isEmpty()) { QV4::StackFrame frame = trace.constFirst(); error.setUrl(QUrl(frame.source)); - error.setLine(frame.line); + error.setLine(qAbs(frame.line)); error.setColumn(frame.column); } QV4::Scoped<QV4::ErrorObject> errorObj(scope, exception); @@ -1340,47 +1483,47 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError() // Variant conversion code typedef QSet<QV4::Heap::Object *> V4ObjectSet; -static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int typeHint, bool createJSValueForObjects, V4ObjectSet *visitedObjects); -static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const QV4::Value &value); -static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V4ObjectSet *visitedObjects = nullptr); -static bool convertToNativeQObject(QV4::ExecutionEngine *e, const QV4::Value &value, - const QByteArray &targetType, - void **result); -static QV4::ReturnedValue variantListToJS(QV4::ExecutionEngine *v4, const QVariantList &lst); -static QV4::ReturnedValue sequentialIterableToJS(QV4::ExecutionEngine *v4, const QSequentialIterable &lst); +enum class JSToQVariantConversionBehavior {Never, Safish, Aggressive }; +static QVariant toVariant( + const QV4::Value &value, QMetaType typeHint, JSToQVariantConversionBehavior conversionBehavior, + V4ObjectSet *visitedObjects); +static QObject *qtObjectFromJS(const QV4::Value &value); +static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects = nullptr, + JSToQVariantConversionBehavior behavior = JSToQVariantConversionBehavior::Safish); +static bool convertToNativeQObject(const QV4::Value &value, QMetaType targetType, void **result); static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVariantMap &vmap); static QV4::ReturnedValue variantToJS(QV4::ExecutionEngine *v4, const QVariant &value) { - return v4->metaTypeToJS(value.userType(), value.constData()); -} - - -QVariant ExecutionEngine::toVariant(const Value &value, int typeHint, bool createJSValueForObjects) -{ - return ::toVariant(this, value, typeHint, createJSValueForObjects, nullptr); + return v4->metaTypeToJS(value.metaType(), value.constData()); } - -static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int typeHint, bool createJSValueForObjects, V4ObjectSet *visitedObjects) +static QVariant toVariant(const QV4::Value &value, QMetaType metaType, JSToQVariantConversionBehavior conversionBehavior, + V4ObjectSet *visitedObjects) { Q_ASSERT (!value.isEmpty()); - QV4::Scope scope(e); if (const QV4::VariantObject *v = value.as<QV4::VariantObject>()) return v->d()->data(); - if (typeHint == QVariant::Bool) + if (metaType == QMetaType::fromType<bool>()) return QVariant(value.toBoolean()); - if (typeHint == QMetaType::QJsonValue) + if (metaType == QMetaType::fromType<double>()) + return QVariant(value.toNumber()); + + if (metaType == QMetaType::fromType<float>()) + return QVariant(float(value.toNumber())); + + if (metaType == QMetaType::fromType<QJsonValue>()) return QVariant::fromValue(QV4::JsonObject::toJsonValue(value)); - if (typeHint == qMetaTypeId<QJSValue>()) - return QVariant::fromValue(QJSValue(e, value.asReturnedValue())); + if (metaType == QMetaType::fromType<QJSValue>()) + return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(value.asReturnedValue())); - if (value.as<QV4::Object>()) { - QV4::ScopedObject object(scope, value); - if (typeHint == QMetaType::QJsonObject + if (const QV4::Object *o = value.as<QV4::Object>()) { + QV4::Scope scope(o->engine()); + QV4::ScopedObject object(scope, o); + if (metaType == QMetaType::fromType<QJsonObject>() && !value.as<ArrayObject>() && !value.as<FunctionObject>()) { return QVariant::fromValue(QV4::JsonObject::toJsonObject(object)); } else if (QV4::QObjectWrapper *wrapper = object->as<QV4::QObjectWrapper>()) { @@ -1393,16 +1536,15 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return v->toVariant(); } else if (QV4::QmlListWrapper *l = object->as<QV4::QmlListWrapper>()) { return l->toVariant(); -#if QT_CONFIG(qml_sequence_object) - } else if (object->isListType()) { - return QV4::SequencePrototype::toVariant(object); -#endif + } else if (QV4::Sequence *s = object->as<QV4::Sequence>()) { + return QV4::SequencePrototype::toVariant(s); } } - if (value.as<ArrayObject>()) { - QV4::ScopedArrayObject a(scope, value); - if (typeHint == qMetaTypeId<QList<QObject *> >()) { + if (const QV4::ArrayObject *o = value.as<ArrayObject>()) { + QV4::Scope scope(o->engine()); + QV4::ScopedArrayObject a(scope, o); + if (metaType == QMetaType::fromType<QList<QObject *>>()) { QList<QObject *> list; uint length = a->getLength(); QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope); @@ -1416,16 +1558,64 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int } return QVariant::fromValue<QList<QObject*> >(list); - } else if (typeHint == QMetaType::QJsonArray) { + } else if (metaType == QMetaType::fromType<QJsonArray>()) { return QVariant::fromValue(QV4::JsonObject::toJsonArray(a)); } -#if QT_CONFIG(qml_sequence_object) - bool succeeded = false; - QVariant retn = QV4::SequencePrototype::toVariant(value, typeHint, &succeeded); - if (succeeded) + QVariant retn = QV4::SequencePrototype::toVariant(value, metaType); + if (retn.isValid()) return retn; -#endif + + if (metaType.isValid()) { + retn = QVariant(metaType, nullptr); + auto retnAsIterable = retn.value<QSequentialIterable>(); + if (retnAsIterable.metaContainer().canAddValue()) { + QMetaType valueMetaType = retnAsIterable.metaContainer().valueMetaType(); + auto const length = a->getLength(); + QV4::ScopedValue arrayValue(scope); + for (qint64 i = 0; i < length; ++i) { + arrayValue = a->get(i); + QVariant asVariant = QQmlValueTypeProvider::createValueType( + arrayValue, valueMetaType); + if (asVariant.isValid()) { + retnAsIterable.metaContainer().addValue(retn.data(), asVariant.constData()); + continue; + } + + if (QMetaType::canConvert(QMetaType::fromType<QJSValue>(), valueMetaType)) { + // before attempting a conversion from the concrete types, + // check if there exists a conversion from QJSValue -> out type + // prefer that one for compatibility reasons + asVariant = QVariant::fromValue(QJSValuePrivate::fromReturnedValue( + arrayValue->asReturnedValue())); + if (asVariant.convert(valueMetaType)) { + retnAsIterable.metaContainer().addValue(retn.data(), asVariant.constData()); + continue; + } + } + + asVariant = toVariant(arrayValue, valueMetaType, JSToQVariantConversionBehavior::Never, visitedObjects); + if (valueMetaType == QMetaType::fromType<QVariant>()) { + retnAsIterable.metaContainer().addValue(retn.data(), &asVariant); + } else { + auto originalType = asVariant.metaType(); + bool couldConvert = asVariant.convert(valueMetaType); + if (!couldConvert && originalType.isValid()) { + // If the original type was void, we're converting a "hole" in a sparse + // array. There is no point in warning about that. + qWarning().noquote() + << QLatin1String("Could not convert array value " + "at position %1 from %2 to %3") + .arg(QString::number(i), + QString::fromUtf8(originalType.name()), + QString::fromUtf8(valueMetaType.name())); + } + retnAsIterable.metaContainer().addValue(retn.data(), asVariant.constData()); + } + } + return retn; + } + } } if (value.isUndefined()) @@ -1441,38 +1631,93 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int if (String *s = value.stringValue()) { const QString &str = s->toQString(); // QChars are stored as a strings - if (typeHint == QVariant::Char && str.size() == 1) + if (metaType == QMetaType::fromType<QChar>() && str.size() == 1) return str.at(0); return str; } -#if QT_CONFIG(qml_locale) - if (const QV4::QQmlLocaleData *ld = value.as<QV4::QQmlLocaleData>()) - return *ld->d()->locale; -#endif - if (const QV4::DateObject *d = value.as<DateObject>()) + if (const QV4::DateObject *d = value.as<DateObject>()) { + // NOTE: since we convert QTime to JS Date, + // round trip will change the variant type (to QDateTime)! + + if (metaType == QMetaType::fromType<QDate>()) + return DateObject::dateTimeToDate(d->toQDateTime()); + + if (metaType == QMetaType::fromType<QTime>()) + return d->toQDateTime().time(); + + if (metaType == QMetaType::fromType<QString>()) + return d->toString(); + return d->toQDateTime(); + } + if (const QV4::UrlObject *d = value.as<UrlObject>()) + return d->toQUrl(); if (const ArrayBuffer *d = value.as<ArrayBuffer>()) return d->asByteArray(); - // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)! + if (const Symbol *symbol = value.as<Symbol>()) { + return conversionBehavior == JSToQVariantConversionBehavior::Never + ? QVariant::fromValue(QJSValuePrivate::fromReturnedValue(symbol->asReturnedValue())) + : symbol->descriptiveString(); + } - QV4::ScopedObject o(scope, value); - Q_ASSERT(o); + const QV4::Object *object = value.as<QV4::Object>(); + Q_ASSERT(object); + QV4::Scope scope(object->engine()); + QV4::ScopedObject o(scope, object); - if (QV4::RegExpObject *re = o->as<QV4::RegExpObject>()) { #if QT_CONFIG(regularexpression) - if (typeHint != QMetaType::QRegExp) - return re->toQRegularExpression(); + if (QV4::RegExpObject *re = o->as<QV4::RegExpObject>()) + return re->toQRegularExpression(); #endif - return re->toQRegExp(); + + if (metaType.isValid() && !(metaType.flags() & QMetaType::PointerToQObject)) { + const QVariant result = QQmlValueTypeProvider::createValueType(value, metaType); + if (result.isValid()) + return result; } - if (createJSValueForObjects) - return QVariant::fromValue(QJSValue(scope.engine, o->asReturnedValue())); + if (conversionBehavior == JSToQVariantConversionBehavior::Never) + return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue())); + + return objectToVariant(o, visitedObjects, conversionBehavior); +} + +QVariant ExecutionEngine::toVariantLossy(const Value &value) +{ + return ::toVariant(value, QMetaType(), JSToQVariantConversionBehavior::Aggressive, nullptr); +} + +QVariant ExecutionEngine::toVariant( + const Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols) +{ + auto behavior = createJSValueForObjectsAndSymbols ? JSToQVariantConversionBehavior::Never + : JSToQVariantConversionBehavior::Safish; + return ::toVariant(value, typeHint, behavior, nullptr); +} + +static QVariantMap objectToVariantMap(const QV4::Object *o, V4ObjectSet *visitedObjects, + JSToQVariantConversionBehavior conversionBehvior) +{ + QVariantMap map; + QV4::Scope scope(o->engine()); + QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly); + QV4::ScopedValue name(scope); + QV4::ScopedValue val(scope); + while (1) { + name = it.nextPropertyNameAsString(val); + if (name->isNull()) + break; - return objectToVariant(e, o, visitedObjects); + QString key = name->toQStringNoThrow(); + map.insert(key, ::toVariant( + val, /*type hint*/ QMetaType {}, + conversionBehvior, visitedObjects)); + } + return map; } -static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V4ObjectSet *visitedObjects) +static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects, + JSToQVariantConversionBehavior conversionBehvior) { Q_ASSERT(o); @@ -1492,7 +1737,7 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V QVariant result; if (o->as<ArrayObject>()) { - QV4::Scope scope(e); + QV4::Scope scope(o->engine()); QV4::ScopedArrayObject a(scope, o->asReturnedValue()); QV4::ScopedValue v(scope); QVariantList list; @@ -1500,37 +1745,51 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V int length = a->getLength(); for (int ii = 0; ii < length; ++ii) { v = a->get(ii); - list << ::toVariant(e, v, -1, /*createJSValueForObjects*/false, visitedObjects); + list << ::toVariant(v, QMetaType {}, conversionBehvior, + visitedObjects); } result = list; - } else if (!o->as<FunctionObject>()) { - QVariantMap map; - QV4::Scope scope(e); - QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly); - QV4::ScopedValue name(scope); - QV4::ScopedValue val(scope); - while (1) { - name = it.nextPropertyNameAsString(val); - if (name->isNull()) - break; - - QString key = name->toQStringNoThrow(); - map.insert(key, ::toVariant(e, val, /*type hint*/-1, /*createJSValueForObjects*/false, visitedObjects)); - } - - result = map; + } else if (o->getPrototypeOf() == o->engine()->objectPrototype()->d() + || (conversionBehvior == JSToQVariantConversionBehavior::Aggressive && + !o->as<QV4::FunctionObject>())) { + /* FunctionObject is excluded for historical reasons, even though + objects with a custom prototype risk losing information + But the Aggressive path is used only in QJSValue::toVariant + which is documented to be lossy + */ + result = objectToVariantMap(o, visitedObjects, conversionBehvior); + } else { + // If it's not a plain object, we can only save it as QJSValue. + result = QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue())); } visitedObjects->remove(o->d()); return result; } -QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) +/*! + \internal + + Transform the given \a metaType and \a ptr into a JavaScript representation. + */ +QV4::ReturnedValue ExecutionEngine::fromData( + QMetaType metaType, const void *ptr, + QV4::Heap::Object *container, int property, uint flags) { - int type = variant.userType(); - const void *ptr = variant.constData(); + const auto createSequence = [&](const QMetaSequence metaSequence) { + QV4::Scope scope(this); + QV4::Scoped<Sequence> sequence(scope); + if (container) { + return QV4::SequencePrototype::newSequence( + this, metaType, metaSequence, ptr, + container, property, Heap::ReferenceObject::Flags(flags)); + } else { + return QV4::SequencePrototype::fromData(this, metaType, metaSequence, ptr); + } + }; + const int type = metaType.id(); if (type < QMetaType::User) { switch (QMetaType::Type(type)) { case QMetaType::UnknownType: @@ -1545,6 +1804,10 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return QV4::Encode(*reinterpret_cast<const int*>(ptr)); case QMetaType::UInt: return QV4::Encode(*reinterpret_cast<const uint*>(ptr)); + case QMetaType::Long: + return QV4::Encode((double)*reinterpret_cast<const long *>(ptr)); + case QMetaType::ULong: + return QV4::Encode((double)*reinterpret_cast<const ulong *>(ptr)); case QMetaType::LongLong: return QV4::Encode((double)*reinterpret_cast<const qlonglong*>(ptr)); case QMetaType::ULongLong: @@ -1565,35 +1828,34 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return QV4::Encode((int)*reinterpret_cast<const char*>(ptr)); case QMetaType::UChar: return QV4::Encode((int)*reinterpret_cast<const unsigned char*>(ptr)); + case QMetaType::SChar: + return QV4::Encode((int)*reinterpret_cast<const signed char*>(ptr)); case QMetaType::QChar: return newString(*reinterpret_cast<const QChar *>(ptr))->asReturnedValue(); + case QMetaType::Char16: + return newString(QChar(*reinterpret_cast<const char16_t *>(ptr)))->asReturnedValue(); case QMetaType::QDateTime: - return QV4::Encode(newDateObject(*reinterpret_cast<const QDateTime *>(ptr))); + return QV4::Encode(newDateObject( + *reinterpret_cast<const QDateTime *>(ptr), + container, property, flags)); case QMetaType::QDate: - return QV4::Encode(newDateObject(QDateTime(*reinterpret_cast<const QDate *>(ptr), QTime(0, 0, 0), Qt::UTC))); + return QV4::Encode(newDateObject( + *reinterpret_cast<const QDate *>(ptr), + container, property, flags)); case QMetaType::QTime: - return QV4::Encode(newDateObjectFromTime(*reinterpret_cast<const QTime *>(ptr))); - case QMetaType::QRegExp: - return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(ptr))); + return QV4::Encode(newDateObject( + *reinterpret_cast<const QTime *>(ptr), + container, property, flags)); #if QT_CONFIG(regularexpression) case QMetaType::QRegularExpression: return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegularExpression *>(ptr))); #endif case QMetaType::QObjectStar: return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr)); -#if QT_CONFIG(qml_sequence_object) case QMetaType::QStringList: - { - bool succeeded = false; - QV4::Scope scope(this); - QV4::ScopedValue retn(scope, QV4::SequencePrototype::fromVariant(this, variant, &succeeded)); - if (succeeded) - return retn->asReturnedValue(); - return QV4::Encode(newArrayObject(*reinterpret_cast<const QStringList *>(ptr))); - } -#endif + return createSequence(QMetaSequence::fromContainer<QStringList>()); case QMetaType::QVariantList: - return variantListToJS(this, *reinterpret_cast<const QVariantList *>(ptr)); + return createSequence(QMetaSequence::fromContainer<QVariantList>()); case QMetaType::QVariantMap: return variantMapToJS(this, *reinterpret_cast<const QVariantMap *>(ptr)); case QMetaType::QJsonValue: @@ -1602,112 +1864,123 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return QV4::JsonObject::fromJsonObject(this, *reinterpret_cast<const QJsonObject *>(ptr)); case QMetaType::QJsonArray: return QV4::JsonObject::fromJsonArray(this, *reinterpret_cast<const QJsonArray *>(ptr)); -#if QT_CONFIG(qml_locale) - case QMetaType::QLocale: - return QQmlLocale::wrap(this, *reinterpret_cast<const QLocale*>(ptr)); -#endif case QMetaType::QPixmap: case QMetaType::QImage: // Scarce value types - return QV4::Encode(newVariantObject(variant)); + return QV4::Encode(newVariantObject(metaType, ptr)); default: break; } + } - if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(type)) - return QV4::QQmlValueTypeWrapper::create(this, variant, vtmo, type); - } else { - QV4::Scope scope(this); - if (type == qMetaTypeId<QQmlListReference>()) { - typedef QQmlListReferencePrivate QDLRP; - QDLRP *p = QDLRP::get((QQmlListReference*)const_cast<void *>(ptr)); - if (p->object) { - return QV4::QmlListWrapper::create(scope.engine, p->property, p->propertyType); - } else { - return QV4::Encode::null(); - } - } else if (type == qMetaTypeId<QJSValue>()) { - const QJSValue *value = reinterpret_cast<const QJSValue *>(ptr); - return QJSValuePrivate::convertedToValue(this, *value); - } else if (type == qMetaTypeId<QList<QObject *> >()) { - // XXX Can this be made more by using Array as a prototype and implementing - // directly against QList<QObject*>? - const QList<QObject *> &list = *(const QList<QObject *>*)ptr; - QV4::ScopedArrayObject a(scope, newArrayObject()); - a->arrayReserve(list.count()); - QV4::ScopedValue v(scope); - for (int ii = 0; ii < list.count(); ++ii) - a->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(this, list.at(ii)))); - a->setArrayLengthUnchecked(list.count()); - return a.asReturnedValue(); - } else if (QMetaType::typeFlags(type) & QMetaType::PointerToQObject) { + if (metaType.flags() & QMetaType::IsEnumeration) + return fromData(metaType.underlyingType(), ptr, container, property, flags); + + QV4::Scope scope(this); + if (metaType == QMetaType::fromType<QQmlListReference>()) { + typedef QQmlListReferencePrivate QDLRP; + QDLRP *p = QDLRP::get((QQmlListReference*)const_cast<void *>(ptr)); + if (p->object) + return QV4::QmlListWrapper::create(scope.engine, p->property, p->propertyType); + else + return QV4::Encode::null(); + } else if (auto flags = metaType.flags(); flags & QMetaType::IsQmlList) { + // casting to QQmlListProperty<QObject> is slightly nasty, but it's the + // same QQmlListReference does. + const auto *p = static_cast<const QQmlListProperty<QObject> *>(ptr); + if (p->object) + return QV4::QmlListWrapper::create(scope.engine, *p, metaType); + else + return QV4::Encode::null(); + } else if (metaType == QMetaType::fromType<QJSValue>()) { + return QJSValuePrivate::convertToReturnedValue( + this, *reinterpret_cast<const QJSValue *>(ptr)); + } else if (metaType == QMetaType::fromType<QList<QObject *> >()) { + // XXX Can this be made more by using Array as a prototype and implementing + // directly against QList<QObject*>? + const QList<QObject *> &list = *(const QList<QObject *>*)ptr; + QV4::ScopedArrayObject a(scope, newArrayObject()); + a->arrayReserve(list.size()); + QV4::ScopedValue v(scope); + for (int ii = 0; ii < list.size(); ++ii) + a->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(this, list.at(ii)))); + a->setArrayLengthUnchecked(list.size()); + return a.asReturnedValue(); + } else if (auto flags = metaType.flags(); flags & QMetaType::PointerToQObject) { + if (flags.testFlag(QMetaType::IsConst)) + return QV4::QObjectWrapper::wrapConst(this, *reinterpret_cast<QObject* const *>(ptr)); + else return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr)); + } else if (metaType == QMetaType::fromType<QJSPrimitiveValue>()) { + const QJSPrimitiveValue *primitive = static_cast<const QJSPrimitiveValue *>(ptr); + switch (primitive->type()) { + case QJSPrimitiveValue::Boolean: + return Encode(primitive->asBoolean()); + case QJSPrimitiveValue::Integer: + return Encode(primitive->asInteger()); + case QJSPrimitiveValue::String: + return newString(primitive->asString())->asReturnedValue(); + case QJSPrimitiveValue::Undefined: + return Encode::undefined(); + case QJSPrimitiveValue::Null: + return Encode::null(); + case QJSPrimitiveValue::Double: + return Encode(primitive->asDouble()); } + } - bool objOk; - QObject *obj = QQmlMetaType::toQObject(variant, &objOk); - if (objOk) - return QV4::QObjectWrapper::wrap(this, obj); + if (const QMetaObject *vtmo = QQmlMetaType::metaObjectForValueType(metaType)) { + if (container) { + return QV4::QQmlValueTypeWrapper::create( + this, ptr, vtmo, metaType, + container, property, Heap::ReferenceObject::Flags(flags)); + } else { + return QV4::QQmlValueTypeWrapper::create(this, ptr, vtmo, metaType); + } + } -#if QT_CONFIG(qml_sequence_object) - bool succeeded = false; - QV4::ScopedValue retn(scope, QV4::SequencePrototype::fromVariant(this, variant, &succeeded)); - if (succeeded) - return retn->asReturnedValue(); -#endif + const QQmlType listType = QQmlMetaType::qmlListType(metaType); + if (listType.isSequentialContainer()) + return createSequence(listType.listMetaSequence()); - if (QMetaType::hasRegisteredConverterFunction(type, qMetaTypeId<QtMetaTypePrivate::QSequentialIterableImpl>())) { - QSequentialIterable lst = variant.value<QSequentialIterable>(); - return sequentialIterableToJS(this, lst); - } + QSequentialIterable iterable; + if (QMetaType::convert(metaType, ptr, QMetaType::fromType<QSequentialIterable>(), &iterable)) { - if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(type)) - return QV4::QQmlValueTypeWrapper::create(this, variant, vtmo, type); - } + // If the resulting iterable is useful for anything, turn it into a QV4::Sequence. + const QMetaSequence sequence = iterable.metaContainer(); + if (sequence.hasSize() && sequence.canGetValueAtIndex()) + return createSequence(sequence); - // XXX TODO: To be compatible, we still need to handle: - // + QObjectList - // + QList<int> + // As a last resort, try to read the contents of the container via an iterator + // and build a JS array from them. + if (sequence.hasConstIterator() && sequence.canGetValueAtConstIterator()) { + QV4::ScopedArrayObject a(scope, newArrayObject()); + for (auto it = iterable.constBegin(), end = iterable.constEnd(); it != end; ++it) + a->push_back(fromVariant(*it)); + return a.asReturnedValue(); + } + } - return QV4::Encode(newVariantObject(variant)); + return QV4::Encode(newVariantObject(metaType, ptr)); } -QVariantMap ExecutionEngine::variantMapFromJS(const Object *o) +QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) { - return objectToVariant(this, o).toMap(); + return fromData(variant.metaType(), variant.constData()); } - -// Converts a QVariantList to JS. -// The result is a new Array object with length equal to the length -// of the QVariantList, and the elements being the QVariantList's -// elements converted to JS, recursively. -static QV4::ReturnedValue variantListToJS(QV4::ExecutionEngine *v4, const QVariantList &lst) +ReturnedValue ExecutionEngine::fromVariant( + const QVariant &variant, Heap::Object *parent, int property, uint flags) { - QV4::Scope scope(v4); - QV4::ScopedArrayObject a(scope, v4->newArrayObject()); - a->arrayReserve(lst.size()); - QV4::ScopedValue v(scope); - for (int i = 0; i < lst.size(); i++) - a->arrayPut(i, (v = variantToJS(v4, lst.at(i)))); - a->setArrayLengthUnchecked(lst.size()); - return a.asReturnedValue(); + return fromData(variant.metaType(), variant.constData(), parent, property, flags); } -// Converts a QSequentialIterable to JS. -// The result is a new Array object with length equal to the length -// of the QSequentialIterable, and the elements being the QSequentialIterable's -// elements converted to JS, recursively. -static QV4::ReturnedValue sequentialIterableToJS(QV4::ExecutionEngine *v4, const QSequentialIterable &lst) +QVariantMap ExecutionEngine::variantMapFromJS(const Object *o) { - QV4::Scope scope(v4); - QV4::ScopedArrayObject a(scope, v4->newArrayObject()); - a->arrayReserve(lst.size()); - QV4::ScopedValue v(scope); - for (int i = 0; i < lst.size(); i++) - a->arrayPut(i, (v = variantToJS(v4, lst.at(i)))); - a->setArrayLengthUnchecked(lst.size()); - return a.asReturnedValue(); + Q_ASSERT(o); + V4ObjectSet visitedObjects; + visitedObjects.insert(o->d()); + return objectToVariantMap(o, &visitedObjects, JSToQVariantConversionBehavior::Safish); } // Converts a QVariantMap to JS. @@ -1735,27 +2008,49 @@ static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVarian // Converts the meta-type defined by the given type and data to JS. // Returns the value if conversion succeeded, an empty handle otherwise. -QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) +QV4::ReturnedValue ExecutionEngine::metaTypeToJS(QMetaType type, const void *data) { Q_ASSERT(data != nullptr); - QVariant variant(type, data); - if (QMetaType::Type(variant.type()) == QMetaType::QVariant) { + if (type == QMetaType::fromType<QVariant>()) { // unwrap it: this is tested in QJSEngine, and makes the most sense for // end-user code too. - return variantToJS(this, *reinterpret_cast<const QVariant*>(data)); + return fromVariant(*reinterpret_cast<const QVariant*>(data)); + } else if (type == QMetaType::fromType<QUrl>()) { + // Create a proper URL object here, rather than a variant. + return newUrlObject(*reinterpret_cast<const QUrl *>(data))->asReturnedValue(); } - return fromVariant(variant); + + return fromData(type, data); } int ExecutionEngine::maxJSStackSize() const { - return m_maxJSStackSize; + return s_maxJSStackSize; } int ExecutionEngine::maxGCStackSize() const { - return m_maxGCStackSize; + return s_maxGCStackSize; +} + +/*! + \internal + Returns \a length converted to int if its safe to + pass to \c Scope::alloc. + Otherwise it throws a RangeError, and returns 0. + */ +int ExecutionEngine::safeForAllocLength(qint64 len64) +{ + if (len64 < 0ll || len64 > qint64(std::numeric_limits<int>::max())) { + throwRangeError(QStringLiteral("Invalid array length.")); + return 0; + } + if (len64 > qint64(this->jsStackLimit - this->jsStackTop)) { + throwRangeError(QStringLiteral("Array too large for apply().")); + return 0; + } + return len64; } ReturnedValue ExecutionEngine::global() @@ -1766,9 +2061,19 @@ ReturnedValue ExecutionEngine::global() QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compileModule(const QUrl &url) { QQmlMetaType::CachedUnitLookupError cacheError = QQmlMetaType::CachedUnitLookupError::NoError; - if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(url, &cacheError)) { - return ExecutableCompilationUnit::create( - QV4::CompiledData::CompilationUnit(cachedUnit, url.fileName(), url.toString())); + const DiskCacheOptions options = diskCacheOptions(); + if (const QQmlPrivate::CachedQmlUnit *cachedUnit = (options & DiskCache::Aot) + ? QQmlMetaType::findCachedCompilationUnit( + url, + (options & DiskCache::AotByteCode) + ? QQmlMetaType::AcceptUntyped + : QQmlMetaType::RequireFullyTyped, + &cacheError) + : nullptr) { + return executableCompilationUnit( + QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>( + cachedUnit->qmlData, cachedUnit->aotCompiledFunctions, url.fileName(), + url.toString())); } QFile f(QQmlFile::urlToLocalFileOrQrc(url)); @@ -1794,73 +2099,192 @@ QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compileModule( sourceCode, sourceTimeStamp, &diagnostics); for (const QQmlJS::DiagnosticMessage &m : diagnostics) { if (m.isError()) { - throwSyntaxError(m.message, url.toString(), m.line, m.column); + throwSyntaxError(m.message, url.toString(), m.loc.startLine, m.loc.startColumn); return nullptr; } else { - qWarning() << url << ':' << m.line << ':' << m.column + qWarning() << url << ':' << m.loc.startLine << ':' << m.loc.startColumn << ": warning: " << m.message; } } - return ExecutableCompilationUnit::create(std::move(unit)); + return insertCompilationUnit(std::move(unit)); } -void ExecutionEngine::injectModule(const QQmlRefPointer<ExecutableCompilationUnit> &moduleUnit) +QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compilationUnitForUrl(const QUrl &url) const { - // Injection can happen from the QML type loader thread for example, but instantiation and - // evaluation must be limited to the ExecutionEngine's thread. - QMutexLocker moduleGuard(&moduleMutex); - modules.insert(moduleUnit->finalUrl(), moduleUnit); + // Gives the _most recently inserted_ CU of that URL. That's what we want. + return m_compilationUnits.value(url); +} + +QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::executableCompilationUnit( + QQmlRefPointer<CompiledData::CompilationUnit> &&unit) +{ + const QUrl url = unit->finalUrl(); + auto [begin, end] = std::as_const(m_compilationUnits).equal_range(url); + + for (auto it = begin; it != end; ++it) { + if ((*it)->baseCompilationUnit() == unit) + return *it; + } + + auto executableUnit = m_compilationUnits.insert( + url, ExecutableCompilationUnit::create(std::move(unit), this)); + // runtime data should not be initialized yet, so we don't need to mark the CU + Q_ASSERT(!(*executableUnit)->runtimeStrings); + return *executableUnit; +} + +QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::insertCompilationUnit(QQmlRefPointer<CompiledData::CompilationUnit> &&unit) { + QUrl url = unit->finalUrl(); + auto executableUnit = ExecutableCompilationUnit::create(std::move(unit), this); + /* Compilation Units stored in the engine are part of the gc roots, + so we don't trigger any write-barrier when they are added. Use + markCustom to make sure they are still marked when we insert them */ + QV4::WriteBarrier::markCustom(this, [&executableUnit](QV4::MarkStack *ms) { + executableUnit->markObjects(ms); + }); + return *m_compilationUnits.insert(std::move(url), std::move(executableUnit)); +} + +void ExecutionEngine::trimCompilationUnits() +{ + for (auto it = m_compilationUnits.begin(); it != m_compilationUnits.end();) { + if ((*it)->count() == 1) + it = m_compilationUnits.erase(it); + else + ++it; + } +} + +ExecutionEngine::Module ExecutionEngine::moduleForUrl( + const QUrl &url, const ExecutableCompilationUnit *referrer) const +{ + const auto nativeModule = nativeModules.find(url); + if (nativeModule != nativeModules.end()) + return Module { nullptr, *nativeModule }; + + const QUrl resolved = referrer + ? referrer->finalUrl().resolved(QQmlTypeLoader::normalize(url)) + : QQmlTypeLoader::normalize(url); + auto existingModule = m_compilationUnits.find(resolved); + if (existingModule == m_compilationUnits.end()) + return Module { nullptr, nullptr }; + return Module { *existingModule, nullptr }; } -QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::moduleForUrl(const QUrl &_url, const ExecutableCompilationUnit *referrer) const +ExecutionEngine::Module ExecutionEngine::loadModule(const QUrl &url, const ExecutableCompilationUnit *referrer) { - QUrl url = QQmlTypeLoader::normalize(_url); - if (referrer) - url = referrer->finalUrl().resolved(url); + const auto nativeModule = nativeModules.constFind(url); + if (nativeModule != nativeModules.cend()) + return Module { nullptr, *nativeModule }; - QMutexLocker moduleGuard(&moduleMutex); - auto existingModule = modules.find(url); - if (existingModule == modules.end()) + const QUrl resolved = referrer + ? referrer->finalUrl().resolved(QQmlTypeLoader::normalize(url)) + : QQmlTypeLoader::normalize(url); + auto existingModule = m_compilationUnits.constFind(resolved); + if (existingModule != m_compilationUnits.cend()) + return Module { *existingModule, nullptr }; + + auto newModule = compileModule(resolved); + Q_ASSERT(!newModule || m_compilationUnits.contains(resolved, newModule)); + + return Module { newModule, nullptr }; +} + +QV4::Value *ExecutionEngine::registerNativeModule(const QUrl &url, const QV4::Value &module) +{ + const auto existingModule = nativeModules.constFind(url); + if (existingModule != nativeModules.cend()) return nullptr; - return *existingModule; + + QV4::Value *val = this->memoryManager->m_persistentValues->allocate(); + *val = module.asReturnedValue(); + nativeModules.insert(url, val); + + // Make sure the type loader doesn't try to resolve the script anymore. + if (m_qmlEngine) + QQmlEnginePrivate::get(m_qmlEngine)->typeLoader.injectScript(url, *val); + + return val; } -QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::loadModule(const QUrl &_url, const ExecutableCompilationUnit *referrer) +static ExecutionEngine::DiskCacheOptions transFormDiskCache(const char *v) { - QUrl url = QQmlTypeLoader::normalize(_url); - if (referrer) - url = referrer->finalUrl().resolved(url); + using DiskCache = ExecutionEngine::DiskCache; + + if (v == nullptr) + return DiskCache::Enabled; + + ExecutionEngine::DiskCacheOptions result = DiskCache::Disabled; + const QList<QByteArray> options = QByteArray(v).split(','); + for (const QByteArray &option : options) { + if (option == "aot-bytecode") + result |= DiskCache::AotByteCode; + else if (option == "aot-native") + result |= DiskCache::AotNative; + else if (option == "aot") + result |= DiskCache::Aot; + else if (option == "qmlc-read") + result |= DiskCache::QmlcRead; + else if (option == "qmlc-write") + result |= DiskCache::QmlcWrite; + else if (option == "qmlc") + result |= DiskCache::Qmlc; + else + qWarning() << "Ignoring unknown option to QML_DISK_CACHE:" << option; + } - QMutexLocker moduleGuard(&moduleMutex); - auto existingModule = modules.find(url); - if (existingModule != modules.end()) - return *existingModule; + return result; +} - moduleGuard.unlock(); +ExecutionEngine::DiskCacheOptions ExecutionEngine::diskCacheOptions() const +{ + if (forceDiskCache()) + return DiskCache::Enabled; + if (disableDiskCache() || debugger()) + return DiskCache::Disabled; + static const DiskCacheOptions options = qmlGetConfigOption< + DiskCacheOptions, transFormDiskCache>("QML_DISK_CACHE"); + return options; +} - auto newModule = compileModule(url); - if (newModule) { - moduleGuard.relock(); - modules.insert(url, newModule); +void ExecutionEngine::callInContext(QV4::Function *function, QObject *self, + QV4::ExecutionContext *context, int argc, void **args, + QMetaType *types) +{ + if (!args) { + Q_ASSERT(argc == 0); + void *dummyArgs[] = { nullptr }; + QMetaType dummyTypes[] = { QMetaType::fromType<void>() }; + function->call(self, dummyArgs, dummyTypes, argc, context); + return; } + Q_ASSERT(types); // both args and types must be present + // implicitly sets the return value, which is args[0] + function->call(self, args, types, argc, context); +} - return newModule; +QV4::ReturnedValue ExecutionEngine::callInContext(QV4::Function *function, QObject *self, + QV4::ExecutionContext *context, int argc, + const QV4::Value *argv) +{ + QV4::Scope scope(this); + QV4::ScopedObject jsSelf(scope, QV4::QObjectWrapper::wrap(this, self)); + Q_ASSERT(jsSelf); + return function->call(jsSelf, argv, argc, context); } void ExecutionEngine::initQmlGlobalObject() { initializeGlobal(); - freezeObject(*globalObject); + lockObject(*globalObject); } void ExecutionEngine::initializeGlobal() { - QV4::Scope scope(this); - QV4::GlobalExtensions::init(globalObject, QJSEngine::AllExtensions); + createQtObject(); - QV4::ScopedObject qt(scope, memoryManager->allocate<QV4::QtObject>(qmlEngine())); - globalObject->defineDefaultProperty(QStringLiteral("Qt"), qt); + QV4::GlobalExtensions::init(globalObject, QJSEngine::AllExtensions); #if QT_CONFIG(qml_locale) QQmlLocale::registerStringLocaleCompare(this); @@ -1885,6 +2309,25 @@ void ExecutionEngine::initializeGlobal() } } +void ExecutionEngine::createQtObject() +{ + QV4::Scope scope(this); + QtObject *qtObject = new QtObject(this); + QJSEngine::setObjectOwnership(qtObject, QJSEngine::JavaScriptOwnership); + + QV4::ScopedObject qtObjectWrapper( + scope, QV4::QObjectWrapper::wrap(this, qtObject)); + QV4::ScopedObject qtNamespaceWrapper( + scope, QV4::QMetaObjectWrapper::create(this, &Qt::staticMetaObject)); + QV4::ScopedObject qtObjectProtoWrapper( + scope, qtObjectWrapper->getPrototypeOf()); + + qtNamespaceWrapper->setPrototypeOf(qtObjectProtoWrapper); + qtObjectWrapper->setPrototypeOf(qtNamespaceWrapper); + + globalObject->defineDefaultProperty(QStringLiteral("Qt"), qtObjectWrapper); +} + const QSet<QString> &ExecutionEngine::illegalNames() const { return m_illegalNames; @@ -1892,13 +2335,16 @@ const QSet<QString> &ExecutionEngine::illegalNames() const void ExecutionEngine::setQmlEngine(QQmlEngine *engine) { + // Second stage of initialization. We're updating some more prototypes here. + isInitialized = false; m_qmlEngine = engine; initQmlGlobalObject(); + isInitialized = true; } static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object) { - if (object->as<QV4::QObjectWrapper>() || object->internalClass()->isFrozen) + if (object->as<QV4::QObjectWrapper>() || object->internalClass()->isFrozen()) return; QV4::Scope scope(v4); @@ -1935,6 +2381,58 @@ void ExecutionEngine::freezeObject(const QV4::Value &value) freeze_recursive(this, o); } +void ExecutionEngine::lockObject(const QV4::Value &value) +{ + QV4::Scope scope(this); + ScopedObject object(scope, value); + if (!object) + return; + + std::vector<Heap::Object *> stack { object->d() }; + + // Methods meant to be overridden + const PropertyKey writableMembers[] = { + id_toString()->propertyKey(), + id_toLocaleString()->propertyKey(), + id_valueOf()->propertyKey(), + id_constructor()->propertyKey() + }; + const auto writableBegin = std::begin(writableMembers); + const auto writableEnd = std::end(writableMembers); + + while (!stack.empty()) { + object = stack.back(); + stack.pop_back(); + + if (object->as<QV4::QObjectWrapper>() || object->internalClass()->isLocked()) + continue; + + Scoped<InternalClass> locked(scope, object->internalClass()->locked()); + QV4::ScopedObject member(scope); + + // Taking this copy is cheap. It's refcounted. This avoids keeping a reference + // to the original IC. + const SharedInternalClassData<PropertyKey> nameMap = locked->d()->nameMap; + + for (uint i = 0, end = locked->d()->size; i < end; ++i) { + const PropertyKey key = nameMap.at(i); + if (!key.isStringOrSymbol()) + continue; + if ((member = *object->propertyData(i))) { + stack.push_back(member->d()); + if (std::find(writableBegin, writableEnd, key) == writableEnd) { + PropertyAttributes attributes = locked->d()->find(key).attributes; + attributes.setConfigurable(false); + attributes.setWritable(false); + locked = locked->changeMember(key, attributes); + } + } + } + + object->setInternalClass(locked->d()); + } +} + void ExecutionEngine::startTimer(const QString &timerName) { if (!m_time.isValid()) @@ -1964,7 +2462,7 @@ int ExecutionEngine::consoleCountHelper(const QString &file, quint16 line, quint void ExecutionEngine::setExtensionData(int index, Deletable *data) { - if (m_extensionData.count() <= index) + if (m_extensionData.size() <= index) m_extensionData.resize(index + 1); if (m_extensionData.at(index)) @@ -1973,97 +2471,163 @@ void ExecutionEngine::setExtensionData(int index, Deletable *data) m_extensionData[index] = data; } -// Converts a JS value to a meta-type. -// data must point to a place that can store a value of the given type. -// Returns true if conversion succeeded, false otherwise. -bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) +template<typename Source> +bool convertToIterable(QMetaType metaType, void *data, Source *sequence) +{ + QSequentialIterable iterable; + if (!QMetaType::view(metaType, data, QMetaType::fromType<QSequentialIterable>(), &iterable)) + return false; + + const QMetaType elementMetaType = iterable.valueMetaType(); + for (qsizetype i = 0, end = sequence->getLength(); i < end; ++i) { + QVariant element(elementMetaType); + ExecutionEngine::metaTypeFromJS(sequence->get(i), elementMetaType, element.data()); + iterable.addValue(element, QSequentialIterable::AtEnd); + } + return true; +} + +/*! + * \internal + * + * Converts a JS value to a meta-type. + * \a data must point to a default-constructed instance of \a metaType. + * Returns \c true if conversion succeeded, \c false otherwise. In the latter case, + * \a data is not modified. + */ +bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, void *data) { // check if it's one of the types we know - switch (QMetaType::Type(type)) { + switch (metaType.id()) { case QMetaType::Bool: - *reinterpret_cast<bool*>(data) = value->toBoolean(); + *reinterpret_cast<bool*>(data) = value.toBoolean(); return true; case QMetaType::Int: - *reinterpret_cast<int*>(data) = value->toInt32(); + *reinterpret_cast<int*>(data) = value.toInt32(); return true; case QMetaType::UInt: - *reinterpret_cast<uint*>(data) = value->toUInt32(); + *reinterpret_cast<uint*>(data) = value.toUInt32(); + return true; + case QMetaType::Long: + *reinterpret_cast<long*>(data) = long(value.toInteger()); + return true; + case QMetaType::ULong: + *reinterpret_cast<ulong*>(data) = ulong(value.toInteger()); return true; case QMetaType::LongLong: - *reinterpret_cast<qlonglong*>(data) = qlonglong(value->toInteger()); + *reinterpret_cast<qlonglong*>(data) = qlonglong(value.toInteger()); return true; case QMetaType::ULongLong: - *reinterpret_cast<qulonglong*>(data) = qulonglong(value->toInteger()); + *reinterpret_cast<qulonglong*>(data) = qulonglong(value.toInteger()); return true; case QMetaType::Double: - *reinterpret_cast<double*>(data) = value->toNumber(); + *reinterpret_cast<double*>(data) = value.toNumber(); return true; case QMetaType::QString: - if (value->isUndefined() || value->isNull()) - *reinterpret_cast<QString*>(data) = QString(); + if (value.isUndefined()) + *reinterpret_cast<QString*>(data) = QStringLiteral("undefined"); + else if (value.isNull()) + *reinterpret_cast<QString*>(data) = QStringLiteral("null"); else - *reinterpret_cast<QString*>(data) = value->toQString(); + *reinterpret_cast<QString*>(data) = value.toQString(); return true; case QMetaType::QByteArray: - if (const ArrayBuffer *ab = value->as<ArrayBuffer>()) + if (const ArrayBuffer *ab = value.as<ArrayBuffer>()) { *reinterpret_cast<QByteArray*>(data) = ab->asByteArray(); - else + } else if (const String *string = value.as<String>()) { + *reinterpret_cast<QByteArray*>(data) = string->toQString().toUtf8(); + } else if (const ArrayObject *ao = value.as<ArrayObject>()) { + // Since QByteArray is sequentially iterable, we have to construct it from a JS Array. + QByteArray result; + const qint64 length = ao->getLength(); + result.reserve(length); + for (qint64 i = 0; i < length; ++i) { + char value = 0; + ExecutionEngine::metaTypeFromJS(ao->get(i), QMetaType::fromType<char>(), &value); + result.push_back(value); + } + *reinterpret_cast<QByteArray*>(data) = std::move(result); + } else { *reinterpret_cast<QByteArray*>(data) = QByteArray(); + } return true; case QMetaType::Float: - *reinterpret_cast<float*>(data) = value->toNumber(); + *reinterpret_cast<float*>(data) = value.toNumber(); return true; case QMetaType::Short: - *reinterpret_cast<short*>(data) = short(value->toInt32()); + *reinterpret_cast<short*>(data) = short(value.toInt32()); return true; case QMetaType::UShort: - *reinterpret_cast<unsigned short*>(data) = value->toUInt16(); + *reinterpret_cast<unsigned short*>(data) = value.toUInt16(); return true; case QMetaType::Char: - *reinterpret_cast<char*>(data) = char(value->toInt32()); + *reinterpret_cast<char*>(data) = char(value.toInt32()); return true; case QMetaType::UChar: - *reinterpret_cast<unsigned char*>(data) = (unsigned char)(value->toInt32()); + *reinterpret_cast<unsigned char*>(data) = (unsigned char)(value.toInt32()); + return true; + case QMetaType::SChar: + *reinterpret_cast<signed char*>(data) = (signed char)(value.toInt32()); return true; case QMetaType::QChar: - if (String *s = value->stringValue()) { + if (String *s = value.stringValue()) { QString str = s->toQString(); *reinterpret_cast<QChar*>(data) = str.isEmpty() ? QChar() : str.at(0); } else { - *reinterpret_cast<QChar*>(data) = QChar(ushort(value->toUInt16())); + *reinterpret_cast<QChar*>(data) = QChar(ushort(value.toUInt16())); } return true; case QMetaType::QDateTime: - if (const QV4::DateObject *d = value->as<DateObject>()) { + if (const QV4::DateObject *d = value.as<DateObject>()) { *reinterpret_cast<QDateTime *>(data) = d->toQDateTime(); return true; } break; case QMetaType::QDate: - if (const QV4::DateObject *d = value->as<DateObject>()) { - *reinterpret_cast<QDate *>(data) = d->toQDateTime().date(); + if (const QV4::DateObject *d = value.as<DateObject>()) { + *reinterpret_cast<QDate *>(data) = DateObject::dateTimeToDate(d->toQDateTime()); return true; } break; - case QMetaType::QRegExp: - if (const QV4::RegExpObject *r = value->as<QV4::RegExpObject>()) { - *reinterpret_cast<QRegExp *>(data) = r->toQRegExp(); + case QMetaType::QTime: + if (const QV4::DateObject *d = value.as<DateObject>()) { + *reinterpret_cast<QTime *>(data) = d->toQDateTime().time(); return true; } break; + case QMetaType::QUrl: + if (String *s = value.stringValue()) { + *reinterpret_cast<QUrl *>(data) = QUrl(s->toQString()); + return true; + } else if (const QV4::UrlObject *d = value.as<UrlObject>()) { + *reinterpret_cast<QUrl *>(data) = d->toQUrl(); + return true; + } else if (const QV4::VariantObject *d = value.as<VariantObject>()) { + const QVariant *variant = &d->d()->data(); + if (variant->metaType() == QMetaType::fromType<QUrl>()) { + *reinterpret_cast<QUrl *>(data) + = *reinterpret_cast<const QUrl *>(variant->constData()); + return true; + } + } + break; #if QT_CONFIG(regularexpression) case QMetaType::QRegularExpression: - if (const QV4::RegExpObject *r = value->as<QV4::RegExpObject>()) { + if (const QV4::RegExpObject *r = value.as<QV4::RegExpObject>()) { *reinterpret_cast<QRegularExpression *>(data) = r->toQRegularExpression(); return true; } break; #endif case QMetaType::QObjectStar: { - const QV4::QObjectWrapper *qobjectWrapper = value->as<QV4::QObjectWrapper>(); - if (qobjectWrapper || value->isNull()) { - *reinterpret_cast<QObject* *>(data) = qtObjectFromJS(this, *value); + if (value.isNull()) { + *reinterpret_cast<QObject* *>(data) = nullptr; return true; - } break; + } + if (value.as<QV4::QObjectWrapper>()) { + *reinterpret_cast<QObject* *>(data) = qtObjectFromJS(value); + return true; + } + break; } case QMetaType::QStringList: { - const QV4::ArrayObject *a = value->as<QV4::ArrayObject>(); + const QV4::ArrayObject *a = value.as<QV4::ArrayObject>(); if (a) { *reinterpret_cast<QStringList *>(data) = a->toQStringList(); return true; @@ -2071,33 +2635,48 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) break; } case QMetaType::QVariantList: { - const QV4::ArrayObject *a = value->as<QV4::ArrayObject>(); + const QV4::ArrayObject *a = value.as<QV4::ArrayObject>(); if (a) { - *reinterpret_cast<QVariantList *>(data) = toVariant(*a, /*typeHint*/-1, /*createJSValueForObjects*/false).toList(); + *reinterpret_cast<QVariantList *>(data) = ExecutionEngine::toVariant( + *a, /*typeHint*/QMetaType{}, /*createJSValueForObjectsAndSymbols*/false) + .toList(); return true; } break; } case QMetaType::QVariantMap: { - const QV4::Object *o = value->as<QV4::Object>(); + const QV4::Object *o = value.as<QV4::Object>(); if (o) { - *reinterpret_cast<QVariantMap *>(data) = variantMapFromJS(o); + *reinterpret_cast<QVariantMap *>(data) = o->engine()->variantMapFromJS(o); return true; } break; } case QMetaType::QVariant: - *reinterpret_cast<QVariant*>(data) = toVariant(*value, /*typeHint*/-1, /*createJSValueForObjects*/false); + if (value.as<QV4::Managed>()) { + *reinterpret_cast<QVariant*>(data) = ExecutionEngine::toVariant( + value, /*typeHint*/QMetaType{}, /*createJSValueForObjectsAndSymbols*/false); + } else if (value.isNull()) { + *reinterpret_cast<QVariant*>(data) = QVariant::fromValue(nullptr); + } else if (value.isUndefined()) { + *reinterpret_cast<QVariant*>(data) = QVariant(); + } else if (value.isBoolean()) { + *reinterpret_cast<QVariant*>(data) = QVariant(value.booleanValue()); + } else if (value.isInteger()) { + *reinterpret_cast<QVariant*>(data) = QVariant(value.integerValue()); + } else if (value.isDouble()) { + *reinterpret_cast<QVariant*>(data) = QVariant(value.doubleValue()); + } return true; case QMetaType::QJsonValue: - *reinterpret_cast<QJsonValue *>(data) = QV4::JsonObject::toJsonValue(*value); + *reinterpret_cast<QJsonValue *>(data) = QV4::JsonObject::toJsonValue(value); return true; case QMetaType::QJsonObject: { - *reinterpret_cast<QJsonObject *>(data) = QV4::JsonObject::toJsonObject(value->as<Object>()); + *reinterpret_cast<QJsonObject *>(data) = QV4::JsonObject::toJsonObject(value.as<Object>()); return true; } case QMetaType::QJsonArray: { - const QV4::ArrayObject *a = value->as<ArrayObject>(); + const QV4::ArrayObject *a = value.as<ArrayObject>(); if (a) { *reinterpret_cast<QJsonArray *>(data) = JsonObject::toJsonArray(a); return true; @@ -2105,92 +2684,166 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) break; } default: - ; + break; } - { - const QQmlValueTypeWrapper *vtw = value->as<QQmlValueTypeWrapper>(); - if (vtw && vtw->typeId() == type) { - return vtw->toGadget(data); - } + if (metaType.flags() & QMetaType::IsEnumeration) { + *reinterpret_cast<int *>(data) = value.toInt32(); + return true; } -#if 0 - if (isQtVariant(value)) { - const QVariant &var = variantValue(value); - // ### Enable once constructInPlace() is in qt master. - if (var.userType() == type) { - QMetaType::constructInPlace(type, data, var.constData()); + if (const QV4::QmlListWrapper *wrapper = value.as<QV4::QmlListWrapper>()) { + if (metaType == QMetaType::fromType<QQmlListReference>()) { + *reinterpret_cast<QQmlListReference *>(data) = wrapper->toListReference(); return true; } - if (var.canConvert(type)) { - QVariant vv = var; - vv.convert(type); - Q_ASSERT(vv.userType() == type); - QMetaType::constructInPlace(type, data, vv.constData()); + + const auto wrapperPrivate = wrapper->d(); + if (wrapperPrivate->propertyType() == metaType) { + *reinterpret_cast<QQmlListProperty<QObject> *>(data) = *wrapperPrivate->property(); return true; } + } + if (const QQmlValueTypeWrapper *vtw = value.as<QQmlValueTypeWrapper>()) { + const QMetaType valueType = vtw->type(); + if (valueType == metaType) + return vtw->toGadget(data); + + Heap::QQmlValueTypeWrapper *d = vtw->d(); + if (d->isReference()) + d->readReference(); + + if (void *gadgetPtr = d->gadgetPtr()) { + if (QQmlValueTypeProvider::populateValueType(metaType, data, valueType, gadgetPtr)) + return true; + if (QMetaType::canConvert(valueType, metaType)) + return QMetaType::convert(valueType, gadgetPtr, metaType, data); + } else { + QVariant empty(valueType); + if (QQmlValueTypeProvider::populateValueType(metaType, data, valueType, empty.data())) + return true; + if (QMetaType::canConvert(valueType, metaType)) + return QMetaType::convert(valueType, empty.data(), metaType, data); + } } -#endif // Try to use magic; for compatibility with qjsvalue_cast. - QByteArray name = QMetaType::typeName(type); - if (convertToNativeQObject(this, *value, name, reinterpret_cast<void* *>(data))) + if (convertToNativeQObject(value, metaType, reinterpret_cast<void **>(data))) return true; - if (value->as<QV4::VariantObject>() && name.endsWith('*')) { - int valueType = QMetaType::type(name.left(name.size()-1)); - QVariant &var = value->as<QV4::VariantObject>()->d()->data(); - if (valueType == var.userType()) { - // We have T t, T* is requested, so return &t. - *reinterpret_cast<void* *>(data) = var.data(); + + const bool isPointer = (metaType.flags() & QMetaType::IsPointer); + const QV4::VariantObject *variantObject = value.as<QV4::VariantObject>(); + if (variantObject) { + // Actually a reference, because we're poking it for its data() below and we want + // the _original_ data, not some copy. + QVariant &var = variantObject->d()->data(); + + if (var.metaType() == metaType) { + metaType.destruct(data); + metaType.construct(data, var.data()); return true; - } else if (Object *o = value->objectValue()) { - // Look in the prototype chain. - QV4::Scope scope(this); - QV4::ScopedObject proto(scope, o->getPrototypeOf()); - while (proto) { - bool canCast = false; - if (QV4::VariantObject *vo = proto->as<QV4::VariantObject>()) { - const QVariant &v = vo->d()->data(); - canCast = (type == v.userType()) || (valueType && (valueType == v.userType())); - } - else if (proto->as<QV4::QObjectWrapper>()) { - QByteArray className = name.left(name.size()-1); - QV4::ScopedObject p(scope, proto.getPointer()); - if (QObject *qobject = qtObjectFromJS(this, p)) - canCast = qobject->qt_metacast(className) != nullptr; - } - if (canCast) { - QByteArray varTypeName = QMetaType::typeName(var.userType()); - if (varTypeName.endsWith('*')) - *reinterpret_cast<void* *>(data) = *reinterpret_cast<void* *>(var.data()); - else - *reinterpret_cast<void* *>(data) = var.data(); - return true; + } + + if (isPointer) { + const QByteArray pointedToTypeName = QByteArray(metaType.name()).chopped(1); + const QMetaType valueType = QMetaType::fromName(pointedToTypeName); + + if (valueType == var.metaType()) { + // ### Qt7: Remove this. Returning pointers to potentially gc'd data is crazy. + // We have T t, T* is requested, so return &t. + *reinterpret_cast<const void **>(data) = var.data(); + return true; + } else if (Object *o = value.objectValue()) { + // Look in the prototype chain. + QV4::Scope scope(o->engine()); + QV4::ScopedObject proto(scope, o->getPrototypeOf()); + while (proto) { + bool canCast = false; + if (QV4::VariantObject *vo = proto->as<QV4::VariantObject>()) { + const QVariant &v = vo->d()->data(); + canCast = (metaType == v.metaType()); + } + else if (proto->as<QV4::QObjectWrapper>()) { + QV4::ScopedObject p(scope, proto.getPointer()); + if (QObject *qobject = qtObjectFromJS(p)) { + if (const QMetaObject *metaObject = metaType.metaObject()) + canCast = metaObject->cast(qobject) != nullptr; + else + canCast = qobject->qt_metacast(pointedToTypeName); + } + } + if (canCast) { + const QMetaType varType = var.metaType(); + if (varType.flags() & QMetaType::IsPointer) { + *reinterpret_cast<const void **>(data) + = *reinterpret_cast<void *const *>(var.data()); + } else { + *reinterpret_cast<const void **>(data) = var.data(); + } + return true; + } + proto = proto->getPrototypeOf(); } - proto = proto->getPrototypeOf(); } + } else if (QQmlValueTypeProvider::populateValueType( + metaType, data, var.metaType(), var.data())) { + return true; } - } else if (value->isNull() && name.endsWith('*')) { + } else if (value.isNull() && isPointer) { *reinterpret_cast<void* *>(data) = nullptr; return true; - } else if (type == qMetaTypeId<QJSValue>()) { - *reinterpret_cast<QJSValue*>(data) = QJSValue(this, value->asReturnedValue()); + } else if (metaType == QMetaType::fromType<QJSValue>()) { + QJSValuePrivate::setValue(reinterpret_cast<QJSValue*>(data), value.asReturnedValue()); + return true; + } else if (metaType == QMetaType::fromType<QJSPrimitiveValue>()) { + *reinterpret_cast<QJSPrimitiveValue *>(data) = createPrimitive(&value); return true; + } else if (!isPointer) { + if (QQmlValueTypeProvider::populateValueType(metaType, data, value)) + return true; + } + + if (const QV4::Sequence *sequence = value.as<Sequence>()) { + const QVariant result = QV4::SequencePrototype::toVariant(sequence); + if (result.metaType() == metaType) { + metaType.destruct(data); + metaType.construct(data, result.constData()); + return true; + } + + if (convertToIterable(metaType, data, sequence)) + return true; + } + + if (const QV4::ArrayObject *array = value.as<ArrayObject>()) { + if (convertToIterable(metaType, data, array)) + return true; } return false; } -static bool convertToNativeQObject(QV4::ExecutionEngine *e, const QV4::Value &value, const QByteArray &targetType, void **result) +static bool convertToNativeQObject(const QV4::Value &value, QMetaType targetType, void **result) { - if (!targetType.endsWith('*')) + if (!(targetType.flags() & QMetaType::IsPointer)) return false; - if (QObject *qobject = qtObjectFromJS(e, value)) { - int start = targetType.startsWith("const ") ? 6 : 0; - QByteArray className = targetType.mid(start, targetType.size()-start-1); + if (QObject *qobject = qtObjectFromJS(value)) { + // If the target type has a metaObject, use that for casting. + if (const QMetaObject *targetMetaObject = targetType.metaObject()) { + if (QObject *instance = targetMetaObject->cast(qobject)) { + *result = instance; + return true; + } + return false; + } + + // We have to call the generated qt_metacast rather than metaObject->cast() here so that + // it works for types without QMetaObject, such as QStandardItem. + const QByteArray targetTypeName = targetType.name(); + const int start = targetTypeName.startsWith("const ") ? 6 : 0; + const QByteArray className = targetTypeName.mid(start, targetTypeName.size() - start - 1); if (void *instance = qobject->qt_metacast(className)) { *result = instance; return true; @@ -2199,12 +2852,12 @@ static bool convertToNativeQObject(QV4::ExecutionEngine *e, const QV4::Value &va return false; } -static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const QV4::Value &value) +static QObject *qtObjectFromJS(const QV4::Value &value) { if (!value.isObject()) return nullptr; - QV4::Scope scope(engine); + QV4::Scope scope(value.as<QV4::Managed>()->engine()); QV4::Scoped<QV4::VariantObject> v(scope, value); if (v) { @@ -2214,9 +2867,14 @@ static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const QV4::Value &v return *reinterpret_cast<QObject* const *>(variant.constData()); } QV4::Scoped<QV4::QObjectWrapper> wrapper(scope, value); - if (!wrapper) - return nullptr; - return wrapper->object(); + if (wrapper) + return wrapper->object(); + + QV4::Scoped<QV4::QQmlTypeWrapper> typeWrapper(scope, value); + if (typeWrapper) + return typeWrapper->object(); + + return nullptr; } struct QV4EngineRegistrationData @@ -2238,4 +2896,11 @@ int ExecutionEngine::registerExtension() return registrationData()->extensionCount++; } +#if QT_CONFIG(qml_network) +QNetworkAccessManager *QV4::detail::getNetworkAccessManager(ExecutionEngine *engine) +{ + return engine->qmlEngine()->networkAccessManager(); +} +#endif // qml_network + QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index d233347060..0958ab3ab5 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4ENGINE_H #define QV4ENGINE_H @@ -50,20 +14,21 @@ // We mean it. // -#include "qv4global_p.h" -#include "qv4managed_p.h" -#include "qv4context_p.h" -#include "qv4stackframe_p.h" #include <private/qintrusivelist_p.h> -#include "qv4enginebase_p.h" -#include <private/qqmlrefcount_p.h> #include <private/qqmldelayedcallqueue_p.h> -#include <QtCore/qelapsedtimer.h> -#include <QtCore/qmutex.h> - -#include "qv4function_p.h" +#include <private/qqmlrefcount_p.h> #include <private/qv4compileddata_p.h> +#include <private/qv4context_p.h> +#include <private/qv4enginebase_p.h> #include <private/qv4executablecompilationunit_p.h> +#include <private/qv4function_p.h> +#include <private/qv4global_p.h> +#include <private/qv4stacklimits_p.h> + +#include <QtCore/qelapsedtimer.h> +#include <QtCore/qmutex.h> +#include <QtCore/qprocessordetection.h> +#include <QtCore/qset.h> namespace WTF { class BumpPointerAllocator; @@ -91,7 +56,18 @@ class PageAllocation; QT_BEGIN_NAMESPACE +#if QT_CONFIG(qml_network) +class QNetworkAccessManager; + +namespace QV4 { +struct QObjectMethod; +namespace detail { +QNetworkAccessManager *getNetworkAccessManager(ExecutionEngine *engine); +} +} +#else namespace QV4 { struct QObjectMethod; } +#endif // qml_network // Used to allow a QObject method take and return raw V4 handles without having to expose // 48 in the public API. @@ -99,7 +75,7 @@ namespace QV4 { struct QObjectMethod; } // class MyClass : public QObject { // Q_OBJECT // ... -// Q_INVOKABLE void myMethod(QQmlV4Function*); +// Q_INVOKABLE void myMethod(QQmlV4FunctionPtr); // }; // The QQmlV8Function - and consequently the arguments and return value - only remains // valid during the call. If the return value isn't set within myMethod(), the will return @@ -133,6 +109,7 @@ class QQmlError; class QJSEngine; class QQmlEngine; class QQmlContextData; +class QQmlTypeLoader; namespace QV4 { namespace Debugging { @@ -158,12 +135,24 @@ class ReactionHandler; struct Q_QML_EXPORT ExecutionEngine : public EngineBase { private: - static qint32 maxCallDepth; - friend struct ExecutionContextSaver; friend struct ExecutionContext; friend struct Heap::ExecutionContext; public: + enum class DiskCache { + Disabled = 0, + AotByteCode = 1 << 0, + AotNative = 1 << 1, + QmlcRead = 1 << 2, + QmlcWrite = 1 << 3, + Aot = AotByteCode | AotNative, + Qmlc = QmlcRead | QmlcWrite, + Enabled = Aot | Qmlc, + + }; + + Q_DECLARE_FLAGS(DiskCacheOptions, DiskCache); + ExecutableAllocator *executableAllocator; ExecutableAllocator *regExpAllocator; @@ -185,6 +174,14 @@ public: QQmlEngine *qmlEngine() const { return m_qmlEngine; } QJSEngine *publicEngine; + template<typename TypeLoader = QQmlTypeLoader> + TypeLoader *typeLoader() + { + if (m_qmlEngine) + return TypeLoader::get(m_qmlEngine); + return nullptr; + } + enum JSObjects { RootContext, ScriptContext, @@ -210,9 +207,7 @@ public: URIErrorProto, PromiseProto, VariantProto, -#if QT_CONFIG(qml_sequence_object) SequenceProto, -#endif SharedArrayBufferProto, ArrayBufferProto, DataViewProto, @@ -222,6 +217,7 @@ public: MapProto, IntrinsicTypedArrayProto, ValueTypeProto, + TypeWrapperProto, SignalHandlerProto, IteratorProto, ForInIteratorProto, @@ -229,6 +225,8 @@ public: MapIteratorProto, ArrayIteratorProto, StringIteratorProto, + UrlProto, + UrlSearchParamsProto, Object_Ctor, String_Ctor, @@ -256,6 +254,8 @@ public: WeakMap_Ctor, Map_Ctor, IntrinsicTypedArray_Ctor, + Url_Ctor, + UrlSearchParams_Ctor, GetSymbolSpecies, @@ -296,6 +296,14 @@ public: FunctionObject *weakMapCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + WeakMap_Ctor); } FunctionObject *mapCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Map_Ctor); } FunctionObject *intrinsicTypedArrayCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + IntrinsicTypedArray_Ctor); } + FunctionObject *urlCtor() const + { + return reinterpret_cast<FunctionObject *>(jsObjects + Url_Ctor); + } + FunctionObject *urlSearchParamsCtor() const + { + return reinterpret_cast<FunctionObject *>(jsObjects + UrlSearchParams_Ctor); + } FunctionObject *typedArrayCtors; FunctionObject *getSymbolSpecies() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetSymbolSpecies); } @@ -321,9 +329,7 @@ public: Object *uRIErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + URIErrorProto); } Object *promisePrototype() const { return reinterpret_cast<Object *>(jsObjects + PromiseProto); } Object *variantPrototype() const { return reinterpret_cast<Object *>(jsObjects + VariantProto); } -#if QT_CONFIG(qml_sequence_object) Object *sequencePrototype() const { return reinterpret_cast<Object *>(jsObjects + SequenceProto); } -#endif Object *sharedArrayBufferPrototype() const { return reinterpret_cast<Object *>(jsObjects + SharedArrayBufferProto); } Object *arrayBufferPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayBufferProto); } @@ -337,17 +343,24 @@ public: Object *valueTypeWrapperPrototype() const { return reinterpret_cast<Object *>(jsObjects + ValueTypeProto); } Object *signalHandlerPrototype() const { return reinterpret_cast<Object *>(jsObjects + SignalHandlerProto); } + Object *typeWrapperPrototype() const { return reinterpret_cast<Object *>(jsObjects + TypeWrapperProto); } Object *iteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + IteratorProto); } Object *forInIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ForInIteratorProto); } Object *setIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + SetIteratorProto); } Object *mapIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + MapIteratorProto); } Object *arrayIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayIteratorProto); } Object *stringIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + StringIteratorProto); } + Object *urlPrototype() const { return reinterpret_cast<Object *>(jsObjects + UrlProto); } + Object *urlSearchParamsPrototype() const { return reinterpret_cast<Object *>(jsObjects + UrlSearchParamsProto); } EvalFunction *evalFunction() const { return reinterpret_cast<EvalFunction *>(jsObjects + Eval_Function); } FunctionObject *getStackFunction() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetStack_Function); } FunctionObject *thrower() const { return reinterpret_cast<FunctionObject *>(jsObjects + ThrowerObject); } +#if QT_CONFIG(qml_network) + QNetworkAccessManager* (*networkAccessManager)(ExecutionEngine*) = detail::getNetworkAccessManager; +#endif + enum JSStrings { String_Empty, String_undefined, @@ -485,8 +498,6 @@ public: Symbol *symbol_unscopables() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_unscopables); } Symbol *symbol_revokableProxy() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_revokableProxy); } - QIntrusiveList<ExecutableCompilationUnit, &ExecutableCompilationUnit::nextCompilationUnit> compilationUnits; - quint32 m_engineId; RegExpCache *regExpCache; @@ -499,7 +510,8 @@ public: // calling preserve() on the object which removes it from this scarceResource list. class ScarceResourceData { public: - ScarceResourceData(const QVariant &data = QVariant()) : data(data) {} + ScarceResourceData() = default; + ScarceResourceData(const QMetaType type, const void *data) : data(type, data) {} QVariant data; QIntrusiveListNode node; }; @@ -532,7 +544,14 @@ public: void setProfiler(Profiling::Profiler *profiler); #endif // QT_CONFIG(qml_debug) - ExecutionContext *currentContext() const; + // We don't want to #include <private/qv4stackframe_p.h> here, but we still want + // currentContext() to be inline. Therefore we shift the requirement to provide the + // complete type of CppStackFrame to the caller by making this a template. + template<typename StackFrame = CppStackFrame> + ExecutionContext *currentContext() const + { + return static_cast<const StackFrame *>(currentStackFrame)->context(); + } // ensure we always get odd prototype IDs. This helps make marking in QV4::Lookup fast quintptr newProtoId() { return (protoIdCount += 2); } @@ -542,6 +561,7 @@ public: Heap::Object *newObject(); Heap::Object *newObject(Heap::InternalClass *internalClass); + Heap::String *newString(char16_t c) { return newString(QChar(c)); } Heap::String *newString(const QString &s = QString()); Heap::String *newIdentifier(const QString &text); @@ -558,17 +578,22 @@ public: Heap::ArrayBuffer *newArrayBuffer(const QByteArray &array); Heap::ArrayBuffer *newArrayBuffer(size_t length); - Heap::DateObject *newDateObject(const Value &value); - Heap::DateObject *newDateObject(const QDateTime &dt); - Heap::DateObject *newDateObjectFromTime(const QTime &t); + Heap::DateObject *newDateObject(double dateTime); + Heap::DateObject *newDateObject(const QDateTime &dateTime); + Heap::DateObject *newDateObject(QDate date, Heap::Object *parent, int index, uint flags); + Heap::DateObject *newDateObject(QTime time, Heap::Object *parent, int index, uint flags); + Heap::DateObject *newDateObject(QDateTime dateTime, Heap::Object *parent, int index, uint flags); Heap::RegExpObject *newRegExpObject(const QString &pattern, int flags); Heap::RegExpObject *newRegExpObject(RegExp *re); - Heap::RegExpObject *newRegExpObject(const QRegExp &re); #if QT_CONFIG(regularexpression) Heap::RegExpObject *newRegExpObject(const QRegularExpression &re); #endif + Heap::UrlObject *newUrlObject(); + Heap::UrlObject *newUrlObject(const QUrl &url); + Heap::UrlSearchParamsObject *newUrlSearchParamsObject(); + Heap::Object *newErrorObject(const Value &value); Heap::Object *newErrorObject(const QString &message); Heap::Object *newSyntaxErrorObject(const QString &message, const QString &fileName, int line, int column); @@ -585,16 +610,35 @@ public: Heap::Object *newPromiseObject(const QV4::FunctionObject *thisObject, const QV4::PromiseCapability *capability); Promise::ReactionHandler *getPromiseReactionHandler(); - Heap::Object *newVariantObject(const QVariant &v); + Heap::Object *newVariantObject(const QMetaType type, const void *data); Heap::Object *newForInIteratorObject(Object *o); Heap::Object *newSetIteratorObject(Object *o); Heap::Object *newMapIteratorObject(Object *o); Heap::Object *newArrayIteratorObject(Object *o); + static Heap::ExecutionContext *qmlContext(Heap::ExecutionContext *ctx) + { + Heap::ExecutionContext *outer = ctx->outer; + + if (ctx->type != Heap::ExecutionContext::Type_QmlContext && !outer) + return nullptr; + + while (outer && outer->type != Heap::ExecutionContext::Type_GlobalContext) { + ctx = outer; + outer = ctx->outer; + } + + Q_ASSERT(ctx); + if (ctx->type != Heap::ExecutionContext::Type_QmlContext) + return nullptr; + + return ctx; + } + Heap::QmlContext *qmlContext() const; QObject *qmlScopeObject() const; - QQmlContextData *callingQmlContext() const; + QQmlRefPointer<QQmlContextData> callingQmlContext() const; StackTrace stackTrace(int frameLimit = -1) const; @@ -628,26 +672,34 @@ public: QQmlError catchExceptionAsQmlError(); // variant conversions - QVariant toVariant(const QV4::Value &value, int typeHint, bool createJSValueForObjects = true); + static QVariant toVariant( + const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols = true); + static QVariant toVariantLossy(const QV4::Value &value); QV4::ReturnedValue fromVariant(const QVariant &); + QV4::ReturnedValue fromVariant( + const QVariant &variant, Heap::Object *parent, int property, uint flags); - QVariantMap variantMapFromJS(const QV4::Object *o); + static QVariantMap variantMapFromJS(const QV4::Object *o); - bool metaTypeFromJS(const Value *value, int type, void *data); - QV4::ReturnedValue metaTypeToJS(int type, const void *data); + static bool metaTypeFromJS(const Value &value, QMetaType type, void *data); + QV4::ReturnedValue metaTypeToJS(QMetaType type, const void *data); int maxJSStackSize() const; int maxGCStackSize() const; bool checkStackLimits(); + int safeForAllocLength(qint64 len64); bool canJIT(Function *f = nullptr) { #if QT_CONFIG(qml_jit) if (!m_canAllocateExecutableMemory) return false; - if (f) - return !f->isGenerator() && f->interpreterCallCount >= jitCallCountThreshold; + if (f) { + return f->kind != Function::AotCompiled + && !f->isGenerator() + && f->interpreterCallCount >= s_jitCallCountThreshold; + } return true; #else Q_UNUSED(f); @@ -658,8 +710,10 @@ public: QV4::ReturnedValue global(); void initQmlGlobalObject(); void initializeGlobal(); + void createQtObject(); void freezeObject(const QV4::Value &value); + void lockObject(const QV4::Value &value); // Return the list of illegal id names (the names of the properties on the global object) const QSet<QString> &illegalNames() const; @@ -689,7 +743,7 @@ public: void setExtensionData(int, Deletable *); Deletable *extensionData(int index) const { - if (index < m_extensionData.count()) + if (index < m_extensionData.size()) return m_extensionData[index]; else return nullptr; @@ -701,19 +755,117 @@ public: QQmlRefPointer<ExecutableCompilationUnit> compileModule( const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp); - mutable QMutex moduleMutex; - QHash<QUrl, QQmlRefPointer<ExecutableCompilationUnit>> modules; - void injectModule(const QQmlRefPointer<ExecutableCompilationUnit> &moduleUnit); - QQmlRefPointer<ExecutableCompilationUnit> moduleForUrl(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr) const; - QQmlRefPointer<ExecutableCompilationUnit> loadModule(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr); + QQmlRefPointer<ExecutableCompilationUnit> compilationUnitForUrl(const QUrl &url) const; + + QQmlRefPointer<ExecutableCompilationUnit> executableCompilationUnit( + QQmlRefPointer<QV4::CompiledData::CompilationUnit> &&unit); + + QQmlRefPointer<ExecutableCompilationUnit> insertCompilationUnit( + QQmlRefPointer<QV4::CompiledData::CompilationUnit> &&unit); + + QMultiHash<QUrl, QQmlRefPointer<ExecutableCompilationUnit>> compilationUnits() const + { + return m_compilationUnits; + } + void clearCompilationUnits() { m_compilationUnits.clear(); } + void trimCompilationUnits(); + + QV4::Value *registerNativeModule(const QUrl &url, const QV4::Value &module); + + struct Module { + QQmlRefPointer<ExecutableCompilationUnit> compiled; + + // We can pass a raw value pointer here, but nowhere else. See below. + Value *native = nullptr; + }; + + Module moduleForUrl(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr) const; + Module loadModule(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr); + + DiskCacheOptions diskCacheOptions() const; + + void callInContext(QV4::Function *function, QObject *self, QV4::ExecutionContext *ctxt, + int argc, void **args, QMetaType *types); + QV4::ReturnedValue callInContext(QV4::Function *function, QObject *self, + QV4::ExecutionContext *ctxt, int argc, const QV4::Value *argv); + + QV4::ReturnedValue fromData( + QMetaType type, const void *ptr, + Heap::Object *parent = nullptr, int property = -1, uint flags = 0); + + + static void setMaxCallDepth(int maxCallDepth) { s_maxCallDepth = maxCallDepth; } + static int maxCallDepth() { return s_maxCallDepth; } + + template<typename Value> + static QJSPrimitiveValue createPrimitive(const Value &v) + { + if (v->isUndefined()) + return QJSPrimitiveValue(QJSPrimitiveUndefined()); + if (v->isNull()) + return QJSPrimitiveValue(QJSPrimitiveNull()); + if (v->isBoolean()) + return QJSPrimitiveValue(v->toBoolean()); + if (v->isInteger()) + return QJSPrimitiveValue(v->integerValue()); + if (v->isDouble()) + return QJSPrimitiveValue(v->doubleValue()); + bool ok; + const QString result = v->toQString(&ok); + return ok ? QJSPrimitiveValue(result) : QJSPrimitiveValue(QJSPrimitiveUndefined()); + } private: + template<int Frames> + friend struct ExecutionEngineCallDepthRecorder; + + static void initializeStaticMembers(); + + bool inStack(const void *current) const + { +#if Q_STACK_GROWTH_DIRECTION > 0 + return current < cppStackLimit && current >= cppStackBase; +#else + return current > cppStackLimit && current <= cppStackBase; +#endif + } + + bool hasCppStackOverflow() + { + if (s_maxCallDepth >= 0) + return callDepth >= s_maxCallDepth; + + if (inStack(currentStackPointer())) + return false; + + // Double check the stack limits on failure. + // We may have moved to a different thread. + const StackProperties stack = stackProperties(); + cppStackBase = stack.base; + cppStackLimit = stack.softLimit; + return !inStack(currentStackPointer()); + } + + bool hasJsStackOverflow() const + { + return jsStackTop > jsStackLimit; + } + + bool hasStackOverflow() + { + return hasJsStackOverflow() || hasCppStackOverflow(); + } + + static int s_maxCallDepth; + static int s_jitCallCountThreshold; + static int s_maxJSStackSize; + static int s_maxGCStackSize; + #if QT_CONFIG(qml_debug) QScopedPointer<QV4::Debugging::Debugger> m_debugger; QScopedPointer<QV4::Profiling::Profiler> m_profiler; #endif QSet<QString> m_illegalNames; - int jitCallCountThreshold; // used by generated Promise objects to handle 'then' events QScopedPointer<QV4::Promise::ReactionHandler> m_reactionHandler; @@ -733,24 +885,46 @@ private: QVector<Deletable *> m_extensionData; - int m_maxJSStackSize = 4 * 1024 * 1024; - int m_maxGCStackSize = 2 * 1024 * 1024; + QMultiHash<QUrl, QQmlRefPointer<ExecutableCompilationUnit>> m_compilationUnits; + + // QV4::PersistentValue would be preferred, but using QHash will create copies, + // and QV4::PersistentValue doesn't like creating copies. + // Instead, we allocate a raw pointer using the same manual memory management + // technique in QV4::PersistentValue. + QHash<QUrl, Value *> nativeModules; }; -#define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \ +#define CHECK_STACK_LIMITS(v4) \ + if (v4->checkStackLimits()) \ + return Encode::undefined(); \ ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4); +template<int Frames = 1> struct ExecutionEngineCallDepthRecorder { ExecutionEngine *ee; - ExecutionEngineCallDepthRecorder(ExecutionEngine *e): ee(e) { ++ee->callDepth; } - ~ExecutionEngineCallDepthRecorder() { --ee->callDepth; } + ExecutionEngineCallDepthRecorder(ExecutionEngine *e): ee(e) + { + if (ExecutionEngine::s_maxCallDepth >= 0) + ee->callDepth += Frames; + } + + ~ExecutionEngineCallDepthRecorder() + { + if (ExecutionEngine::s_maxCallDepth >= 0) + ee->callDepth -= Frames; + } + + bool hasOverflow() const + { + return ee->hasCppStackOverflow(); + } }; inline bool ExecutionEngine::checkStackLimits() { - if (Q_UNLIKELY((jsStackTop > jsStackLimit) || (callDepth >= maxCallDepth))) { + if (Q_UNLIKELY(hasStackOverflow())) { throwRangeError(QStringLiteral("Maximum call stack size exceeded.")); return true; } @@ -758,6 +932,8 @@ inline bool ExecutionEngine::checkStackLimits() return false; } +Q_DECLARE_OPERATORS_FOR_FLAGS(ExecutionEngine::DiskCacheOptions); + } // namespace QV4 QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index 788897bdad..68e906baa1 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4ENGINEBASE_P_H #define QV4ENGINEBASE_P_H @@ -60,10 +24,6 @@ namespace QV4 { struct CppStackFrame; // Base class for the execution engine - -#if defined(Q_CC_MSVC) || defined(Q_CC_GNU) -#pragma pack(push, 1) -#endif struct Q_QML_EXPORT EngineBase { CppStackFrame *currentStackFrame = nullptr; @@ -84,16 +44,27 @@ struct Q_QML_EXPORT EngineBase { #endif quint8 isExecutingInRegExpJIT = false; - quint8 padding[3]; + quint8 isInitialized = false; + quint8 inShutdown = false; + quint8 isGCOngoing = false; // incremental gc is ongoing (but mutator might be running) MemoryManager *memoryManager = nullptr; - Runtime runtime; - qint32 callDepth = 0; + union { + const void *cppStackBase = nullptr; + struct { + qint32 callDepth; +#if QT_POINTER_SIZE == 8 + quint32 padding2; +#endif + }; + }; + const void *cppStackLimit = nullptr; + + Object *globalObject = nullptr; Value *jsStackLimit = nullptr; Value *jsStackBase = nullptr; IdentifierTable *identifierTable = nullptr; - Object *globalObject = nullptr; // Exception handling Value *exceptionValue = nullptr; @@ -137,17 +108,14 @@ struct Q_QML_EXPORT EngineBase { Heap::InternalClass *classes[NClasses]; Heap::InternalClass *internalClasses(InternalClassType icType) { return classes[icType]; } }; -#if defined(Q_CC_MSVC) || defined(Q_CC_GNU) -#pragma pack(pop) -#endif Q_STATIC_ASSERT(std::is_standard_layout<EngineBase>::value); Q_STATIC_ASSERT(offsetof(EngineBase, currentStackFrame) == 0); Q_STATIC_ASSERT(offsetof(EngineBase, jsStackTop) == offsetof(EngineBase, currentStackFrame) + QT_POINTER_SIZE); Q_STATIC_ASSERT(offsetof(EngineBase, hasException) == offsetof(EngineBase, jsStackTop) + QT_POINTER_SIZE); Q_STATIC_ASSERT(offsetof(EngineBase, memoryManager) == offsetof(EngineBase, hasException) + 8); -Q_STATIC_ASSERT(offsetof(EngineBase, runtime) == offsetof(EngineBase, memoryManager) + QT_POINTER_SIZE); Q_STATIC_ASSERT(offsetof(EngineBase, isInterrupted) + sizeof(EngineBase::isInterrupted) <= offsetof(EngineBase, hasException) + 4); +Q_STATIC_ASSERT(offsetof(EngineBase, globalObject) % QT_POINTER_SIZE == 0); } diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index 525d3458f4..02145a0243 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4errorobject_p.h" @@ -45,7 +9,6 @@ #include <QtCore/QStringList> #include <QtCore/QDebug> -#include "qv4string_p.h" #include <private/qv4mm_p.h> #include <private/qv4codegen_p.h> @@ -57,7 +20,7 @@ # include "qplatformdefs.h" # endif #else -# include <windows.h> +# include <qt_windows.h> #endif using namespace QV4; @@ -93,7 +56,7 @@ void Heap::ErrorObject::init(const Value &message, ErrorType t) e->d()->stackTrace = new StackTrace(scope.engine->stackTrace()); if (!e->d()->stackTrace->isEmpty()) { setProperty(scope.engine, QV4::ErrorObject::Index_FileName, scope.engine->newString(e->d()->stackTrace->at(0).source)); - setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Value::fromInt32(e->d()->stackTrace->at(0).line)); + setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Value::fromInt32(qAbs(e->d()->stackTrace->at(0).line))); } if (!message.isUndefined()) @@ -121,7 +84,7 @@ void Heap::ErrorObject::init(const Value &message, const QString &fileName, int Q_ASSERT(!e->d()->stackTrace->isEmpty()); setProperty(scope.engine, QV4::ErrorObject::Index_FileName, scope.engine->newString(e->d()->stackTrace->at(0).source)); - setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Value::fromInt32(e->d()->stackTrace->at(0).line)); + setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Value::fromInt32(qAbs(e->d()->stackTrace->at(0).line))); if (!message.isUndefined()) setProperty(scope.engine, QV4::ErrorObject::Index_Message, message); @@ -156,7 +119,7 @@ ReturnedValue ErrorObject::method_get_stack(const FunctionObject *b, const Value return v4->throwTypeError(); if (!This->d()->stack) { QString trace; - for (int i = 0; i < This->d()->stackTrace->count(); ++i) { + for (int i = 0; i < This->d()->stackTrace->size(); ++i) { if (i > 0) trace += QLatin1Char('\n'); const StackFrame &frame = This->d()->stackTrace->at(i); @@ -219,14 +182,14 @@ DEFINE_OBJECT_VTABLE(SyntaxErrorCtor); DEFINE_OBJECT_VTABLE(TypeErrorCtor); DEFINE_OBJECT_VTABLE(URIErrorCtor); -void Heap::ErrorCtor::init(QV4::ExecutionContext *scope) +void Heap::ErrorCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("Error")); + Heap::FunctionObject::init(engine, QStringLiteral("Error")); } -void Heap::ErrorCtor::init(QV4::ExecutionContext *scope, const QString &name) +void Heap::ErrorCtor::init(QV4::ExecutionEngine *engine, const QString &name) { - Heap::FunctionObject::init(scope, name); + Heap::FunctionObject::init(engine, name); } ReturnedValue ErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) @@ -240,9 +203,9 @@ ReturnedValue ErrorCtor::virtualCall(const FunctionObject *f, const Value *, con return f->callAsConstructor(argv, argc); } -void Heap::EvalErrorCtor::init(QV4::ExecutionContext *scope) +void Heap::EvalErrorCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("EvalError")); + Heap::FunctionObject::init(engine, QStringLiteral("EvalError")); } ReturnedValue EvalErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) @@ -251,9 +214,9 @@ ReturnedValue EvalErrorCtor::virtualCallAsConstructor(const FunctionObject *f, c return ErrorObject::create<EvalErrorObject>(f->engine(), v, newTarget)->asReturnedValue(); } -void Heap::RangeErrorCtor::init(QV4::ExecutionContext *scope) +void Heap::RangeErrorCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("RangeError")); + Heap::FunctionObject::init(engine, QStringLiteral("RangeError")); } ReturnedValue RangeErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) @@ -262,9 +225,9 @@ ReturnedValue RangeErrorCtor::virtualCallAsConstructor(const FunctionObject *f, return ErrorObject::create<RangeErrorObject>(f->engine(), v, newTarget)->asReturnedValue(); } -void Heap::ReferenceErrorCtor::init(QV4::ExecutionContext *scope) +void Heap::ReferenceErrorCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("ReferenceError")); + Heap::FunctionObject::init(engine, QStringLiteral("ReferenceError")); } ReturnedValue ReferenceErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) @@ -273,9 +236,9 @@ ReturnedValue ReferenceErrorCtor::virtualCallAsConstructor(const FunctionObject return ErrorObject::create<ReferenceErrorObject>(f->engine(), v, newTarget)->asReturnedValue(); } -void Heap::SyntaxErrorCtor::init(QV4::ExecutionContext *scope) +void Heap::SyntaxErrorCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("SyntaxError")); + Heap::FunctionObject::init(engine, QStringLiteral("SyntaxError")); } ReturnedValue SyntaxErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) @@ -284,9 +247,9 @@ ReturnedValue SyntaxErrorCtor::virtualCallAsConstructor(const FunctionObject *f, return ErrorObject::create<SyntaxErrorObject>(f->engine(), v, newTarget)->asReturnedValue(); } -void Heap::TypeErrorCtor::init(QV4::ExecutionContext *scope) +void Heap::TypeErrorCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("TypeError")); + Heap::FunctionObject::init(engine, QStringLiteral("TypeError")); } ReturnedValue TypeErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) @@ -295,9 +258,9 @@ ReturnedValue TypeErrorCtor::virtualCallAsConstructor(const FunctionObject *f, c return ErrorObject::create<TypeErrorObject>(f->engine(), v, newTarget)->asReturnedValue(); } -void Heap::URIErrorCtor::init(QV4::ExecutionContext *scope) +void Heap::URIErrorCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("URIError")); + Heap::FunctionObject::init(engine, QStringLiteral("URIError")); } ReturnedValue URIErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index 139bcc9754..f9adbb443b 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4ERROROBJECT_H #define QV4ERROROBJECT_H @@ -52,7 +16,6 @@ #include "qv4object_p.h" #include "qv4functionobject_p.h" -#include "qv4string_p.h" QT_BEGIN_NAMESPACE @@ -67,7 +30,7 @@ namespace Heap { Member(class, Pointer, String *, stack) DECLARE_HEAP_OBJECT(ErrorObject, Object) { - DECLARE_MARKOBJECTS(ErrorObject); + DECLARE_MARKOBJECTS(ErrorObject) enum ErrorType { Error, EvalError, @@ -116,32 +79,32 @@ struct URIErrorObject : ErrorObject { }; struct ErrorCtor : FunctionObject { - void init(QV4::ExecutionContext *scope); - void init(QV4::ExecutionContext *scope, const QString &name); + void init(ExecutionEngine *engine); + void init(ExecutionEngine *engine, const QString &name); }; struct EvalErrorCtor : ErrorCtor { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; struct RangeErrorCtor : ErrorCtor { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; struct ReferenceErrorCtor : ErrorCtor { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; struct SyntaxErrorCtor : ErrorCtor { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; struct TypeErrorCtor : ErrorCtor { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; struct URIErrorCtor : ErrorCtor { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; } diff --git a/src/qml/jsruntime/qv4estable.cpp b/src/qml/jsruntime/qv4estable.cpp index 99f6bf6aa0..fb36b10728 100644 --- a/src/qml/jsruntime/qv4estable.cpp +++ b/src/qml/jsruntime/qv4estable.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Crimson AS <info@crimson.no> -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 Crimson AS <info@crimson.no> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4estable_p.h" #include "qv4object_p.h" @@ -147,21 +111,18 @@ ReturnedValue ESTable::get(const Value &key, bool *hasValue) const // Removes the given \a key from the table bool ESTable::remove(const Value &key) { - bool found = false; - uint idx = 0; - for (; idx < m_size; ++idx) { - if (m_keys[idx].sameValueZero(key)) { - found = true; - break; + for (uint index = 0; index < m_size; ++index) { + if (m_keys[index].sameValueZero(key)) { + // Remove the element at |index| by moving all elements to the right + // of |index| one place to the left. + size_t count = (m_size - (index + 1)) * sizeof(Value); + memmove(m_keys + index, m_keys + index + 1, count); + memmove(m_values + index, m_values + index + 1, count); + m_size--; + return true; } } - - if (found == true) { - memmove(m_keys + idx, m_keys + idx + 1, (m_size - idx)*sizeof(Value)); - memmove(m_values + idx, m_values + idx + 1, (m_size - idx)*sizeof(Value)); - m_size--; - } - return found; + return false; } // Returns the size of the table. Note that the size may not match the underlying allocation. diff --git a/src/qml/jsruntime/qv4estable_p.h b/src/qml/jsruntime/qv4estable_p.h index f54fc37a7b..f0c5c7cb81 100644 --- a/src/qml/jsruntime/qv4estable_p.h +++ b/src/qml/jsruntime/qv4estable_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Crimson AS <info@crimson.no> -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 Crimson AS <info@crimson.no> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // // W A R N I N G @@ -53,12 +17,13 @@ #include "qv4value_p.h" +class tst_qv4estable; + QT_BEGIN_NAMESPACE -namespace QV4 -{ +namespace QV4 { -class ESTable +class Q_AUTOTEST_EXPORT ESTable { public: ESTable(); @@ -76,13 +41,15 @@ public: void removeUnmarkedKeys(); private: + friend class ::tst_qv4estable; + Value *m_keys = nullptr; Value *m_values = nullptr; uint m_size = 0; uint m_capacity = 0; }; -} +} // namespace QV4 QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4executableallocator.cpp b/src/qml/jsruntime/qv4executableallocator.cpp index 7ee6f39aa2..71d8061d65 100644 --- a/src/qml/jsruntime/qv4executableallocator.cpp +++ b/src/qml/jsruntime/qv4executableallocator.cpp @@ -1,56 +1,30 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4executableallocator_p.h" -#include "qv4functiontable_p.h" +#include <QtQml/private/qv4functiontable_p.h> #include <wtf/StdLibExtras.h> #include <wtf/PageAllocation.h> using namespace QV4; -void *ExecutableAllocator::Allocation::exceptionHandler() const +void *ExecutableAllocator::Allocation::exceptionHandlerStart() const { return reinterpret_cast<void*>(addr); } -void *ExecutableAllocator::Allocation::start() const +size_t ExecutableAllocator::Allocation::exceptionHandlerSize() const +{ + return QV4::exceptionHandlerSize(); +} + +void *ExecutableAllocator::Allocation::memoryStart() const +{ + return reinterpret_cast<void*>(addr); +} + +void *ExecutableAllocator::Allocation::codeStart() const { return reinterpret_cast<void*>(addr + exceptionHandlerSize()); } @@ -151,7 +125,7 @@ ExecutableAllocator::ExecutableAllocator() ExecutableAllocator::~ExecutableAllocator() { - for (ChunkOfPages *chunk : qAsConst(chunks)) { + for (ChunkOfPages *chunk : std::as_const(chunks)) { for (Allocation *allocation = chunk->firstAllocation; allocation; allocation = allocation->next) if (!allocation->free) allocation->invalidate(); diff --git a/src/qml/jsruntime/qv4executableallocator_p.h b/src/qml/jsruntime/qv4executableallocator_p.h index f98f2c7d33..8181bf17ae 100644 --- a/src/qml/jsruntime/qv4executableallocator_p.h +++ b/src/qml/jsruntime/qv4executableallocator_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4EXECUTABLEALLOCATOR_H #define QV4EXECUTABLEALLOCATOR_H @@ -51,14 +15,14 @@ // We mean it. // -#include "qv4global_p.h" - #include <QMultiMap> #include <QHash> #include <QVector> #include <QByteArray> #include <QMutex> +#include <QtQml/private/qtqmlglobal_p.h> + namespace WTF { class PageAllocation; } @@ -86,8 +50,14 @@ public: , free(true) {} - void *exceptionHandler() const; - void *start() const; + void *memoryStart() const; + size_t memorySize() const { return size; } + + void *exceptionHandlerStart() const; + size_t exceptionHandlerSize() const; + + void *codeStart() const; + void invalidate() { addr = 0; } bool isValid() const { return addr != 0; } void deallocate(ExecutableAllocator *allocator); @@ -109,8 +79,8 @@ public: }; // for debugging / unit-testing - int freeAllocationCount() const { return freeAllocations.count(); } - int chunkCount() const { return chunks.count(); } + int freeAllocationCount() const { return freeAllocations.size(); } + int chunkCount() const { return chunks.size(); } struct ChunkOfPages { @@ -130,7 +100,7 @@ public: private: QMultiMap<size_t, Allocation*> freeAllocations; QMap<quintptr, ChunkOfPages*> chunks; - mutable QRecursiveMutex mutex; + mutable QMutex mutex; }; } diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index d51e986006..34d737cdae 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -1,42 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#include "qml/qqmlprivate.h" +#include "qv4engine_p.h" #include "qv4executablecompilationunit_p.h" #include <private/qv4engine_p.h> @@ -51,31 +17,15 @@ #include <private/qqmlscriptdata_p.h> #include <private/qv4module_p.h> #include <private/qv4compilationunitmapper_p.h> -#include <private/qml_compile_hash_p.h> #include <private/qqmltypewrapper_p.h> +#include <private/qv4resolvedtypereference_p.h> +#include <private/qv4objectiterator_p.h> -#include <QtQml/qqmlfile.h> #include <QtQml/qqmlpropertymap.h> -#include <QtCore/qdir.h> -#include <QtCore/qstandardpaths.h> #include <QtCore/qfileinfo.h> -#include <QtCore/qscopeguard.h> #include <QtCore/qcryptographichash.h> -#if defined(QML_COMPILE_HASH) -# ifdef Q_OS_LINUX -// Place on a separate section on Linux so it's easier to check from outside -// what the hash version is. -__attribute__((section(".qml_compile_hash"))) -# endif -const char qml_compile_hash[48 + 1] = QML_COMPILE_HASH; -static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1, - "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version"); -#else -# error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files" -#endif - QT_BEGIN_NAMESPACE namespace QV4 { @@ -83,24 +33,16 @@ namespace QV4 { ExecutableCompilationUnit::ExecutableCompilationUnit() = default; ExecutableCompilationUnit::ExecutableCompilationUnit( - CompiledData::CompilationUnit &&compilationUnit) - : CompiledData::CompilationUnit(std::move(compilationUnit)) -{} - -ExecutableCompilationUnit::~ExecutableCompilationUnit() + QQmlRefPointer<CompiledData::CompilationUnit> &&compilationUnit) + : m_compilationUnit(std::move(compilationUnit)) { - unlink(); + constants = m_compilationUnit->constants; } -QString ExecutableCompilationUnit::localCacheFilePath(const QUrl &url) +ExecutableCompilationUnit::~ExecutableCompilationUnit() { - const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); - const QString cacheFileSuffix = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix(); - QCryptographicHash fileNameHash(QCryptographicHash::Sha1); - fileNameHash.addData(localSourcePath.toUtf8()); - QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/"); - QDir::root().mkpath(directory); - return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + cacheFileSuffix; + if (engine) + clear(); } static QString toString(QV4::ReturnedValue v) @@ -128,31 +70,36 @@ static void dumpConstantTable(const StaticValue *constants, uint count) } } -QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) +void ExecutableCompilationUnit::populate() { - this->engine = engine; - engine->compilationUnits.insert(this); + /* In general, we should use QV4::Scope whenever we allocate heap objects, and employ write barriers + for member variables pointing to heap objects. However, ExecutableCompilationUnit is special, as it + is always part of the root set. So instead of using scopde allocations and write barriers, we use a + slightly different approach: We temporarily block the gc from running. Afterwards, at the end of the + function we check whether the gc was already running, and mark the ExecutableCompilationUnit. This + ensures that all the newly allocated objects of the compilation unit will be marked in turn. + If the gc was not running, we don't have to do anything, because everything will be marked when the + gc starts marking the root set at the start of a run. + */ + const CompiledData::Unit *data = m_compilationUnit->data; + GCCriticalSection<ExecutableCompilationUnit> criticalSection(engine, this); Q_ASSERT(!runtimeStrings); + Q_ASSERT(engine); Q_ASSERT(data); const quint32 stringCount = totalStringCount(); - runtimeStrings = (QV4::Heap::String **)malloc(stringCount * sizeof(QV4::Heap::String*)); - // memset the strings to 0 in case a GC run happens while we're within the loop below - memset(runtimeStrings, 0, stringCount * sizeof(QV4::Heap::String*)); + runtimeStrings = (QV4::Heap::String **)calloc(stringCount, sizeof(QV4::Heap::String*)); for (uint i = 0; i < stringCount; ++i) runtimeStrings[i] = engine->newString(stringAt(i)); runtimeRegularExpressions = new QV4::Value[data->regexpTableSize]; - // memset the regexps to 0 in case a GC run happens while we're within the loop below - memset(runtimeRegularExpressions, 0, - data->regexpTableSize * sizeof(QV4::Value)); for (uint i = 0; i < data->regexpTableSize; ++i) { const CompiledData::RegExp *re = data->regexpAt(i); - uint f = re->flags; + uint f = re->flags(); const CompiledData::RegExp::Flags flags = static_cast<CompiledData::RegExp::Flags>(f); runtimeRegularExpressions[i] = QV4::RegExp::create( - engine, stringAt(re->stringIndex), flags); + engine, stringAt(re->stringIndex()), flags); } if (data->lookupTableSize) { @@ -163,7 +110,7 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) QV4::Lookup *l = runtimeLookups + i; CompiledData::Lookup::Type type - = CompiledData::Lookup::Type(uint(compiledLookups[i].type_and_flags)); + = CompiledData::Lookup::Type(uint(compiledLookups[i].type())); if (type == CompiledData::Lookup::Type_Getter) l->getter = QV4::Lookup::getterGeneric; else if (type == CompiledData::Lookup::Type_Setter) @@ -172,17 +119,16 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) l->globalGetter = QV4::Lookup::globalGetterGeneric; else if (type == CompiledData::Lookup::Type_QmlContextPropertyGetter) l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; - l->nameIndex = compiledLookups[i].nameIndex; + l->forCall = compiledLookups[i].mode() == CompiledData::Lookup::Mode_ForCall; + l->nameIndex = compiledLookups[i].nameIndex(); } } if (data->jsClassTableSize) { runtimeClasses - = (QV4::Heap::InternalClass **)malloc(data->jsClassTableSize - * sizeof(QV4::Heap::InternalClass *)); - // memset the regexps to 0 in case a GC run happens while we're within the loop below - memset(runtimeClasses, 0, - data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *)); + = (QV4::Heap::InternalClass **)calloc(data->jsClassTableSize, + sizeof(QV4::Heap::InternalClass *)); + for (uint i = 0; i < data->jsClassTableSize; ++i) { int memberCount = 0; const CompiledData::JSClassMember *member @@ -193,17 +139,37 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) runtimeClasses[i] = runtimeClasses[i]->addMember( engine->identifierTable->asPropertyKey( - runtimeStrings[member->nameOffset]), - member->isAccessor + runtimeStrings[member->nameOffset()]), + member->isAccessor() ? QV4::Attr_Accessor : QV4::Attr_Data); } } runtimeFunctions.resize(data->functionTableSize); + static bool ignoreAotCompiledFunctions + = qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER") + || !(engine->diskCacheOptions() & ExecutionEngine::DiskCache::AotNative); + + const QQmlPrivate::AOTCompiledFunction *aotFunction + = ignoreAotCompiledFunctions ? nullptr : m_compilationUnit->aotCompiledFunctions; + + auto advanceAotFunction = [&](int i) -> const QQmlPrivate::AOTCompiledFunction * { + if (aotFunction) { + if (aotFunction->functionPtr) { + if (aotFunction->functionIndex == i) + return aotFunction++; + } else { + aotFunction = nullptr; + } + } + return nullptr; + }; + for (int i = 0 ;i < runtimeFunctions.size(); ++i) { const QV4::CompiledData::Function *compiledFunction = data->functionAt(i); - runtimeFunctions[i] = QV4::Function::create(engine, this, compiledFunction); + runtimeFunctions[i] = QV4::Function::create(engine, this, compiledFunction, + advanceAotFunction(i)); } Scope scope(engine); @@ -237,15 +203,14 @@ QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine) << (data->indexOfRootFunction != -1 ? data->indexOfRootFunction : 0); } - - if (data->indexOfRootFunction != -1) - return runtimeFunctions[data->indexOfRootFunction]; - else - return nullptr; } Heap::Object *ExecutableCompilationUnit::templateObjectAt(int index) const { + const CompiledData::Unit *data = m_compilationUnit->data; + Q_ASSERT(data); + Q_ASSERT(engine); + Q_ASSERT(index < int(data->templateObjectTableSize)); if (!templateObjects.size()) templateObjects.resize(data->templateObjectTableSize); @@ -274,56 +239,21 @@ Heap::Object *ExecutableCompilationUnit::templateObjectAt(int index) const return templateObjects.at(index); } -void ExecutableCompilationUnit::unlink() +void ExecutableCompilationUnit::clear() { - if (engine) - nextCompilationUnit.remove(); - - if (isRegisteredWithEngine) { - Q_ASSERT(data && propertyCaches.count() > 0 && propertyCaches.at(/*root object*/0)); - if (qmlEngine) - qmlEngine->unregisterInternalCompositeType(this); - QQmlMetaType::unregisterInternalCompositeType(this); - isRegisteredWithEngine = false; - } - - propertyCaches.clear(); + delete [] imports; + imports = nullptr; if (runtimeLookups) { - for (uint i = 0; i < data->lookupTableSize; ++i) { - QV4::Lookup &l = runtimeLookups[i]; - if (l.getter == QV4::QObjectWrapper::lookupGetter - || l.getter == QQmlTypeWrapper::lookupSingletonProperty) { - if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache) - pc->release(); - } else if (l.getter == QQmlValueTypeWrapper::lookupGetter - || l.getter == QQmlTypeWrapper::lookupSingletonProperty) { - if (QQmlPropertyCache *pc = l.qgadgetLookup.propertyCache) - pc->release(); - } - - if (l.qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty - || l.qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectProperty) { - if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache) - pc->release(); - } - } + const uint lookupTableSize = unitData()->lookupTableSize; + for (uint i = 0; i < lookupTableSize; ++i) + runtimeLookups[i].releasePropertyCache(); } - dependentScripts.clear(); - - typeNameCache = nullptr; - - qDeleteAll(resolvedTypes); - resolvedTypes.clear(); - - engine = nullptr; - qmlEngine = nullptr; - delete [] runtimeLookups; runtimeLookups = nullptr; - for (QV4::Function *f : qAsConst(runtimeFunctions)) + for (QV4::Function *f : std::as_const(runtimeFunctions)) f->destroy(); runtimeFunctions.clear(); @@ -335,8 +265,10 @@ void ExecutableCompilationUnit::unlink() runtimeClasses = nullptr; } -void ExecutableCompilationUnit::markObjects(QV4::MarkStack *markStack) +void ExecutableCompilationUnit::markObjects(QV4::MarkStack *markStack) const { + const CompiledData::Unit *data = m_compilationUnit->data; + if (runtimeStrings) { for (uint i = 0, end = totalStringCount(); i < end; ++i) if (runtimeStrings[i]) @@ -351,14 +283,14 @@ void ExecutableCompilationUnit::markObjects(QV4::MarkStack *markStack) if (runtimeClasses[i]) runtimeClasses[i]->mark(markStack); } - for (QV4::Function *f : qAsConst(runtimeFunctions)) + for (QV4::Function *f : std::as_const(runtimeFunctions)) if (f && f->internalClass) f->internalClass->mark(markStack); - for (QV4::Heap::InternalClass *c : qAsConst(runtimeBlocks)) + for (QV4::Heap::InternalClass *c : std::as_const(runtimeBlocks)) if (c) c->mark(markStack); - for (QV4::Heap::Object *o : qAsConst(templateObjects)) + for (QV4::Heap::Object *o : std::as_const(templateObjects)) if (o) o->mark(markStack); @@ -378,92 +310,35 @@ IdentifierHash ExecutableCompilationUnit::createNamedObjectsPerComponent(int com const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable(); for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) { const CompiledData::Object *namedObject = objectAt(*namedObjectIndexPtr); - namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id); + namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->objectId()); } + Q_ASSERT(!namedObjectCache.isEmpty()); return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache); } -void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine) -{ - this->qmlEngine = qmlEngine; - - // Add to type registry of composites - if (propertyCaches.needsVMEMetaObject(/*root object*/0)) { - QQmlMetaType::registerInternalCompositeType(this); - qmlEngine->registerInternalCompositeType(this); - } else { - const QV4::CompiledData::Object *obj = objectAt(/*root object*/0); - auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); - Q_ASSERT(typeRef); - if (typeRef->compilationUnit) { - metaTypeId = typeRef->compilationUnit->metaTypeId; - listMetaTypeId = typeRef->compilationUnit->listMetaTypeId; - } else { - metaTypeId = typeRef->type.typeId(); - listMetaTypeId = typeRef->type.qListTypeId(); - } - } - - // Collect some data for instantiation later. - int bindingCount = 0; - int parserStatusCount = 0; - int objectCount = 0; - for (quint32 i = 0, count = this->objectCount(); i < count; ++i) { - const QV4::CompiledData::Object *obj = objectAt(i); - bindingCount += obj->nBindings; - if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { - if (typeRef->type.isValid()) { - if (typeRef->type.parserStatusCast() != -1) - ++parserStatusCount; - } - ++objectCount; - if (typeRef->compilationUnit) { - bindingCount += typeRef->compilationUnit->totalBindingsCount; - parserStatusCount += typeRef->compilationUnit->totalParserStatusCount; - objectCount += typeRef->compilationUnit->totalObjectCount; - } - } - } - - totalBindingsCount = bindingCount; - totalParserStatusCount = parserStatusCount; - totalObjectCount = objectCount; -} - -bool ExecutableCompilationUnit::verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const +QQmlRefPointer<ExecutableCompilationUnit> ExecutableCompilationUnit::create( + QQmlRefPointer<CompiledData::CompilationUnit> &&compilationUnit, ExecutionEngine *engine) { - if (!dependencyHasher) { - for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) { - if (data->dependencyMD5Checksum[i] != 0) - return false; - } - return true; - } - const QByteArray checksum = dependencyHasher(); - return checksum.size() == sizeof(data->dependencyMD5Checksum) - && memcmp(data->dependencyMD5Checksum, checksum.constData(), - sizeof(data->dependencyMD5Checksum)) == 0; + auto result = QQmlRefPointer<ExecutableCompilationUnit>( + new ExecutableCompilationUnit(std::move(compilationUnit)), + QQmlRefPointer<ExecutableCompilationUnit>::Adopt); + result->engine = engine; + return result; } -QStringList ExecutableCompilationUnit::moduleRequests() const +Heap::Module *ExecutableCompilationUnit::instantiate() { - QStringList requests; - requests.reserve(data->moduleRequestTableSize); - for (uint i = 0; i < data->moduleRequestTableSize; ++i) - requests << stringAt(data->moduleRequestTable()[i]); - return requests; -} + const CompiledData::Unit *data = m_compilationUnit->data; -Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine) -{ if (isESModule() && module()) return module(); if (data->indexOfRootFunction < 0) return nullptr; - if (!this->engine) - linkToEngine(engine); + Q_ASSERT(engine); + if (!runtimeStrings) + populate(); Scope scope(engine); Scoped<Module> module(scope, engine->memoryManager->allocate<Module>(engine, this)); @@ -471,11 +346,14 @@ Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine) if (isESModule()) setModule(module->d()); - for (const QString &request: moduleRequests()) { - auto dependentModuleUnit = engine->loadModule(QUrl(request), this); + const QStringList moduleRequests = m_compilationUnit->moduleRequests(); + for (const QString &request: moduleRequests) { + const QUrl url(request); + const auto dependentModuleUnit = engine->loadModule(url, this); if (engine->hasException) return nullptr; - dependentModuleUnit->instantiate(engine); + if (dependentModuleUnit.compiled) + dependentModuleUnit.compiled->instantiate(); } ScopedString importName(scope); @@ -487,30 +365,87 @@ Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine) } for (uint i = 0; i < importCount; ++i) { const CompiledData::ImportEntry &entry = data->importEntryTable()[i]; - auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this); + QUrl url = urlAt(entry.moduleRequest); importName = runtimeStrings[entry.importName]; - const Value *valuePtr = dependentModuleUnit->resolveExport(importName); - if (!valuePtr) { - QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference "); - referenceErrorMessage += importName->toQString(); - engine->throwReferenceError(referenceErrorMessage, fileName(), entry.location.line, entry.location.column); - return nullptr; + + const auto module = engine->loadModule(url, this); + if (module.compiled) { + const Value *valuePtr = module.compiled->resolveExport(importName); + if (!valuePtr) { + QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference "); + referenceErrorMessage += importName->toQString(); + engine->throwReferenceError( + referenceErrorMessage, fileName(), + entry.location.line(), entry.location.column()); + return nullptr; + } + imports[i] = valuePtr; + } else if (Value *value = module.native) { + const QString name = importName->toQString(); + if (value->isNullOrUndefined()) { + QString errorMessage = name; + errorMessage += QStringLiteral(" from "); + errorMessage += url.toString(); + errorMessage += QStringLiteral(" is null"); + engine->throwError(errorMessage); + return nullptr; + } + + if (name == QStringLiteral("default")) { + imports[i] = value; + } else { + url.setFragment(name); + const auto fragment = engine->moduleForUrl(url, this); + if (fragment.native) { + imports[i] = fragment.native; + } else { + Scope scope(this->engine); + ScopedObject o(scope, value); + if (!o) { + QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference "); + referenceErrorMessage += name; + referenceErrorMessage += QStringLiteral(" because "); + referenceErrorMessage += url.toString(QUrl::RemoveFragment); + referenceErrorMessage += QStringLiteral(" is not an object"); + engine->throwReferenceError( + referenceErrorMessage, fileName(), + entry.location.line(), entry.location.column()); + return nullptr; + } + + const ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(name)); + const ScopedValue result(scope, o->get(key)); + imports[i] = engine->registerNativeModule(url, result); + } + } } - imports[i] = valuePtr; } + const auto throwReferenceError = [&](const CompiledData::ExportEntry &entry, const QString &importName) { + QString referenceErrorMessage = QStringLiteral("Unable to resolve re-export reference "); + referenceErrorMessage += importName; + engine->throwReferenceError( + referenceErrorMessage, fileName(), + entry.location.line(), entry.location.column()); + }; + for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) { const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i]; - auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this); - if (!dependentModuleUnit) - return nullptr; - + auto dependentModule = engine->loadModule(urlAt(entry.moduleRequest), this); ScopedString importName(scope, runtimeStrings[entry.importName]); - if (!dependentModuleUnit->resolveExport(importName)) { - QString referenceErrorMessage = QStringLiteral("Unable to resolve re-export reference "); - referenceErrorMessage += importName->toQString(); - engine->throwReferenceError(referenceErrorMessage, fileName(), entry.location.line, entry.location.column); - return nullptr; + if (const auto dependentModuleUnit = dependentModule.compiled) { + if (!dependentModuleUnit->resolveExport(importName)) { + throwReferenceError(entry, importName->toQString()); + return nullptr; + } + } else if (const auto native = dependentModule.native) { + ScopedObject o(scope, native); + const ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(importName)); + const ScopedValue result(scope, o->get(key)); + if (result->isUndefined()) { + throwReferenceError(entry, importName->toQString()); + return nullptr; + } } } @@ -532,6 +467,11 @@ const Value *ExecutableCompilationUnit::resolveExportRecursively( if (exportName->toQString() == QLatin1String("*")) return &module()->self; + const CompiledData::Unit *data = m_compilationUnit->data; + + Q_ASSERT(data); + Q_ASSERT(engine); + Scope scope(engine); if (auto localExport = lookupNameInExportTable( @@ -547,13 +487,31 @@ const Value *ExecutableCompilationUnit::resolveExportRecursively( if (auto indirectExport = lookupNameInExportTable( data->indirectExportEntryTable(), data->indirectExportEntryTableSize, exportName)) { - auto dependentModuleUnit = engine->loadModule(urlAt(indirectExport->moduleRequest), this); - if (!dependentModuleUnit) - return nullptr; + QUrl request = urlAt(indirectExport->moduleRequest); + auto dependentModule = engine->loadModule(request, this); ScopedString importName(scope, runtimeStrings[indirectExport->importName]); - return dependentModuleUnit->resolveExportRecursively(importName, resolveSet); - } + if (dependentModule.compiled) { + return dependentModule.compiled->resolveExportRecursively(importName, resolveSet); + } else if (dependentModule.native) { + if (exportName->toQString() == QLatin1String("*")) + return dependentModule.native; + if (exportName->toQString() == QLatin1String("default")) + return nullptr; + + request.setFragment(importName->toQString()); + const auto fragment = engine->moduleForUrl(request); + if (fragment.native) + return fragment.native; + ScopedObject o(scope, dependentModule.native); + if (o) + return engine->registerNativeModule(request, o->get(importName)); + + return nullptr; + } else { + return nullptr; + } + } if (exportName->toQString() == QLatin1String("default")) return nullptr; @@ -562,11 +520,28 @@ const Value *ExecutableCompilationUnit::resolveExportRecursively( for (uint i = 0; i < data->starExportEntryTableSize; ++i) { const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i]; - auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this); - if (!dependentModuleUnit) - return nullptr; + QUrl request = urlAt(entry.moduleRequest); + auto dependentModule = engine->loadModule(request, this); + const Value *resolution = nullptr; + if (dependentModule.compiled) { + resolution = dependentModule.compiled->resolveExportRecursively( + exportName, resolveSet); + } else if (dependentModule.native) { + if (exportName->toQString() == QLatin1String("*")) { + resolution = dependentModule.native; + } else if (exportName->toQString() != QLatin1String("default")) { + request.setFragment(exportName->toQString()); + const auto fragment = engine->moduleForUrl(request); + if (fragment.native) { + resolution = fragment.native; + } else { + ScopedObject o(scope, dependentModule.native); + if (o) + resolution = engine->registerNativeModule(request, o->get(exportName)); + } + } + } - const Value *resolution = dependentModuleUnit->resolveExportRecursively(exportName, resolveSet); // ### handle ambiguous if (resolution) { if (!starResolution) { @@ -607,6 +582,11 @@ void ExecutableCompilationUnit::getExportedNamesRecursively( names->append(name); }; + const CompiledData::Unit *data = m_compilationUnit->data; + + Q_ASSERT(data); + Q_ASSERT(engine); + for (uint i = 0; i < data->localExportEntryTableSize; ++i) { const CompiledData::ExportEntry &entry = data->localExportEntryTable()[i]; append(stringAt(entry.exportName)); @@ -619,15 +599,28 @@ void ExecutableCompilationUnit::getExportedNamesRecursively( for (uint i = 0; i < data->starExportEntryTableSize; ++i) { const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i]; - auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this); - if (!dependentModuleUnit) - return; - dependentModuleUnit->getExportedNamesRecursively(names, exportNameSet, /*includeDefaultExport*/false); + auto dependentModule = engine->loadModule(urlAt(entry.moduleRequest), this); + if (dependentModule.compiled) { + dependentModule.compiled->getExportedNamesRecursively( + names, exportNameSet, /*includeDefaultExport*/false); + } else if (dependentModule.native) { + Scope scope(engine); + ScopedObject o(scope, dependentModule.native); + ObjectIterator iterator(scope, o, ObjectIterator::EnumerableOnly); + while (true) { + ScopedValue val(scope, iterator.nextPropertyNameAsString()); + if (val->isNull()) + break; + append(val->toQString()); + } + } } } void ExecutableCompilationUnit::evaluate() { + Q_ASSERT(engine); + QV4::Scope scope(engine); QV4::Scoped<Module> mod(scope, module()); mod->evaluate(); @@ -635,245 +628,81 @@ void ExecutableCompilationUnit::evaluate() void ExecutableCompilationUnit::evaluateModuleRequests() { - for (const QString &request: moduleRequests()) { - auto dependentModuleUnit = engine->loadModule(QUrl(request), this); - if (engine->hasException) - return; - dependentModuleUnit->evaluate(); - if (engine->hasException) - return; - } -} - -bool ExecutableCompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString) -{ - if (!QQmlFile::isLocalFile(url)) { - *errorString = QStringLiteral("File has to be a local file."); - return false; - } - - const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url); - QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper()); + Q_ASSERT(engine); - const QStringList cachePaths = { sourcePath + QLatin1Char('c'), localCacheFilePath(url) }; - for (const QString &cachePath : cachePaths) { - CompiledData::Unit *mappedUnit = cacheFile->open(cachePath, sourceTimeStamp, errorString); - if (!mappedUnit) + const QStringList moduleRequests = m_compilationUnit->moduleRequests(); + for (const QString &request: moduleRequests) { + auto dependentModule = engine->loadModule(QUrl(request), this); + if (dependentModule.native) continue; - const CompiledData::Unit * const oldDataPtr - = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data - : nullptr; - const CompiledData::Unit *oldData = data; - auto dataPtrRevert = qScopeGuard([this, oldData](){ - setUnitData(oldData); - }); - setUnitData(mappedUnit); - - if (data->sourceFileIndex != 0 - && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { - *errorString = QStringLiteral("QML source file has moved to a different location."); - continue; - } - - dataPtrRevert.dismiss(); - free(const_cast<CompiledData::Unit*>(oldDataPtr)); - backingFile.reset(cacheFile.take()); - return true; - } - - return false; -} - -bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) -{ - if (data->sourceTimeStamp == 0) { - *errorString = QStringLiteral("Missing time stamp for source file"); - return false; - } - - if (!QQmlFile::isLocalFile(unitUrl)) { - *errorString = QStringLiteral("File has to be a local file."); - return false; - } - - return CompiledData::SaveableUnitPointer(unitData()).saveToDisk<char>( - [&unitUrl, errorString](const char *data, quint32 size) { - return CompiledData::SaveableUnitPointer::writeDataToFile(localCacheFilePath(unitUrl), data, - size, errorString); - }); -} - -/*! -Returns the property cache, if one alread exists. The cache is not referenced. -*/ -QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::propertyCache() const -{ - if (type.isValid()) - return typePropertyCache; - else - return compilationUnit->rootPropertyCache(); -} - -/*! -Returns the property cache, creating one if it doesn't already exist. The cache is not referenced. -*/ -QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::createPropertyCache(QQmlEngine *engine) -{ - if (typePropertyCache) { - return typePropertyCache; - } else if (type.isValid()) { - typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type.metaObject(), minorVersion); - return typePropertyCache; - } else { - return compilationUnit->rootPropertyCache(); - } -} - -bool ResolvedTypeReference::addToHash(QCryptographicHash *hash, QQmlEngine *engine) -{ - if (type.isValid()) { - bool ok = false; - hash->addData(createPropertyCache(engine)->checksum(&ok)); - return ok; - } - if (!compilationUnit) - return false; - hash->addData(compilationUnit->data->md5Checksum, - sizeof(compilationUnit->data->md5Checksum)); - return true; -} - -template <typename T> -bool qtTypeInherits(const QMetaObject *mo) { - while (mo) { - if (mo == &T::staticMetaObject) - return true; - mo = mo->superClass(); - } - return false; -} - -void ResolvedTypeReference::doDynamicTypeCheck() -{ - const QMetaObject *mo = nullptr; - if (typePropertyCache) - mo = typePropertyCache->firstCppMetaObject(); - else if (type.isValid()) - mo = type.metaObject(); - else if (compilationUnit) - mo = compilationUnit->rootPropertyCache()->firstCppMetaObject(); - isFullyDynamicType = qtTypeInherits<QQmlPropertyMap>(mo); -} + if (engine->hasException) + return; -bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *engine) const -{ - for (auto it = constBegin(), end = constEnd(); it != end; ++it) { - if (!it.value()->addToHash(hash, engine)) - return false; + Q_ASSERT(dependentModule.compiled); + dependentModule.compiled->evaluate(); + if (engine->hasException) + return; } - - return true; } QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Binding *binding) const { +#if QT_CONFIG(translation) using namespace CompiledData; - switch (binding->type) { - case Binding::Type_Script: - case Binding::Type_String: - return stringAt(binding->stringIndex); - case Binding::Type_Null: - return QStringLiteral("null"); - case Binding::Type_Boolean: - return binding->value.b ? QStringLiteral("true") : QStringLiteral("false"); - case Binding::Type_Number: - return QString::number(bindingValueAsNumber(binding), 'g', QLocale::FloatingPointShortest); - case Binding::Type_Invalid: - return QString(); -#if !QT_CONFIG(translation) + bool byId = false; + switch (binding->type()) { case Binding::Type_TranslationById: - case Binding::Type_Translation: - return stringAt( - data->translations()[binding->value.translationDataIndex].stringIndex); -#else - case Binding::Type_TranslationById: { - const TranslationData &translation - = data->translations()[binding->value.translationDataIndex]; - QByteArray id = stringAt(translation.stringIndex).toUtf8(); - return qtTrId(id.constData(), translation.number); - } + byId = true; + Q_FALLTHROUGH(); case Binding::Type_Translation: { - const TranslationData &translation - = data->translations()[binding->value.translationDataIndex]; - // This code must match that in the qsTr() implementation - const QString &path = fileName(); - int lastSlash = path.lastIndexOf(QLatin1Char('/')); - QStringRef context = (lastSlash > -1) ? path.midRef(lastSlash + 1, path.length() - lastSlash - 5) - : QStringRef(); - QByteArray contextUtf8 = context.toUtf8(); - QByteArray comment = stringAt(translation.commentIndex).toUtf8(); - QByteArray text = stringAt(translation.stringIndex).toUtf8(); - return QCoreApplication::translate(contextUtf8.constData(), text.constData(), - comment.constData(), translation.number); + return translateFrom({ binding->value.translationDataIndex, byId }); } -#endif default: break; } - return QString(); -} - -QString ExecutableCompilationUnit::bindingValueAsScriptString( - const CompiledData::Binding *binding) const -{ - return (binding->type == CompiledData::Binding::Type_String) - ? CompiledData::Binding::escapedString(stringAt(binding->stringIndex)) - : bindingValueAsString(binding); +#endif + return m_compilationUnit->bindingValueAsString(binding); } -bool ExecutableCompilationUnit::verifyHeader( - const CompiledData::Unit *unit, QDateTime expectedSourceTimeStamp, QString *errorString) +QString ExecutableCompilationUnit::translateFrom(TranslationDataIndex index) const { - if (strncmp(unit->magic, CompiledData::magic_str, sizeof(unit->magic))) { - *errorString = QStringLiteral("Magic bytes in the header do not match"); - return false; - } +#if !QT_CONFIG(translation) + return QString(); +#else + const CompiledData::TranslationData &translation = unitData()->translations()[index.index]; - if (unit->version != quint32(QV4_DATA_STRUCTURE_VERSION)) { - *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2") - .arg(unit->version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); - return false; + if (index.byId) { + QByteArray id = stringAt(translation.stringIndex).toUtf8(); + return qtTrId(id.constData(), translation.number); } - if (unit->qtVersion != quint32(QT_VERSION)) { - *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2") - .arg(unit->qtVersion, 0, 16).arg(QT_VERSION, 0, 16); - return false; - } + const auto fileContext = [this]() { + // This code must match that in the qsTr() implementation + const QString &path = fileName(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); - if (unit->sourceTimeStamp) { - // Files from the resource system do not have any time stamps, so fall back to the application - // executable. - if (!expectedSourceTimeStamp.isValid()) - expectedSourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified(); + QStringView context = (lastSlash > -1) + ? QStringView{ path }.mid(lastSlash + 1, path.size() - lastSlash - 5) + : QStringView(); + return context.toUtf8(); + }; - if (expectedSourceTimeStamp.isValid() - && expectedSourceTimeStamp.toMSecsSinceEpoch() != unit->sourceTimeStamp) { - *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); - return false; - } + const bool hasContext + = translation.contextIndex != QV4::CompiledData::TranslationData::NoContextIndex; + QByteArray context; + if (hasContext) { + context = stringAt(translation.contextIndex).toUtf8(); + } else { + auto pragmaTranslationContext = unitData()->translationContextIndex(); + context = stringAt(*pragmaTranslationContext).toUtf8(); + context = context.isEmpty() ? fileContext() : context; } -#if defined(QML_COMPILE_HASH) - if (qstrcmp(qml_compile_hash, unit->libraryVersionHash) != 0) { - *errorString = QStringLiteral("QML library version mismatch. Expected compile hash does not match"); - return false; - } -#else -#error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files" + QByteArray comment = stringAt(translation.commentIndex).toUtf8(); + QByteArray text = stringAt(translation.stringIndex).toUtf8(); + return QCoreApplication::translate(context, text, comment, translation.number); #endif - return true; } } // namespace QV4 diff --git a/src/qml/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h index 6eef3b12c3..930e138732 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit_p.h +++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4EXECUTABLECOMPILATIONUNIT_P_H #define QV4EXECUTABLECOMPILATIONUNIT_P_H @@ -51,173 +15,138 @@ // We mean it. // -#include <private/qv4compileddata_p.h> -#include <private/qv4identifier_p.h> -#include <private/qqmlrefcount_p.h> #include <private/qintrusivelist_p.h> +#include <private/qqmlmetatype_p.h> +#include <private/qqmlnullablevalue_p.h> #include <private/qqmlpropertycachevector_p.h> +#include <private/qqmlrefcount_p.h> #include <private/qqmltype_p.h> -#include <private/qqmlnullablevalue_p.h> +#include <private/qqmltypenamecache_p.h> +#include <private/qv4compileddata_p.h> +#include <private/qv4identifierhash_p.h> + +#include <memory> QT_BEGIN_NAMESPACE class QQmlScriptData; class QQmlEnginePrivate; -namespace QV4 { -// index is per-object binding index -typedef QVector<QQmlPropertyData*> BindingPropertyData; +namespace QV4 { class CompilationUnitMapper; -struct ResolvedTypeReference; -// map from name index -// While this could be a hash, a map is chosen here to provide a stable -// order, which is used to calculating a check-sum on dependent meta-objects. -struct ResolvedTypeReferenceMap: public QMap<int, ResolvedTypeReference*> + +struct CompilationUnitRuntimeData { - bool addToHash(QCryptographicHash *hash, QQmlEngine *engine) const; + Heap::String **runtimeStrings = nullptr; // Array + + // pointers either to data->constants() or little-endian memory copy. + // We keep this member twice so that the JIT can access it via standard layout. + const StaticValue *constants = nullptr; + + QV4::StaticValue *runtimeRegularExpressions = nullptr; + Heap::InternalClass **runtimeClasses = nullptr; + const StaticValue **imports = nullptr; + + QV4::Lookup *runtimeLookups = nullptr; + QVector<QV4::Function *> runtimeFunctions; + QVector<QV4::Heap::InternalClass *> runtimeBlocks; + mutable QVector<QV4::Heap::Object *> templateObjects; }; -class Q_QML_PRIVATE_EXPORT ExecutableCompilationUnit final: public CompiledData::CompilationUnit, - public QQmlRefCount +static_assert(std::is_standard_layout_v<CompilationUnitRuntimeData>); +static_assert(offsetof(CompilationUnitRuntimeData, runtimeStrings) == 0); +static_assert(offsetof(CompilationUnitRuntimeData, constants) == sizeof(QV4::Heap::String **)); +static_assert(offsetof(CompilationUnitRuntimeData, runtimeRegularExpressions) == offsetof(CompilationUnitRuntimeData, constants) + sizeof(const StaticValue *)); +static_assert(offsetof(CompilationUnitRuntimeData, runtimeClasses) == offsetof(CompilationUnitRuntimeData, runtimeRegularExpressions) + sizeof(const StaticValue *)); +static_assert(offsetof(CompilationUnitRuntimeData, imports) == offsetof(CompilationUnitRuntimeData, runtimeClasses) + sizeof(const StaticValue *)); + +class Q_QML_EXPORT ExecutableCompilationUnit final + : public CompilationUnitRuntimeData, + public QQmlRefCounted<ExecutableCompilationUnit> { Q_DISABLE_COPY_MOVE(ExecutableCompilationUnit) public: + friend class QQmlRefCounted<ExecutableCompilationUnit>; friend class QQmlRefPointer<ExecutableCompilationUnit>; + friend struct ExecutionEngine; - static QQmlRefPointer<ExecutableCompilationUnit> create( - CompiledData::CompilationUnit &&compilationUnit) + ExecutionEngine *engine = nullptr; + + QString finalUrlString() const { return m_compilationUnit->finalUrlString(); } + QString fileName() const { return m_compilationUnit->fileName(); } + + QUrl url() const { return m_compilationUnit->url(); } + QUrl finalUrl() const { return m_compilationUnit->finalUrl(); } + + QQmlRefPointer<QQmlTypeNameCache> typeNameCache() const { - return QQmlRefPointer<ExecutableCompilationUnit>( - new ExecutableCompilationUnit(std::move(compilationUnit)), - QQmlRefPointer<ExecutableCompilationUnit>::Adopt); + return m_compilationUnit->typeNameCache; } - static QQmlRefPointer<ExecutableCompilationUnit> create() + QQmlPropertyCacheVector *propertyCachesPtr() { - return QQmlRefPointer<ExecutableCompilationUnit>( - new ExecutableCompilationUnit, - QQmlRefPointer<ExecutableCompilationUnit>::Adopt); + return &m_compilationUnit->propertyCaches; } - QIntrusiveListNode nextCompilationUnit; - ExecutionEngine *engine = nullptr; - QQmlEnginePrivate *qmlEngine = nullptr; // only used in QML environment for composite types, not in plain QJSEngine case. - - // url() and fileName() shall be used to load the actual QML/JS code or to show errors or - // warnings about that code. They include any potential URL interceptions and thus represent the - // "physical" location of the code. - // - // finalUrl() and finalUrlString() shall be used to resolve further URLs referred to in the code - // They are _not_ intercepted and thus represent the "logical" name for the code. - - QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; } - QUrl finalUrl() const + QQmlPropertyCache::ConstPtr rootPropertyCache() const { - if (m_finalUrl.isNull) - m_finalUrl = QUrl(finalUrlString()); - return m_finalUrl; + return m_compilationUnit->rootPropertyCache(); } - QV4::Lookup *runtimeLookups = nullptr; - QVector<QV4::Function *> runtimeFunctions; - QVector<QV4::Heap::InternalClass *> runtimeBlocks; - mutable QVector<QV4::Heap::Object *> templateObjects; - mutable QQmlNullableValue<QUrl> m_url; - mutable QQmlNullableValue<QUrl> m_finalUrl; - - // QML specific fields - QQmlPropertyCacheVector propertyCaches; - QQmlRefPointer<QQmlPropertyCache> rootPropertyCache() const { return propertyCaches.at(/*root object*/0); } - - QQmlRefPointer<QQmlTypeNameCache> typeNameCache; - - // index is object index. This allows fast access to the - // property data when initializing bindings, avoiding expensive - // lookups by string (property name). - QVector<BindingPropertyData> bindingPropertyDataPerObject; - // mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects // this is initialized on-demand by QQmlContextData QHash<int, IdentifierHash> namedObjectsPerComponentCache; inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex); - void finalizeCompositeType(QQmlEnginePrivate *qmlEngine); - - int totalBindingsCount = 0; // Number of bindings used in this type - int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses - int totalObjectCount = 0; // Number of objects explicitly instantiated - - QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts; - ResolvedTypeReferenceMap resolvedTypes; - ResolvedTypeReference *resolvedType(int id) const { return resolvedTypes.value(id); } - - bool verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const; + int totalBindingsCount() const { return m_compilationUnit->totalBindingsCount(); } + int totalParserStatusCount() const { return m_compilationUnit->totalParserStatusCount(); } + int totalObjectCount() const { return m_compilationUnit->totalObjectCount(); } - int metaTypeId = -1; - int listMetaTypeId = -1; - bool isRegisteredWithEngine = false; - - QScopedPointer<CompilationUnitMapper> backingFile; - - // --- interface for QQmlPropertyCacheCreator - using CompiledObject = CompiledData::Object; - using CompiledFunction = CompiledData::Function; - - int objectCount() const { return qmlData->nObjects; } - const CompiledObject *objectAt(int index) const + ResolvedTypeReference *resolvedType(int id) const { - return qmlData->objectAt(index); + return m_compilationUnit->resolvedType(id); } - int importCount() const { return qmlData->nImports; } - const CompiledData::Import *importAt(int index) const + QQmlType qmlTypeForComponent(const QString &inlineComponentName = QString()) const { - return qmlData->importAt(index); + return m_compilationUnit->qmlTypeForComponent(inlineComponentName); } - Heap::Object *templateObjectAt(int index) const; - - struct FunctionIterator - { - FunctionIterator(const CompiledData::Unit *unit, const CompiledObject *object, int index) - : unit(unit), object(object), index(index) {} - const CompiledData::Unit *unit; - const CompiledObject *object; - int index; - - const CompiledFunction *operator->() const - { - return unit->functionAt(object->functionOffsetTable()[index]); - } - - void operator++() { ++index; } - bool operator==(const FunctionIterator &rhs) const { return index == rhs.index; } - bool operator!=(const FunctionIterator &rhs) const { return index != rhs.index; } - }; + QMetaType metaType() const { return m_compilationUnit->qmlType.typeId(); } - FunctionIterator objectFunctionsBegin(const CompiledObject *object) const + int inlineComponentId(const QString &inlineComponentName) const { - return FunctionIterator(data, object, 0); + return m_compilationUnit->inlineComponentId(inlineComponentName); } - FunctionIterator objectFunctionsEnd(const CompiledObject *object) const - { - return FunctionIterator(data, object, object->nFunctions); - } + // --- interface for QQmlPropertyCacheCreator + using CompiledObject = CompiledData::CompilationUnit::CompiledObject; + using CompiledFunction = CompiledData::CompilationUnit::CompiledFunction; + using CompiledBinding = CompiledData::CompilationUnit::CompiledBinding; + using IdToObjectMap = CompiledData::CompilationUnit::IdToObjectMap; - bool isESModule() const + bool nativeMethodsAcceptThisObjects() const { - return data->flags & CompiledData::Unit::IsESModule; + return m_compilationUnit->nativeMethodsAcceptThisObjects(); } - bool isSharedLibrary() const + bool ignoresFunctionSignature() const { return m_compilationUnit->ignoresFunctionSignature(); } + bool valueTypesAreCopied() const { return m_compilationUnit->valueTypesAreCopied(); } + bool valueTypesAreAddressable() const { return m_compilationUnit->valueTypesAreAddressable(); } + bool valueTypesAreAssertable() const { return m_compilationUnit->valueTypesAreAssertable(); } + bool componentsAreBound() const { return m_compilationUnit->componentsAreBound(); } + bool isESModule() const { return m_compilationUnit->isESModule(); } + + int objectCount() const { return m_compilationUnit->objectCount(); } + const CompiledObject *objectAt(int index) const { - return data->flags & CompiledData::Unit::IsSharedLibrary; + return m_compilationUnit->objectAt(index); } - QStringList moduleRequests() const; - Heap::Module *instantiate(ExecutionEngine *engine); + Heap::Object *templateObjectAt(int index) const; + + Heap::Module *instantiate(); const Value *resolveExport(QV4::String *exportName) { QVector<ResolveSetEntry> resolveSet; @@ -238,33 +167,74 @@ public: void evaluate(); void evaluateModuleRequests(); - QV4::Function *linkToEngine(QV4::ExecutionEngine *engine); - void unlink(); + void mark(MarkStack *markStack) const { markObjects(markStack); } + void markObjects(MarkStack *markStack) const; - void markObjects(MarkStack *markStack); + QString bindingValueAsString(const CompiledData::Binding *binding) const; + double bindingValueAsNumber(const CompiledData::Binding *binding) const + { + return m_compilationUnit->bindingValueAsNumber(binding); + } + QString bindingValueAsScriptString(const CompiledData::Binding *binding) const + { + return m_compilationUnit->bindingValueAsScriptString(binding); + } - bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString); + struct TranslationDataIndex + { + uint index; + bool byId; + }; - static QString localCacheFilePath(const QUrl &url); - bool saveToDisk(const QUrl &unitUrl, QString *errorString); + QString translateFrom(TranslationDataIndex index) const; - QString bindingValueAsString(const CompiledData::Binding *binding) const; - QString bindingValueAsScriptString(const CompiledData::Binding *binding) const; - double bindingValueAsNumber(const CompiledData::Binding *binding) const + Heap::Module *module() const { return m_module; } + void setModule(Heap::Module *module) { m_module = module; } + + const CompiledData::Unit *unitData() const { return m_compilationUnit->data; } + + QString stringAt(uint index) const { return m_compilationUnit->stringAt(index); } + + const QVector<QQmlRefPointer<QQmlScriptData>> *dependentScriptsPtr() const + { + return &m_compilationUnit->dependentScripts; + } + + const CompiledData::BindingPropertyData *bindingPropertyDataPerObjectAt( + qsizetype objectIndex) const + { + return &m_compilationUnit->bindingPropertyDataPerObject.at(objectIndex); + } + + const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &baseCompilationUnit() const + { + return m_compilationUnit; + } + + QV4::Function *rootFunction() { - if (binding->type != CompiledData::Binding::Type_Number) - return 0.0; - return constants[binding->value.constantValueIndex].doubleValue(); + if (!runtimeStrings) + populate(); + + const auto *data = unitData(); + return data->indexOfRootFunction != -1 + ? runtimeFunctions[data->indexOfRootFunction] + : nullptr; } - static bool verifyHeader(const CompiledData::Unit *unit, QDateTime expectedSourceTimeStamp, - QString *errorString); + void populate(); + void clear(); protected: quint32 totalStringCount() const - { return data->stringTableSize; } + { return unitData()->stringTableSize; } private: + friend struct ExecutionEngine; + + QQmlRefPointer<CompiledData::CompilationUnit> m_compilationUnit; + Heap::Module *m_module = nullptr; + struct ResolveSetEntry { ResolveSetEntry() {} @@ -275,9 +245,13 @@ private: }; ExecutableCompilationUnit(); - ExecutableCompilationUnit(CompiledData::CompilationUnit &&compilationUnit); + ExecutableCompilationUnit(QQmlRefPointer<CompiledData::CompilationUnit> &&compilationUnit); ~ExecutableCompilationUnit(); + static QQmlRefPointer<ExecutableCompilationUnit> create( + QQmlRefPointer<CompiledData::CompilationUnit> &&compilationUnit, + ExecutionEngine *engine); + const Value *resolveExportRecursively(QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet); @@ -293,36 +267,12 @@ private: bool includeDefaultExport = true) const; }; -struct ResolvedTypeReference -{ - ResolvedTypeReference() - : majorVersion(0) - , minorVersion(0) - , isFullyDynamicType(false) - {} - - QQmlType type; - QQmlRefPointer<QQmlPropertyCache> typePropertyCache; - QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; - - int majorVersion; - int minorVersion; - // Types such as QQmlPropertyMap can add properties dynamically at run-time and - // therefore cannot have a property cache installed when instantiated. - bool isFullyDynamicType; - - QQmlRefPointer<QQmlPropertyCache> propertyCache() const; - QQmlRefPointer<QQmlPropertyCache> createPropertyCache(QQmlEngine *); - bool addToHash(QCryptographicHash *hash, QQmlEngine *engine); - - void doDynamicTypeCheck(); -}; - IdentifierHash ExecutableCompilationUnit::namedObjectsPerComponent(int componentObjectIndex) { - auto it = namedObjectsPerComponentCache.find(componentObjectIndex); - if (Q_UNLIKELY(it == namedObjectsPerComponentCache.end())) + auto it = namedObjectsPerComponentCache.constFind(componentObjectIndex); + if (Q_UNLIKELY(it == namedObjectsPerComponentCache.cend())) return createNamedObjectsPerComponent(componentObjectIndex); + Q_ASSERT(!it->isEmpty()); return *it; } diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index aeb4835c40..82646e2822 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -1,82 +1,87 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4function_p.h" -#include "qv4functionobject_p.h" -#include "qv4managed_p.h" -#include "qv4string_p.h" -#include "qv4value_p.h" -#include "qv4engine_p.h" -#include "qv4lookup_p.h" -#include <private/qv4mm_p.h> -#include <private/qv4identifiertable_p.h> + +#include <private/qqmlpropertycachecreator_p.h> +#include <private/qqmltype_p_p.h> + +#include <private/qv4engine_p.h> #include <private/qv4functiontable_p.h> -#include <assembler/MacroAssemblerCodeRef.h> +#include <private/qv4identifiertable_p.h> +#include <private/qv4jscall_p.h> #include <private/qv4vme_moth_p.h> -#include <private/qqmlglobal_p.h> + +#include <assembler/MacroAssemblerCodeRef.h> QT_BEGIN_NAMESPACE -using namespace QV4; +namespace QV4 { + +bool Function::call(QObject *thisObject, void **a, const QMetaType *types, int argc, + ExecutionContext *context) +{ + if (kind != AotCompiled) { + return QV4::convertAndCall( + context->engine(), thisObject, a, types, argc, + [this, context](const Value *thisObject, const Value *argv, int argc) { + return call(thisObject, argv, argc, context); + }); + } -ReturnedValue Function::call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) { ExecutionEngine *engine = context->engine(); - CppStackFrame frame; - frame.init(engine, this, argv, argc); - frame.setupJSFrame(engine->jsStackTop, Value::undefinedValue(), context->d(), - thisObject ? *thisObject : Value::undefinedValue(), - Value::undefinedValue()); + MetaTypesStackFrame frame; + frame.init(this, thisObject, context, a, types, argc); + frame.push(engine); + Moth::VME::exec(&frame, engine); + frame.pop(engine); + return !frame.isReturnValueUndefined(); +} - frame.push(); +static ReturnedValue doCall( + QV4::Function *self, const QV4::Value *thisObject, const QV4::Value *argv, int argc, + QV4::ExecutionContext *context) +{ + ExecutionEngine *engine = context->engine(); + JSTypesStackFrame frame; + frame.init(self, argv, argc); + frame.setupJSFrame(engine->jsStackTop, Value::undefinedValue(), context->d(), + thisObject ? *thisObject : Value::undefinedValue()); engine->jsStackTop += frame.requiredJSStackFrameSize(); - + frame.push(engine); ReturnedValue result = Moth::VME::exec(&frame, engine); + frame.pop(engine); + return result; +} - frame.pop(); +ReturnedValue Function::call( + const Value *thisObject, const Value *argv, int argc, ExecutionContext *context) { + switch (kind) { + case AotCompiled: + return QV4::convertAndCall( + context->engine(), &aotCompiledFunction, thisObject, argv, argc, + [this, context]( + QObject *thisObject, void **a, const QMetaType *types, int argc) { + call(thisObject, a, types, argc, context); + }); + case JsTyped: + return QV4::coerceAndCall( + context->engine(), &jsTypedFunction, compiledFunction, argv, argc, + [this, context, thisObject](const Value *argv, int argc) { + return doCall(this, thisObject, argv, argc, context); + }); + default: + break; + } - return result; + return doCall(this, thisObject, argv, argc, context); } Function *Function::create(ExecutionEngine *engine, ExecutableCompilationUnit *unit, - const CompiledData::Function *function) + const CompiledData::Function *function, + const QQmlPrivate::AOTCompiledFunction *aotFunction) { - return new Function(engine, unit, function); + return new Function(engine, unit, function, aotFunction); } void Function::destroy() @@ -84,13 +89,24 @@ void Function::destroy() delete this; } +void Function::mark(MarkStack *ms) +{ + if (internalClass) + internalClass->mark(ms); +} + +static bool isSpecificType(const CompiledData::ParameterType &type) +{ + return type.typeNameIndexOrCommonType() + != (type.indexIsCommonType() ? quint32(CompiledData::CommonType::Invalid) : 0); +} + Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit, - const CompiledData::Function *function) - : FunctionData(unit) + const CompiledData::Function *function, + const QQmlPrivate::AOTCompiledFunction *aotFunction) + : FunctionData(engine, unit) , compiledFunction(function) , codeData(function->code()) - , jittedCode(nullptr) - , codeRef(nullptr) { Scope scope(engine); Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext)); @@ -101,11 +117,59 @@ Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit, ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); const CompiledData::Parameter *formalsIndices = compiledFunction->formalsTable(); - for (quint32 i = 0; i < compiledFunction->nFormals; ++i) + bool enforceJsTypes = !unit->ignoresFunctionSignature(); + + for (quint32 i = 0; i < compiledFunction->nFormals; ++i) { ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[formalsIndices[i].nameIndex]), Attr_NotConfigurable); - internalClass = ic->d(); + if (enforceJsTypes && !isSpecificType(formalsIndices[i].type)) + enforceJsTypes = false; + } + internalClass.set(engine, ic->d()); nFormals = compiledFunction->nFormals; + + if (!enforceJsTypes) + return; + + if (aotFunction) { + aotCompiledCode = aotFunction->functionPtr; + new (&aotCompiledFunction) AOTCompiledFunction; + kind = AotCompiled; + aotCompiledFunction.types.resize(aotFunction->numArguments + 1); + aotFunction->signature(unit, aotCompiledFunction.types.data()); + return; + } + + // If a function has any typed arguments, but an untyped return value, the return value is void. + // If it doesn't have any arguments at all and the return value is untyped, the function is + // untyped. Users can specifically set the return type to "void" to have it enforced. + if (nFormals == 0 && !isSpecificType(compiledFunction->returnType)) + return; + + QQmlTypeLoader *typeLoader = engine->typeLoader(); + + auto findQmlType = [&](const CompiledData::ParameterType ¶m) { + const quint32 type = param.typeNameIndexOrCommonType(); + if (param.indexIsCommonType()) { + return QQmlMetaType::qmlType(QQmlPropertyCacheCreatorBase::metaTypeForPropertyType( + QV4::CompiledData::CommonType(type))); + } + + if (type == 0 || !typeLoader) + return QQmlType(); + + const auto &base = unit->baseCompilationUnit(); + const QQmlType qmltype = QQmlTypePrivate::compositeQmlType( + base, typeLoader, base->stringAt(type)); + return qmltype.typeId().isValid() ? qmltype : QQmlType(); + }; + + new (&jsTypedFunction) JSTypedFunction; + kind = JsTyped; + jsTypedFunction.types.reserve(nFormals + 1); + jsTypedFunction.types.append(findQmlType(compiledFunction->returnType)); + for (quint16 i = 0; i < nFormals; ++i) + jsTypedFunction.types.append(findQmlType(formalsIndices[i].type)); } Function::~Function() @@ -114,6 +178,18 @@ Function::~Function() destroyFunctionTable(this, codeRef); delete codeRef; } + + switch (kind) { + case JsTyped: + jsTypedFunction.~JSTypedFunction(); + break; + case AotCompiled: + aotCompiledFunction.~AOTCompiledFunction(); + break; + case JsUntyped: + case Eval: + break; + } } void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> ¶meters) @@ -121,7 +197,7 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr QStringList parameterNames; // Resolve duplicate parameter names: - for (int i = 0, ei = parameters.count(); i != ei; ++i) { + for (int i = 0, ei = parameters.size(); i != ei; ++i) { const QByteArray ¶m = parameters.at(i); int duplicate = -1; @@ -136,30 +212,31 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr if (duplicate == -1) { parameterNames.append(QString::fromUtf8(param)); } else { - const QString &dup = parameterNames[duplicate]; + const QString dup = parameterNames[duplicate]; parameterNames.append(dup); parameterNames[duplicate] = - QString(0xfffe) + QString::number(duplicate) + dup; + QString(QChar(0xfffe)) + QString::number(duplicate) + dup; } } - internalClass = engine->internalClasses(EngineBase::Class_CallContext); + Scope scope(engine); + Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext)); // first locals const quint32_le *localsIndices = compiledFunction->localsTable(); for (quint32 i = 0; i < compiledFunction->nLocals; ++i) { - internalClass = internalClass->addMember( + ic = ic->addMember( engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); } - Scope scope(engine); ScopedString arg(scope); for (const QString ¶meterName : parameterNames) { arg = engine->newIdentifier(parameterName); - internalClass = internalClass->addMember(arg->propertyKey(), Attr_NotConfigurable); + ic = ic->addMember(arg->propertyKey(), Attr_NotConfigurable); } + internalClass.set(engine, ic->d()); nFormals = parameters.size(); } @@ -176,7 +253,15 @@ QString Function::prettyName(const Function *function, const void *code) QQmlSourceLocation Function::sourceLocation() const { - return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column); + return QQmlSourceLocation( + sourceFile(), compiledFunction->location.line(), compiledFunction->location.column()); } +FunctionData::FunctionData(EngineBase *engine, ExecutableCompilationUnit *compilationUnit_) +{ + compilationUnit.set(engine, compilationUnit_); +} + +} // namespace QV4 + QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 51960863c4..7543dd3c4b 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4FUNCTION_H #define QV4FUNCTION_H @@ -50,6 +14,7 @@ // We mean it. // +#include <qqmlprivate.h> #include "qv4global_p.h" #include <private/qv4executablecompilationunit_p.h> #include <private/qv4context_p.h> @@ -65,33 +30,38 @@ struct QQmlSourceLocation; namespace QV4 { -struct Q_QML_EXPORT FunctionData { - CompiledData::CompilationUnitBase *compilationUnit; +struct Q_QML_EXPORT FunctionData +{ + WriteBarrier::HeapObjectWrapper<CompilationUnitRuntimeData, 1> compilationUnit; // Intentionally require an ExecutableCompilationUnit but save only a pointer to // CompilationUnitBase. This is so that we can take advantage of the standard layout // of CompilationUnitBase in the JIT. Furthermore we can safely static_cast to // ExecutableCompilationUnit where we need it. - FunctionData(ExecutableCompilationUnit *compilationUnit) - : compilationUnit(compilationUnit) - {} + FunctionData(EngineBase *engine, ExecutableCompilationUnit *compilationUnit_); }; // Make sure this class can be accessed through offsetof (done by the assemblers): Q_STATIC_ASSERT(std::is_standard_layout< FunctionData >::value); struct Q_QML_EXPORT Function : public FunctionData { -private: +protected: Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit, - const CompiledData::Function *function); + const CompiledData::Function *function, const QQmlPrivate::AOTCompiledFunction *aotFunction); ~Function(); public: - const CompiledData::Function *compiledFunction; + struct JSTypedFunction { + QVarLengthArray<QQmlType, 4> types; + }; + + struct AOTCompiledFunction { + QVarLengthArray<QMetaType, 4> types; + }; QV4::ExecutableCompilationUnit *executableCompilationUnit() const { // This is safe: We require an ExecutableCompilationUnit in the ctor. - return static_cast<QV4::ExecutableCompilationUnit *>(compilationUnit); + return static_cast<QV4::ExecutableCompilationUnit *>(compilationUnit.get()); } QV4::Heap::String *runtimeString(uint i) const @@ -99,24 +69,44 @@ public: return compilationUnit->runtimeStrings[i]; } - ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context); + bool call(QObject *thisObject, void **a, const QMetaType *types, int argc, + ExecutionContext *context); + ReturnedValue call(const Value *thisObject, const Value *argv, int argc, + ExecutionContext *context); - const char *codeData; + const CompiledData::Function *compiledFunction = nullptr; + const char *codeData = nullptr; + JSC::MacroAssemblerCodeRef *codeRef = nullptr; typedef ReturnedValue (*JittedCode)(CppStackFrame *, ExecutionEngine *); - JittedCode jittedCode; - JSC::MacroAssemblerCodeRef *codeRef; + typedef void (*AotCompiledCode)(const QQmlPrivate::AOTCompiledContext *context, void **argv); + + union { + void *noFunction = nullptr; + JSTypedFunction jsTypedFunction; + AOTCompiledFunction aotCompiledFunction; + }; + + union { + JittedCode jittedCode = nullptr; + AotCompiledCode aotCompiledCode; + }; // first nArguments names in internalClass are the actual arguments - Heap::InternalClass *internalClass; - uint nFormals; + QV4::WriteBarrier::Pointer<Heap::InternalClass> internalClass; int interpreterCallCount = 0; - bool isEval = false; + quint16 nFormals = 0; + enum Kind : quint8 { JsUntyped, JsTyped, AotCompiled, Eval }; + Kind kind = JsUntyped; + bool detectedInjectedParameters = false; static Function *create(ExecutionEngine *engine, ExecutableCompilationUnit *unit, - const CompiledData::Function *function); + const CompiledData::Function *function, + const QQmlPrivate::AOTCompiledFunction *aotFunction); void destroy(); + void mark(QV4::MarkStack *ms); + // used when dynamically assigning signal handlers (QQmlConnection) void updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> ¶meters); @@ -132,6 +122,7 @@ public: inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; } inline bool isArrowFunction() const { return compiledFunction->flags & CompiledData::Function::IsArrowFunction; } inline bool isGenerator() const { return compiledFunction->flags & CompiledData::Function::IsGenerator; } + inline bool isClosureWrapper() const { return compiledFunction->flags & CompiledData::Function::IsClosureWrapper; } QQmlSourceLocation sourceLocation() const; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 6fb7946023..e9f91fbc06 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -1,50 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4object_p.h" -#include "qv4objectproto_p.h" -#include "qv4stringobject_p.h" #include "qv4function_p.h" #include "qv4symbol_p.h" #include <private/qv4mm_p.h> -#include "qv4arrayobject_p.h" #include "qv4scopedvalue_p.h" #include "qv4argumentsobject_p.h" @@ -63,86 +24,63 @@ #include <QtCore/QDebug> #include <algorithm> -#include "qv4profiling_p.h" using namespace QV4; DEFINE_OBJECT_VTABLE(FunctionObject); -void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, VTable::Call call) +void Heap::FunctionObject::init(QV4::ExecutionEngine *engine, QV4::String *name) { - jsCall = call; - jsConstruct = nullptr; - - Object::init(); - this->scope.set(scope->engine(), scope->d()); - Scope s(scope->engine()); - ScopedFunctionObject f(s, this); - if (name) - f->setName(name); -} - -void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name) -{ - ExecutionEngine *e = scope->engine(); - - jsCall = vtable()->call; - jsConstruct = vtable()->callAsConstructor; - Object::init(); - this->scope.set(scope->engine(), scope->d()); - Scope s(e); + Scope s(engine); ScopedFunctionObject f(s, this); if (name) f->setName(name); } - - -void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function, QV4::String *n) +void Heap::FunctionObject::init(QV4::ExecutionEngine *engine, const QString &name) { - jsCall = vtable()->call; - jsConstruct = vtable()->callAsConstructor; - - Object::init(); - setFunction(function); - this->scope.set(scope->engine(), scope->d()); - Scope s(scope->engine()); - ScopedString name(s, n ? n->d() : function->name()); - ScopedFunctionObject f(s, this); - if (name) - f->setName(name); + Scope valueScope(engine); + ScopedString s(valueScope, engine->newString(name)); + init(engine, s); } -void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const QString &name) +void Heap::FunctionObject::init() { - Scope valueScope(scope); - ScopedString s(valueScope, valueScope.engine->newString(name)); - init(scope, s); + init(internalClass->engine, static_cast<QV4::String *>(nullptr)); } -void Heap::FunctionObject::init() +void Heap::JavaScriptFunctionObject::init( + QV4::ExecutionContext *scope, Function *function, QV4::String *n) { - jsCall = vtable()->call; - jsConstruct = vtable()->callAsConstructor; - - Object::init(); - this->scope.set(internalClass->engine, internalClass->engine->rootContext()->d()); + Q_ASSERT(n || function); + Scope s(scope->engine()); + ScopedString name(s, n ? n->d() : function->name()); + FunctionObject::init(s.engine, name); + this->scope.set(s.engine, scope->d()); + setFunction(function); } -void Heap::FunctionObject::setFunction(Function *f) +void Heap::JavaScriptFunctionObject::setFunction(Function *f) { if (f) { function = f; function->executableCompilationUnit()->addref(); } } -void Heap::FunctionObject::destroy() +void Heap::JavaScriptFunctionObject::destroy() { if (function) function->executableCompilationUnit()->release(); - Object::destroy(); + FunctionObject::destroy(); +} + +void Heap::DynamicFunctionObject::init( + QV4::ExecutionEngine *engine, QV4::String *name, VTable::Call call) +{ + FunctionObject::init(engine, name); + jsCall = call; } void FunctionObject::createDefaultPrototypeProperty(uint protoConstructorSlot) @@ -158,12 +96,27 @@ void FunctionObject::createDefaultPrototypeProperty(uint protoConstructorSlot) ReturnedValue FunctionObject::name() const { - return get(scope()->internalClass->engine->id_name()); + return get(engine()->id_name()); } -ReturnedValue FunctionObject::virtualCall(const FunctionObject *, const Value *, const Value *, int) +ReturnedValue FunctionObject::failCall() const { - return Encode::undefined(); + return engine()->throwTypeError(QStringLiteral("Function can only be called with |new|.")); +} + +ReturnedValue FunctionObject::failCallAsConstructor() const +{ + return engine()->throwTypeError(QStringLiteral("Function is not a constructor.")); +} + +void FunctionObject::virtualConvertAndCall( + const FunctionObject *f, QObject *thisObject, + void **argv, const QMetaType *types, int argc) +{ + QV4::convertAndCall(f->engine(), thisObject, argv, types, argc, + [f](const Value *thisObject, const Value *argv, int argc) { + return f->call(thisObject, argv, argc); + }); } Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function) @@ -173,15 +126,20 @@ Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *sco return scope->engine()->memoryManager->allocate<ScriptFunction>(scope, function); } -Heap::FunctionObject *FunctionObject::createConstructorFunction(ExecutionContext *scope, Function *function, Object *homeObject, bool isDerivedConstructor) +Heap::FunctionObject *FunctionObject::createConstructorFunction( + ExecutionContext *scope, Function *function, Object *homeObject, bool isDerivedConstructor) { + QV4::ExecutionEngine *engine = scope->engine(); if (!function) { - Heap::DefaultClassConstructorFunction *c = scope->engine()->memoryManager->allocate<DefaultClassConstructorFunction>(scope); + Heap::DefaultClassConstructorFunction *c + = engine->memoryManager->allocate<DefaultClassConstructorFunction>(scope); c->isDerivedConstructor = isDerivedConstructor; return c; } - Heap::ConstructorFunction *c = scope->engine()->memoryManager->allocate<ConstructorFunction>(scope, function); - c->homeObject.set(scope->engine(), homeObject->d()); + + Heap::ConstructorFunction *c + = engine->memoryManager->allocate<ConstructorFunction>(scope, function); + c->homeObject.set(engine, homeObject->d()); c->isDerivedConstructor = isDerivedConstructor; return c; } @@ -198,9 +156,10 @@ Heap::FunctionObject *FunctionObject::createBuiltinFunction(ExecutionEngine *eng Scope scope(engine); ScopedString name(scope, nameOrSymbol); if (!name) - name = engine->newString(QChar::fromLatin1('[') + nameOrSymbol->toQString().midRef(1) + QChar::fromLatin1(']')); + name = engine->newString(QChar::fromLatin1('[') + QStringView{nameOrSymbol->toQString()}.mid(1) + QChar::fromLatin1(']')); - ScopedFunctionObject function(scope, engine->memoryManager->allocate<FunctionObject>(engine->rootContext(), name, code)); + ScopedFunctionObject function( + scope, engine->memoryManager->allocate<DynamicFunctionObject>(engine, name, code)); function->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(argumentCount)); return function->d(); } @@ -216,16 +175,29 @@ ReturnedValue FunctionObject::getHomeObject() const return Encode::undefined(); } -QQmlSourceLocation FunctionObject::sourceLocation() const +DEFINE_OBJECT_VTABLE(JavaScriptFunctionObject); + +QQmlSourceLocation JavaScriptFunctionObject::sourceLocation() const { return d()->function->sourceLocation(); } +DEFINE_OBJECT_VTABLE(DynamicFunctionObject); + +ReturnedValue DynamicFunctionObject::virtualCall( + const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) { + Heap::DynamicFunctionObject *d = static_cast<const DynamicFunctionObject *>(f)->d(); + if (d->jsCall) + return d->jsCall(f, thisObject, argv, argc); + return d->internalClass->engine->throwTypeError( + QStringLiteral("Function can only be called with |new|.")); +} + DEFINE_OBJECT_VTABLE(FunctionCtor); -void Heap::FunctionCtor::init(QV4::ExecutionContext *scope) +void Heap::FunctionCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("Function")); + Heap::FunctionObject::init(engine, QStringLiteral("Function")); } // 15.3.2 @@ -273,7 +245,7 @@ QQmlRefPointer<ExecutableCompilationUnit> FunctionCtor::parse(ExecutionEngine *e if (engine->hasException) return nullptr; - return ExecutableCompilationUnit::create(cg.generateCompilationUnit()); + return engine->insertCompilationUnit(cg.generateCompilationUnit()); } ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) @@ -285,7 +257,7 @@ ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, co if (engine->hasException) return Encode::undefined(); - Function *vmf = compilationUnit->linkToEngine(engine); + Function *vmf = compilationUnit->rootFunction(); ExecutionContext *global = engine->scriptContext(); ReturnedValue o = Encode(FunctionObject::createScriptFunction(global, vmf)); @@ -328,6 +300,12 @@ void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(engine->symbol_hasInstance(), method_hasInstance, 1, Attr_ReadOnly); } +ReturnedValue FunctionPrototype::virtualCall( + const FunctionObject *, const Value *, const Value *, int) +{ + return Encode::undefined(); +} + ReturnedValue FunctionPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); @@ -358,47 +336,49 @@ ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, cons return v4->throwTypeError(); thisObject = argc ? argv : nullptr; if (argc < 2 || argv[1].isNullOrUndefined()) - return f->call(thisObject, argv, 0); + return checkedResult(v4, f->call(thisObject, argv, 0)); Object *arr = argv[1].objectValue(); if (!arr) return v4->throwTypeError(); - uint len = arr->getLength(); - Scope scope(v4); + const int len = v4->safeForAllocLength(arr->getLength()); + CHECK_EXCEPTION(); + Value *arguments = scope.alloc<Scope::Uninitialized>(len); if (len) { if (ArgumentsObject::isNonStrictArgumentsObject(arr) && !arr->cast<ArgumentsObject>()->fullyCreated()) { QV4::ArgumentsObject *a = arr->cast<ArgumentsObject>(); - int l = qMin(len, (uint)a->d()->context->argc()); + int l = qMin(len, a->d()->context->argc()); memcpy(arguments, a->d()->context->args(), l*sizeof(Value)); - for (quint32 i = l; i < len; ++i) + for (int i = l; i < len; ++i) arguments[i] = Value::undefinedValue(); } else if (arr->arrayType() == Heap::ArrayData::Simple && !arr->protoHasArray()) { auto sad = static_cast<Heap::SimpleArrayData *>(arr->arrayData()); - uint alen = sad ? sad->values.size : 0; + int alen = sad ? sad->values.size : 0; if (alen > len) alen = len; - for (uint i = 0; i < alen; ++i) + for (int i = 0; i < alen; ++i) arguments[i] = sad->data(i); - for (quint32 i = alen; i < len; ++i) + for (int i = alen; i < len; ++i) arguments[i] = Value::undefinedValue(); } else { // need to init the arguments array, as the get() calls below can have side effects memset(arguments, 0, len*sizeof(Value)); - for (quint32 i = 0; i < len; ++i) + for (int i = 0; i < len; ++i) arguments[i] = arr->get(i); } } - return f->call(thisObject, arguments, len); + return checkedResult(v4, f->call(thisObject, arguments, len)); } ReturnedValue FunctionPrototype::method_call(const QV4::FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { + QV4::ExecutionEngine *v4 = b->engine(); if (!thisObject->isFunctionObject()) - return b->engine()->throwTypeError(); + return v4->throwTypeError(); const FunctionObject *f = static_cast<const FunctionObject *>(thisObject); @@ -407,7 +387,7 @@ ReturnedValue FunctionPrototype::method_call(const QV4::FunctionObject *b, const ++argv; --argc; } - return f->call(thisObject, argv, argc); + return checkedResult(v4, f->call(thisObject, argv, argc)); } ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) @@ -442,10 +422,13 @@ ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Valu boundArgs->set(scope.engine, i, argv[i + 1]); } - ScopedContext ctx(scope, target->scope()); - Heap::BoundFunction *bound = BoundFunction::create(ctx, target, boundThis, boundArgs); - bound->setFunction(target->function()); - return bound->asReturnedValue(); + if (target->isConstructor()) { + return scope.engine->memoryManager->allocate<BoundConstructor>(target, boundThis, boundArgs) + ->asReturnedValue(); + } + + return scope.engine->memoryManager->allocate<BoundFunction>(target, boundThis, boundArgs) + ->asReturnedValue(); } ReturnedValue FunctionPrototype::method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc) @@ -480,18 +463,18 @@ ReturnedValue ScriptFunction::virtualCallAsConstructor(const FunctionObject *fo, } ScopedValue thisObject(scope, v4->memoryManager->allocObject<Object>(ic)); - CppStackFrame frame; - frame.init(v4, f->function(), argv, argc); + JSTypesStackFrame frame; + frame.init(f->function(), argv, argc); frame.setupJSFrame(v4->jsStackTop, *f, f->scope(), thisObject, newTarget ? *newTarget : Value::undefinedValue()); - frame.push(); + frame.push(v4); v4->jsStackTop += frame.requiredJSStackFrameSize(); ReturnedValue result = Moth::VME::exec(&frame, v4); - frame.pop(); + frame.pop(v4); if (Q_UNLIKELY(v4->hasException)) return Encode::undefined(); @@ -502,49 +485,87 @@ ReturnedValue ScriptFunction::virtualCallAsConstructor(const FunctionObject *fo, DEFINE_OBJECT_VTABLE(ArrowFunction); -ReturnedValue ArrowFunction::virtualCall(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc) +void ArrowFunction::virtualCallWithMetaTypes(const FunctionObject *fo, QObject *thisObject, + void **a, const QMetaType *types, int argc) +{ + const ArrowFunction *self = static_cast<const ArrowFunction *>(fo); + Function *function = self->function(); + if (function->kind != Function::AotCompiled) { + QV4::convertAndCall(fo->engine(), thisObject, a, types, argc, + [fo](const Value *thisObject, const Value *argv, int argc) { + return ArrowFunction::virtualCall(fo, thisObject, argv, argc); + }); + return; + } + + QV4::Scope scope(fo->engine()); + QV4::Scoped<ExecutionContext> context(scope, self->scope()); + MetaTypesStackFrame frame; + frame.init(function, thisObject, context, a, types, argc); + frame.push(scope.engine); + Moth::VME::exec(&frame, scope.engine); + frame.pop(scope.engine); +} + +static ReturnedValue qfoDoCall( + const QV4::JavaScriptFunctionObject *fo, const QV4::Value *thisObject, + const QV4::Value *argv, int argc) { ExecutionEngine *engine = fo->engine(); - CppStackFrame frame; - frame.init(engine, fo->function(), argv, argc, true); + JSTypesStackFrame frame; + frame.init(fo->function(), argv, argc, true); frame.setupJSFrame(engine->jsStackTop, *fo, fo->scope(), - thisObject ? *thisObject : Value::undefinedValue(), - Value::undefinedValue()); + thisObject ? *thisObject : Value::undefinedValue()); - frame.push(); + frame.push(engine); engine->jsStackTop += frame.requiredJSStackFrameSize(); ReturnedValue result; do { - frame.pendingTailCall = false; + frame.setPendingTailCall(false); result = Moth::VME::exec(&frame, engine); - frame.isTailCalling = true; - } while (frame.pendingTailCall); + frame.setTailCalling(true); + } while (frame.pendingTailCall()); - frame.pop(); + frame.pop(engine); return result; } +ReturnedValue ArrowFunction::virtualCall(const QV4::FunctionObject *fo, const Value *thisObject, + const QV4::Value *argv, int argc) +{ + const ArrowFunction *self = static_cast<const ArrowFunction *>(fo); + Function *function = self->function(); + switch (function->kind) { + case Function::AotCompiled: + return QV4::convertAndCall( + fo->engine(), &function->aotCompiledFunction, thisObject, argv, argc, + [fo](QObject *thisObject, void **a, const QMetaType *types, int argc) { + ArrowFunction::virtualCallWithMetaTypes(fo, thisObject, a, types, argc); + }); + case Function::JsTyped: + return QV4::coerceAndCall( + fo->engine(), &function->jsTypedFunction, function->compiledFunction, argv, argc, + [self, thisObject](const Value *argv, int argc) { + return qfoDoCall(self, thisObject, argv, argc); + }); + default: + break; + } + + return qfoDoCall(self, thisObject, argv, argc); +} + void Heap::ArrowFunction::init(QV4::ExecutionContext *scope, Function *function, QV4::String *n) { - FunctionObject::init(); - this->scope.set(scope->engine(), scope->d()); - - setFunction(function); Q_ASSERT(function); + JavaScriptFunctionObject::init(scope, function, n); Scope s(scope); - ScopedFunctionObject f(s, this); - - ScopedString name(s, n ? n->d() : function->name()); - if (name) - f->setName(name); - Q_ASSERT(internalClass && internalClass->verifyIndex(s.engine->id_length()->propertyKey(), Index_Length)); setProperty(s.engine, Index_Length, Value::fromInt32(int(function->compiledFunction->length))); - canBeTailCalled = true; } void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function) @@ -557,6 +578,13 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function f->createDefaultPrototypeProperty(Heap::FunctionObject::Index_ProtoConstructor); } +void Heap::DefaultClassConstructorFunction::init(QV4::ExecutionContext *scope) +{ + Scope s(scope->engine()); + FunctionObject::init(s.engine, nullptr); + this->scope.set(s.engine, scope->d()); +} + Heap::InternalClass *ScriptFunction::classForConstructor() const { Scope scope(engine()); @@ -583,19 +611,19 @@ ReturnedValue ConstructorFunction::virtualCallAsConstructor(const FunctionObject ExecutionEngine *v4 = f->engine(); - CppStackFrame frame; - frame.init(v4, f->function(), argv, argc); - frame.setupJSFrame(v4->jsStackTop, *f, f->scope(), + JSTypesStackFrame frame; + frame.init(c->function(), argv, argc); + frame.setupJSFrame(v4->jsStackTop, *f, c->scope(), Value::emptyValue(), newTarget ? *newTarget : Value::undefinedValue()); - frame.push(); + frame.push(v4); v4->jsStackTop += frame.requiredJSStackFrameSize(); ReturnedValue result = Moth::VME::exec(&frame, v4); ReturnedValue thisObject = frame.jsFrame->thisObject.asReturnedValue(); - frame.pop(); + frame.pop(v4); if (Q_UNLIKELY(v4->hasException)) return Encode::undefined(); @@ -637,20 +665,20 @@ ReturnedValue DefaultClassConstructorFunction::virtualCallAsConstructor(const Fu ScopedFunctionObject super(scope, f->getPrototypeOf()); Q_ASSERT(super->isFunctionObject()); - CppStackFrame frame; - frame.init(v4, nullptr, argv, argc); - frame.setupJSFrame(v4->jsStackTop, *f, f->scope(), + JSTypesStackFrame frame; + frame.init(nullptr, argv, argc); + frame.setupJSFrame(v4->jsStackTop, *f, c->scope(), Value::undefinedValue(), newTarget ? *newTarget : Value::undefinedValue(), argc, argc); - frame.push(); + frame.push(v4); v4->jsStackTop += frame.requiredJSStackFrameSize(argc); // Do a super call ReturnedValue result = super->callAsConstructor(argv, argc, newTarget); ReturnedValue thisObject = frame.jsFrame->thisObject.asReturnedValue(); - frame.pop(); + frame.pop(v4); if (Q_UNLIKELY(v4->hasException)) return Encode::undefined(); @@ -676,57 +704,66 @@ DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction); DEFINE_OBJECT_VTABLE(BoundFunction); -void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject *target, - const Value &boundThis, QV4::MemberData *boundArgs) +void Heap::BoundFunction::init( + QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs) { - Scope s(scope); - Heap::FunctionObject::init(scope, QStringLiteral("__bound function__")); + ExecutionEngine *engine = target->engine(); + Scope s(engine); + ScopedString name(s, engine->newString(QStringLiteral("__bound function__"))); + if (auto *js = target->as<QV4::JavaScriptFunctionObject>()) { + ScopedContext ctx(s, js->scope()); + JavaScriptFunctionObject::init(ctx, js->function(), name); + } else { + Q_ASSERT(name); + JavaScriptFunctionObject::init(engine->rootContext(), nullptr, name); + } + this->target.set(s.engine, target->d()); this->boundArgs.set(s.engine, boundArgs ? boundArgs->d() : nullptr); - this->boundThis.set(scope->engine(), boundThis); - - if (!target->isConstructor()) - jsConstruct = nullptr; + this->boundThis.set(s.engine, boundThis); ScopedObject f(s, this); - ScopedValue l(s, target->get(s.engine->id_length())); + ScopedValue l(s, target->get(engine->id_length())); int len = l->toUInt32(); if (boundArgs) len -= boundArgs->size(); if (len < 0) len = 0; - f->defineReadonlyConfigurableProperty(s.engine->id_length(), Value::fromInt32(len)); + f->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(len)); ScopedProperty pd(s); - pd->value = s.engine->thrower(); - pd->set = s.engine->thrower(); - f->insertMember(s.engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + pd->value = engine->thrower(); + pd->set = engine->thrower(); + f->insertMember(engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + f->insertMember(engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } ReturnedValue BoundFunction::virtualCall(const FunctionObject *fo, const Value *, const Value *argv, int argc) { - const BoundFunction *f = static_cast<const BoundFunction *>(fo); - Scope scope(f->engine()); - - if (scope.hasException()) + QV4::ExecutionEngine *v4 = fo->engine(); + if (v4->hasException) return Encode::undefined(); + const BoundFunction *f = static_cast<const BoundFunction *>(fo); + Scope scope(v4); Scoped<MemberData> boundArgs(scope, f->boundArgs()); ScopedFunctionObject target(scope, f->target()); - JSCallData jsCallData(scope, (boundArgs ? boundArgs->size() : 0) + argc); - *jsCallData->thisObject = f->boundThis(); - Value *argp = jsCallData->args; + JSCallArguments jsCallData(scope, (boundArgs ? boundArgs->size() : 0) + argc); + *jsCallData.thisObject = f->boundThis(); + Value *argp = jsCallData.args; if (boundArgs) { memcpy(argp, boundArgs->data(), boundArgs->size()*sizeof(Value)); argp += boundArgs->size(); } memcpy(argp, argv, argc*sizeof(Value)); - return target->call(jsCallData); + return checkedResult(v4, target->call(jsCallData)); } -ReturnedValue BoundFunction::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *) +DEFINE_OBJECT_VTABLE(BoundConstructor); + +ReturnedValue BoundConstructor::virtualCallAsConstructor( + const FunctionObject *fo, const Value *argv, int argc, const Value *) { const BoundFunction *f = static_cast<const BoundFunction *>(fo); Scope scope(f->engine()); @@ -736,8 +773,8 @@ ReturnedValue BoundFunction::virtualCallAsConstructor(const FunctionObject *fo, Scoped<MemberData> boundArgs(scope, f->boundArgs()); ScopedFunctionObject target(scope, f->target()); - JSCallData jsCallData(scope, (boundArgs ? boundArgs->size() : 0) + argc); - Value *argp = jsCallData->args; + JSCallArguments jsCallData(scope, (boundArgs ? boundArgs->size() : 0) + argc); + Value *argp = jsCallData.args; if (boundArgs) { memcpy(argp, boundArgs->data(), boundArgs->size()*sizeof(Value)); argp += boundArgs->size(); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index c99cad8e33..f4a2935b5a 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4FUNCTIONOBJECT_H #define QV4FUNCTIONOBJECT_H @@ -64,34 +28,42 @@ namespace QV4 { struct IndexedBuiltinFunction; struct JSCallData; -namespace Heap { - +// A FunctionObject is generally something that can be called, either with a JavaScript +// signature (QV4::Value etc) or with a C++ signature (QMetaType etc). For this, it has +// the Call and CallWithMetaTypes VTable entries. +// Some FunctionObjects need to select the actual implementation of the call at run time. +// This comese in two flavors: +// 1. The implementation is a JavaScript function. For these we have +// JavaScriptFunctionObject that holds a QV4::Function member to defer the call to. +// 2. The implementation is a C++ function. For these we have DynamicFunctionObject that +// holds another Call member in the heap object to defer the call to. +// In addition, a FunctionObject may want to be called as constructor. For this we have +// another VTable entry and a flag in the heap object. -#define FunctionObjectMembers(class, Member) \ - Member(class, Pointer, ExecutionContext *, scope) \ - Member(class, NoMark, Function *, function) \ - Member(class, NoMark, VTable::Call, jsCall) \ - Member(class, NoMark, VTable::CallAsConstructor, jsConstruct) \ - Member(class, NoMark, bool, canBeTailCalled) +namespace Heap { +#define FunctionObjectMembers(class, Member) DECLARE_HEAP_OBJECT(FunctionObject, Object) { - DECLARE_MARKOBJECTS(FunctionObject); enum { Index_ProtoConstructor = 0, Index_Prototype = 0, Index_HasInstance = 1, }; - bool isConstructor() const { - return jsConstruct != nullptr; - } + Q_QML_EXPORT void init(QV4::ExecutionEngine *engine, QV4::String *name = nullptr); + Q_QML_EXPORT void init(QV4::ExecutionEngine *engine, const QString &name); + Q_QML_EXPORT void init(); +}; + +#define JavaScriptFunctionObjectMembers(class, Member) \ + Member(class, Pointer, ExecutionContext *, scope) \ + Member(class, NoMark, Function *, function) - Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name, VTable::Call call); - Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr); - Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr); - Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, const QString &name); - Q_QML_PRIVATE_EXPORT void init(); - Q_QML_PRIVATE_EXPORT void destroy(); +DECLARE_HEAP_OBJECT(JavaScriptFunctionObject, FunctionObject) { + DECLARE_MARKOBJECTS(JavaScriptFunctionObject) + + void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr); + Q_QML_EXPORT void destroy(); void setFunction(Function *f); @@ -99,20 +71,32 @@ DECLARE_HEAP_OBJECT(FunctionObject, Object) { unsigned int varCount() { return function ? function->compiledFunction->nLocals : 0; } }; +#define DynamicFunctionObjectMembers(class, Member) \ + Member(class, NoMark, VTable::Call, jsCall) + +DECLARE_HEAP_OBJECT(DynamicFunctionObject, FunctionObject) { + // NB: We might add a CallWithMetaTypes member to this struct and implement our + // builtins with metatypes, to be called from C++ code. This would make them + // available to qmlcachegen's C++ code generation. + void init(ExecutionEngine *engine, QV4::String *name, VTable::Call call); +}; + struct FunctionCtor : FunctionObject { - void init(QV4::ExecutionContext *scope); + void init(QV4::ExecutionEngine *engine); }; struct FunctionPrototype : FunctionObject { void init(); }; -struct IndexedBuiltinFunction : FunctionObject { - inline void init(QV4::ExecutionContext *scope, uint index, VTable::Call call); - uint index; +// A function object with an additional index into a list. +// Used by Models to refer to property roles. +struct IndexedBuiltinFunction : DynamicFunctionObject { + inline void init(QV4::ExecutionEngine *engine, qsizetype index, VTable::Call call); + qsizetype index; }; -struct ArrowFunction : FunctionObject { +struct ArrowFunction : JavaScriptFunctionObject { enum { Index_Name = Index_HasInstance + 1, Index_Length @@ -147,9 +131,15 @@ DECLARE_HEAP_OBJECT(ConstructorFunction, ScriptFunction) { bool isDerivedConstructor; }; -struct DefaultClassConstructorFunction : FunctionObject -{ +#define DefaultClassConstructorFunctionMembers(class, Member) \ + Member(class, Pointer, ExecutionContext *, scope) + +DECLARE_HEAP_OBJECT(DefaultClassConstructorFunction, FunctionObject) { + DECLARE_MARKOBJECTS(DefaultClassConstructorFunction) + bool isDerivedConstructor; + + void init(QV4::ExecutionContext *scope); }; #define BoundFunctionMembers(class, Member) \ @@ -157,63 +147,72 @@ struct DefaultClassConstructorFunction : FunctionObject Member(class, HeapValue, HeapValue, boundThis) \ Member(class, Pointer, MemberData *, boundArgs) -DECLARE_HEAP_OBJECT(BoundFunction, FunctionObject) { - DECLARE_MARKOBJECTS(BoundFunction); +DECLARE_HEAP_OBJECT(BoundFunction, JavaScriptFunctionObject) { + DECLARE_MARKOBJECTS(BoundFunction) - void init(QV4::ExecutionContext *scope, QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs); + void init(QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs); }; +struct BoundConstructor : BoundFunction {}; + } struct Q_QML_EXPORT FunctionObject: Object { - enum { - IsFunctionObject = true - }; V4_OBJECT2(FunctionObject, Object) Q_MANAGED_TYPE(FunctionObject) V4_INTERNALCLASS(FunctionObject) V4_PROTOTYPE(functionPrototype) - V4_NEEDS_DESTROY enum { NInlineProperties = 1 }; - bool canBeTailCalled() const { return d()->canBeTailCalled; } - Heap::ExecutionContext *scope() const { return d()->scope; } - Function *function() const { return d()->function; } + bool canBeTailCalled() const { return vtable()->isTailCallable; } ReturnedValue name() const; - unsigned int formalParameterCount() const { return d()->formalParameterCount(); } - unsigned int varCount() const { return d()->varCount(); } void setName(String *name) { defineReadonlyConfigurableProperty(engine()->id_name(), *name); } void createDefaultPrototypeProperty(uint protoConstructorSlot); - inline ReturnedValue callAsConstructor(const JSCallData &data) const; - ReturnedValue callAsConstructor(const Value *argv, int argc, const Value *newTarget = nullptr) const { - if (!d()->jsConstruct) - return engine()->throwTypeError(QStringLiteral("Function is not a constructor.")); - return d()->jsConstruct(this, argv, argc, newTarget ? newTarget : this); + ReturnedValue callAsConstructor( + const Value *argv, int argc, const Value *newTarget = nullptr) const + { + if (const auto callAsConstructor = vtable()->callAsConstructor) + return callAsConstructor(this, argv, argc, newTarget ? newTarget : this); + return failCallAsConstructor(); } - inline ReturnedValue call(const JSCallData &data) const; - ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const { - if (!d()->jsCall) - return engine()->throwTypeError(QStringLiteral("Function can only be called with |new|.")); - return d()->jsCall(this, thisObject, argv, argc); + + ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const + { + if (const auto call = vtable()->call) + return call(this, thisObject, argv, argc); + return failCall(); } - static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + + void call(QObject *thisObject, void **argv, const QMetaType *types, int argc) const + { + if (const auto callWithMetaTypes = vtable()->callWithMetaTypes) + callWithMetaTypes(this, thisObject, argv, types, argc); + else + failCall(); + } + + inline ReturnedValue callAsConstructor(const JSCallData &data) const; + inline ReturnedValue call(const JSCallData &data) const; + + ReturnedValue failCall() const; + ReturnedValue failCallAsConstructor() const; + static void virtualConvertAndCall( + const FunctionObject *f, QObject *thisObject, + void **argv, const QMetaType *types, int argc); static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function); static Heap::FunctionObject *createConstructorFunction(ExecutionContext *scope, Function *function, Object *homeObject, bool isDerivedConstructor); static Heap::FunctionObject *createMemberFunction(ExecutionContext *scope, Function *function, Object *homeObject, String *name); static Heap::FunctionObject *createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, VTable::Call code, int argumentCount); - bool strictMode() const { return d()->function ? d()->function->isStrict() : false; } bool isBinding() const; bool isBoundFunction() const; - bool isConstructor() const { - return d()->isConstructor(); - } + bool isConstructor() const { return vtable()->callAsConstructor; } ReturnedValue getHomeObject() const; @@ -223,15 +222,40 @@ struct Q_QML_EXPORT FunctionObject: Object { bool hasHasInstanceProperty() const { return !internalClass()->propertyData.at(Heap::FunctionObject::Index_HasInstance).isEmpty(); } - - QQmlSourceLocation sourceLocation() const; }; template<> inline const FunctionObject *Value::as() const { - return isManaged() && m()->internalClass->vtable->isFunctionObject ? reinterpret_cast<const FunctionObject *>(this) : nullptr; + if (!isManaged()) + return nullptr; + + const VTable *vtable = m()->internalClass->vtable; + return (vtable->call || vtable->callAsConstructor) + ? reinterpret_cast<const FunctionObject *>(this) + : nullptr; } +struct Q_QML_EXPORT JavaScriptFunctionObject: FunctionObject +{ + V4_OBJECT2(JavaScriptFunctionObject, FunctionObject) + V4_NEEDS_DESTROY + + Heap::ExecutionContext *scope() const { return d()->scope; } + + Function *function() const { return d()->function; } + unsigned int formalParameterCount() const { return d()->formalParameterCount(); } + unsigned int varCount() const { return d()->varCount(); } + bool strictMode() const { return d()->function ? d()->function->isStrict() : false; } + QQmlSourceLocation sourceLocation() const; +}; + +struct Q_QML_EXPORT DynamicFunctionObject: FunctionObject +{ + V4_OBJECT2(DynamicFunctionObject, FunctionObject) + + static ReturnedValue virtualCall( + const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; struct FunctionCtor: FunctionObject { @@ -253,6 +277,9 @@ struct FunctionPrototype: FunctionObject void init(ExecutionEngine *engine, Object *ctor); + static ReturnedValue virtualCall( + const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); @@ -260,24 +287,31 @@ struct FunctionPrototype: FunctionObject static ReturnedValue method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; -struct Q_QML_PRIVATE_EXPORT IndexedBuiltinFunction : FunctionObject +struct Q_QML_EXPORT IndexedBuiltinFunction : DynamicFunctionObject { - V4_OBJECT2(IndexedBuiltinFunction, FunctionObject) + V4_OBJECT2(IndexedBuiltinFunction, DynamicFunctionObject) }; -void Heap::IndexedBuiltinFunction::init(QV4::ExecutionContext *scope, uint index, VTable::Call call) +void Heap::IndexedBuiltinFunction::init( + QV4::ExecutionEngine *engine, qsizetype index, VTable::Call call) { - Heap::FunctionObject::init(scope); + Heap::FunctionObject::init(engine); this->jsCall = call; this->index = index; } -struct ArrowFunction : FunctionObject { - V4_OBJECT2(ArrowFunction, FunctionObject) +struct ArrowFunction : JavaScriptFunctionObject { + V4_OBJECT2(ArrowFunction, JavaScriptFunctionObject) V4_INTERNALCLASS(ArrowFunction) - enum { NInlineProperties = 3 }; + enum { + NInlineProperties = 3, + IsTailCallable = true, + }; - static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static void virtualCallWithMetaTypes(const FunctionObject *f, QObject *thisObject, + void **a, const QMetaType *types, int argc); + static ReturnedValue virtualCall(const QV4::FunctionObject *f, const QV4::Value *thisObject, + const QV4::Value *argv, int argc); }; struct ScriptFunction : ArrowFunction { @@ -303,31 +337,39 @@ struct ConstructorFunction : ScriptFunction { struct DefaultClassConstructorFunction : FunctionObject { V4_OBJECT2(DefaultClassConstructorFunction, FunctionObject) + + Heap::ExecutionContext *scope() const { return d()->scope; } static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; -struct BoundFunction: FunctionObject { - V4_OBJECT2(BoundFunction, FunctionObject) - - static Heap::BoundFunction *create(ExecutionContext *scope, FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs) - { - return scope->engine()->memoryManager->allocate<BoundFunction>(scope, target, boundThis, boundArgs); - } +struct BoundFunction: JavaScriptFunctionObject { + V4_OBJECT2(BoundFunction, JavaScriptFunctionObject) Heap::FunctionObject *target() const { return d()->target; } Value boundThis() const { return d()->boundThis; } Heap::MemberData *boundArgs() const { return d()->boundArgs; } - static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; +struct BoundConstructor: BoundFunction { + V4_OBJECT2(BoundConstructor, BoundFunction) + + static ReturnedValue virtualCallAsConstructor( + const FunctionObject *f, const Value *argv, int argc, const Value *); +}; + inline bool FunctionObject::isBoundFunction() const { - return d()->vtable() == BoundFunction::staticVTable(); + const VTable *vtable = d()->vtable(); + return vtable == BoundFunction::staticVTable() || vtable == BoundConstructor::staticVTable(); } +inline ReturnedValue checkedResult(QV4::ExecutionEngine *v4, ReturnedValue result) +{ + return v4->hasException ? QV4::Encode::undefined() : result; +} } diff --git a/src/qml/jsruntime/qv4functiontable_noop.cpp b/src/qml/jsruntime/qv4functiontable_noop.cpp index 31c198eb00..8a72fa5469 100644 --- a/src/qml/jsruntime/qv4functiontable_noop.cpp +++ b/src/qml/jsruntime/qv4functiontable_noop.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4functiontable_p.h" diff --git a/src/qml/jsruntime/qv4functiontable_p.h b/src/qml/jsruntime/qv4functiontable_p.h index 69e3d2bdd5..8937e2fe85 100644 --- a/src/qml/jsruntime/qv4functiontable_p.h +++ b/src/qml/jsruntime/qv4functiontable_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4FUNCTIONTABLE_P_H #define QV4FUNCTIONTABLE_P_H @@ -51,7 +15,7 @@ // We mean it. // -#include "qv4global_p.h" +#include <QtQml/private/qqmlglobal_p.h> namespace JSC { class MacroAssemblerCodeRef; diff --git a/src/qml/jsruntime/qv4functiontable_unix.cpp b/src/qml/jsruntime/qv4functiontable_unix.cpp index 25b5c27161..9561917777 100644 --- a/src/qml/jsruntime/qv4functiontable_unix.cpp +++ b/src/qml/jsruntime/qv4functiontable_unix.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4functiontable_p.h" #include "qv4function_p.h" diff --git a/src/qml/jsruntime/qv4functiontable_win64.cpp b/src/qml/jsruntime/qv4functiontable_win64.cpp index fc13dc2602..c21cdb790a 100644 --- a/src/qml/jsruntime/qv4functiontable_win64.cpp +++ b/src/qml/jsruntime/qv4functiontable_win64.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4functiontable_p.h" @@ -43,7 +7,7 @@ #include <QtCore/qdebug.h> -#include <windows.h> +#include <qt_windows.h> QT_BEGIN_NAMESPACE @@ -106,7 +70,7 @@ struct ExceptionHandlerRecord void generateFunctionTable(Function *, JSC::MacroAssemblerCodeRef *codeRef) { ExceptionHandlerRecord *record = reinterpret_cast<ExceptionHandlerRecord *>( - codeRef->executableMemory()->exceptionHandler()); + codeRef->executableMemory()->exceptionHandlerStart()); record->info.Version = 1; record->info.Flags = 0; @@ -136,7 +100,7 @@ void generateFunctionTable(Function *, JSC::MacroAssemblerCodeRef *codeRef) void destroyFunctionTable(Function *, JSC::MacroAssemblerCodeRef *codeRef) { ExceptionHandlerRecord *record = reinterpret_cast<ExceptionHandlerRecord *>( - codeRef->executableMemory()->exceptionHandler()); + codeRef->executableMemory()->exceptionHandlerStart()); if (!RtlDeleteFunctionTable(&record->handler)) { const unsigned int errorCode = GetLastError(); qWarning() << "Failed to remove win64 unwind hook. Error code:" << errorCode; diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp index 4eee6f4338..f2d7dffde5 100644 --- a/src/qml/jsruntime/qv4generatorobject.cpp +++ b/src/qml/jsruntime/qv4generatorobject.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <qv4generatorobject_p.h> #include <qv4symbol_p.h> @@ -49,9 +13,9 @@ DEFINE_OBJECT_VTABLE(GeneratorFunctionCtor); DEFINE_OBJECT_VTABLE(GeneratorFunction); DEFINE_OBJECT_VTABLE(GeneratorObject); -void Heap::GeneratorFunctionCtor::init(QV4::ExecutionContext *scope) +void Heap::GeneratorFunctionCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("GeneratorFunction")); + Heap::FunctionObject::init(engine, QStringLiteral("GeneratorFunction")); } ReturnedValue GeneratorFunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) @@ -62,7 +26,7 @@ ReturnedValue GeneratorFunctionCtor::virtualCallAsConstructor(const FunctionObje if (engine->hasException) return Encode::undefined(); - Function *vmf = compilationUnit->linkToEngine(engine); + Function *vmf = compilationUnit->rootFunction(); ExecutionContext *global = engine->scriptContext(); ReturnedValue o = Encode(GeneratorFunction::create(global, vmf)); @@ -97,33 +61,31 @@ ReturnedValue GeneratorFunction::virtualCall(const FunctionObject *f, const Valu Function *function = gf->function(); ExecutionEngine *engine = gf->engine(); - // We need to set up a separate stack for the generator, as it's being re-entered - uint stackSize = argc // space for the original arguments - + CppStackFrame::requiredJSStackFrameSize(function); // space for the JS stack frame - - size_t requiredMemory = sizeof(GeneratorObject::Data) - sizeof(Value) + sizeof(Value) * stackSize; - Scope scope(gf); - Scoped<GeneratorObject> g(scope, scope.engine->memoryManager->allocManaged<GeneratorObject>(requiredMemory, scope.engine->classes[EngineBase::Class_GeneratorObject])); + Scoped<GeneratorObject> g(scope, engine->memoryManager->allocManaged<GeneratorObject>(engine->classes[EngineBase::Class_GeneratorObject])); g->setPrototypeOf(ScopedObject(scope, gf->get(scope.engine->id_prototype()))); + // We need to set up a separate JSFrame for the generator, as it's being re-entered Heap::GeneratorObject *gp = g->d(); - gp->stack.size = stackSize; - gp->stack.alloc = stackSize; + gp->values.set(engine, engine->newArrayObject(argc)); + gp->jsFrame.set(engine, engine->newArrayObject( + JSTypesStackFrame::requiredJSStackFrameSize(function))); // copy original arguments - memcpy(gp->stack.values, argv, argc*sizeof(Value)); - gp->cppFrame.init(engine, function, gp->stack.values, argc); - gp->cppFrame.setupJSFrame(&gp->stack.values[argc], *gf, gf->scope(), + for (int i = 0; i < argc; i++) + gp->values->arrayData->setArrayData(engine, i, argv[i]); + + gp->cppFrame.init(function, gp->values->arrayData->values.values, argc); + gp->cppFrame.setupJSFrame(gp->jsFrame->arrayData->values.values, *gf, gf->scope(), thisObject ? *thisObject : Value::undefinedValue(), Value::undefinedValue()); - gp->cppFrame.push(); + gp->cppFrame.push(engine); Moth::VME::interpret(&gp->cppFrame, engine, function->codeData); gp->state = GeneratorState::SuspendedStart; - gp->cppFrame.pop(); + gp->cppFrame.pop(engine); return g->asReturnedValue(); } @@ -191,7 +153,7 @@ ReturnedValue GeneratorPrototype::method_return(const FunctionObject *f, const V // a yield called with return() engine->throwError(Value::emptyValue()); - return g->resume(engine, argc ? argv[0]: Value::undefinedValue()); + return g->resume(engine, argc ? argv[0] : Value::undefinedValue()); } ReturnedValue GeneratorPrototype::method_throw(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) @@ -203,7 +165,7 @@ ReturnedValue GeneratorPrototype::method_throw(const FunctionObject *f, const Va Heap::GeneratorObject *gp = g->d(); - engine->throwError(argc ? argv[0]: Value::undefinedValue()); + engine->throwError(argc ? argv[0] : Value::undefinedValue()); if (gp->state == GeneratorState::SuspendedStart || gp->state == GeneratorState::Completed) { gp->state = GeneratorState::Completed; @@ -217,25 +179,25 @@ ReturnedValue GeneratorObject::resume(ExecutionEngine *engine, const Value &arg) { Heap::GeneratorObject *gp = d(); gp->state = GeneratorState::Executing; - gp->cppFrame.parent = engine->currentStackFrame; + gp->cppFrame.setParentFrame(engine->currentStackFrame); engine->currentStackFrame = &gp->cppFrame; - Q_ASSERT(gp->cppFrame.yield != nullptr); - const char *code = gp->cppFrame.yield; - gp->cppFrame.yield = nullptr; + Q_ASSERT(gp->cppFrame.yield() != nullptr); + const char *code = gp->cppFrame.yield(); + gp->cppFrame.setYield(nullptr); gp->cppFrame.jsFrame->accumulator = arg; - gp->cppFrame.yieldIsIterator = false; + gp->cppFrame.setYieldIsIterator(false); Scope scope(engine); ScopedValue result(scope, Moth::VME::interpret(&gp->cppFrame, engine, code)); - engine->currentStackFrame = gp->cppFrame.parent; + engine->currentStackFrame = gp->cppFrame.parentFrame(); - bool done = (gp->cppFrame.yield == nullptr); + bool done = (gp->cppFrame.yield() == nullptr); gp->state = done ? GeneratorState::Completed : GeneratorState::SuspendedYield; if (engine->hasException) return Encode::undefined(); - if (gp->cppFrame.yieldIsIterator) + if (gp->cppFrame.yieldIsIterator()) return result->asReturnedValue(); return IteratorPrototype::createIterResultObject(engine, result, done); } diff --git a/src/qml/jsruntime/qv4generatorobject_p.h b/src/qml/jsruntime/qv4generatorobject_p.h index 366319723d..cb2c1962c5 100644 --- a/src/qml/jsruntime/qv4generatorobject_p.h +++ b/src/qml/jsruntime/qv4generatorobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4GENERATOROBJECT_P_H #define QV4GENERATOROBJECT_P_H @@ -69,7 +33,7 @@ enum class GeneratorState { namespace Heap { struct GeneratorFunctionCtor : FunctionObject { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; struct GeneratorFunction : ArrowFunction { @@ -87,13 +51,13 @@ struct GeneratorPrototype : FunctionObject { #define GeneratorObjectMembers(class, Member) \ Member(class, Pointer, ExecutionContext *, context) \ - Member(class, Pointer, GeneratorFunction *, function) \ Member(class, NoMark, GeneratorState, state) \ - Member(class, NoMark, CppStackFrame, cppFrame) \ - Member(class, ValueArray, ValueArray, stack) + Member(class, NoMark, JSTypesStackFrame, cppFrame) \ + Member(class, Pointer, ArrayObject *, values) \ + Member(class, Pointer, ArrayObject *, jsFrame) DECLARE_HEAP_OBJECT(GeneratorObject, Object) { - DECLARE_MARKOBJECTS(GeneratorObject); + DECLARE_MARKOBJECTS(GeneratorObject) }; } diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index c6a737b467..e3fc0ac1b3 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4GLOBAL_H #define QV4GLOBAL_H @@ -55,30 +19,9 @@ #include <private/qv4compilerglobal_p.h> #include <QString> -#ifdef QT_NO_DEBUG -#define QML_NEARLY_ALWAYS_INLINE Q_ALWAYS_INLINE -#else -#define QML_NEARLY_ALWAYS_INLINE inline -#endif - #include <qtqmlglobal.h> #include <private/qtqmlglobal_p.h> -#if defined(Q_CC_MSVC) -#include <float.h> -#include <math.h> - -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); } - -} // namespace std - -inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); } -#endif - // Do certain things depending on whether the JIT is enabled or disabled #if QT_CONFIG(qml_jit) @@ -137,10 +80,13 @@ namespace Heap { struct ArrayObject; struct DateObject; struct FunctionObject; + struct JavaScriptFunctionObject; struct ErrorObject; struct ArgumentsObject; struct QObjectWrapper; struct RegExpObject; + struct UrlObject; + struct UrlSearchParamsObject; struct RegExp; struct EvalFunction; @@ -159,6 +105,8 @@ namespace Heap { } struct CppStackFrame; +struct JSTypesStackFrame; +struct MetaTypesStackFrame; class MemoryManager; class ExecutableAllocator; struct PropertyKey; @@ -244,6 +192,8 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(PropertyFlags) struct PropertyAttributes { + QT_WARNING_PUSH + QT_WARNING_DISABLE_MSVC(4201) // nonstandard extension used: nameless struct/union union { uchar m_all; struct { @@ -261,6 +211,7 @@ struct PropertyAttributes uchar configurable_set : 1; }; }; + QT_WARNING_POP enum Type { Data = 0, @@ -318,7 +269,6 @@ struct PropertyAttributes void clear() { m_all = 0; } bool isEmpty() const { return !m_all; } - uint flags() const { return m_flags; } uint all() const { return m_all; } bool operator==(PropertyAttributes other) { diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index bb81fb52d4..989de0de23 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -1,65 +1,29 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4globalobject_p.h" -#include <private/qv4mm_p.h> -#include "qv4value_p.h" -#include "qv4context_p.h" -#include "qv4function_p.h" -#include "qv4debugging_p.h" -#include "qv4profiling_p.h" -#include "qv4script_p.h" -#include "qv4scopedvalue_p.h" -#include "qv4string_p.h" -#include "qv4jscall_p.h" -#include <private/qv4codegen_p.h> #include <private/qv4alloca_p.h> -#include "private/qlocale_tools_p.h" -#include "private/qtools_p.h" - -#include <QtCore/QDebug> -#include <QtCore/QString> -#include <iostream> +#include <private/qv4codegen_p.h> +#include <private/qv4context_p.h> +#include <private/qv4function_p.h> +#include <private/qv4mm_p.h> +#include <private/qv4scopedvalue_p.h> +#include <private/qv4script_p.h> +#include <private/qv4stackframe_p.h> +#include <private/qv4string_p.h> +#include <private/qv4value_p.h> #include <wtf/MathExtras.h> +#include <QtCore/private/qlocale_tools_p.h> +#include <QtCore/private/qtools_p.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qstring.h> + +#include <iostream> + using namespace QV4; using QtMiscUtils::toHexUpper; using QtMiscUtils::fromHex; @@ -68,7 +32,7 @@ static QString escape(const QString &input) { QString output; output.reserve(input.size() * 3); - const int length = input.length(); + const int length = input.size(); for (int i = 0; i < length; ++i) { ushort uc = input.at(i).unicode(); if (uc < 0x100) { @@ -80,13 +44,13 @@ static QString escape(const QString &input) || (uc == 0x5F)) { output.append(QChar(uc)); } else { - output.append('%'); + output.append(u'%'); output.append(QLatin1Char(toHexUpper(uc >> 4))); output.append(QLatin1Char(toHexUpper(uc))); } } else { - output.append('%'); - output.append('u'); + output.append(u'%'); + output.append(u'u'); output.append(QLatin1Char(toHexUpper(uc >> 12))); output.append(QLatin1Char(toHexUpper(uc >> 8))); output.append(QLatin1Char(toHexUpper(uc >> 4))); @@ -99,14 +63,14 @@ static QString escape(const QString &input) static QString unescape(const QString &input) { QString result; - result.reserve(input.length()); + result.reserve(input.size()); int i = 0; - const int length = input.length(); + const int length = input.size(); while (i < length) { QChar c = input.at(i++); - if ((c == '%') && (i + 1 < length)) { + if ((c == u'%') && (i + 1 < length)) { QChar a = input.at(i); - if ((a == 'u') && (i + 4 < length)) { + if ((a == u'u') && (i + 4 < length)) { int d3 = fromHex(input.at(i+1).unicode()); int d2 = fromHex(input.at(i+2).unicode()); int d1 = fromHex(input.at(i+3).unicode()); @@ -122,7 +86,7 @@ static QString unescape(const QString &input) int d1 = fromHex(a.unicode()); int d0 = fromHex(input.at(i+1).unicode()); if ((d1 != -1) && (d0 != -1)) { - c = (d1 << 4) | d0; + c = QChar((d1 << 4) | d0); i += 2; } result.append(c); @@ -149,7 +113,7 @@ static QString encode(const QString &input, const char *unescapedSet, bool *ok) { *ok = true; QString output; - const int length = input.length(); + const int length = input.size(); int i = 0; while (i < length) { const QChar c = input.at(i); @@ -223,8 +187,8 @@ static QString decode(const QString &input, DecodeMode decodeMode, bool *ok) { *ok = true; QString output; - output.reserve(input.length()); - const int length = input.length(); + output.reserve(input.size()); + const int length = input.size(); int i = 0; const QChar percent = QLatin1Char('%'); while (i < length) { @@ -304,7 +268,7 @@ static QString decode(const QString &input, DecodeMode decodeMode, bool *ok) ++r; } if (*r) - output.append(input.midRef(start, i - start + 1)); + output.append(QStringView{input}.mid(start, i - start + 1)); else output.append(QChar(b)); } else { @@ -326,10 +290,10 @@ static QString decode(const QString &input, DecodeMode decodeMode, bool *ok) DEFINE_OBJECT_VTABLE(EvalFunction); -void Heap::EvalFunction::init(QV4::ExecutionContext *scope) +void Heap::EvalFunction::init(QV4::ExecutionEngine *engine) { - Scope s(scope); - Heap::FunctionObject::init(scope, s.engine->id_eval()); + Scope s(engine); + Heap::FunctionObject::init(engine, s.engine->id_eval()); ScopedFunctionObject f(s, this); f->defineReadonlyConfigurableProperty(s.engine->id_length(), Value::fromInt32(1)); } @@ -367,17 +331,17 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc, Function *function = script.function(); if (!function) return Encode::undefined(); - function->isEval = true; + function->kind = Function::Eval; if (function->isStrict() || isStrict) { ScopedFunctionObject e(scope, FunctionObject::createScriptFunction(ctx, function)); ScopedValue thisObject(scope, directCall ? scope.engine->currentStackFrame->thisObject() : scope.engine->globalObject->asReturnedValue()); - return e->call(thisObject, nullptr, 0); + return checkedResult(v4, e->call(thisObject, nullptr, 0)); } ScopedValue thisObject(scope, scope.engine->currentStackFrame->thisObject()); - return function->call(thisObject, nullptr, 0, ctx); + return checkedResult(v4, function->call(thisObject, nullptr, 0, ctx)); } @@ -417,7 +381,7 @@ ReturnedValue GlobalFunctions::method_parseInt(const FunctionObject *b, const Va CHECK_EXCEPTION(); const QChar *pos = trimmed.constData(); - const QChar *end = pos + trimmed.length(); + const QChar *end = pos + trimmed.size(); int sign = 1; // 3 if (pos != end) { diff --git a/src/qml/jsruntime/qv4globalobject_p.h b/src/qml/jsruntime/qv4globalobject_p.h index 021b445955..fd23d71332 100644 --- a/src/qml/jsruntime/qv4globalobject_p.h +++ b/src/qml/jsruntime/qv4globalobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4GLOBALOBJECT_H #define QV4GLOBALOBJECT_H @@ -50,7 +14,7 @@ // We mean it. // -#include "qv4global_p.h" +#include <QtQml/private/qqmlglobal_p.h> #include "qv4functionobject_p.h" QT_BEGIN_NAMESPACE @@ -60,7 +24,7 @@ namespace QV4 { namespace Heap { struct EvalFunction : FunctionObject { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; } diff --git a/src/qml/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp deleted file mode 100644 index c3d7165f71..0000000000 --- a/src/qml/jsruntime/qv4identifier.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "qv4identifier_p.h" -#include "qv4identifiertable_p.h" -#include "qv4string_p.h" -#include <private/qprimefornumbits_p.h> - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -IdentifierHashData::IdentifierHashData(IdentifierTable *table, int numBits) - : size(0) - , numBits(numBits) - , identifierTable(table) -{ - refCount.storeRelaxed(1); - alloc = qPrimeForNumBits(numBits); - entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); - memset(entries, 0, alloc*sizeof(IdentifierHashEntry)); - identifierTable->addIdentifierHash(this); -} - -IdentifierHashData::IdentifierHashData(IdentifierHashData *other) - : size(other->size) - , numBits(other->numBits) - , identifierTable(other->identifierTable) -{ - refCount.storeRelaxed(1); - alloc = other->alloc; - entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); - memcpy(entries, other->entries, alloc*sizeof(IdentifierHashEntry)); - identifierTable->addIdentifierHash(this); -} - -IdentifierHashData::~IdentifierHashData() { - free(entries); - if (identifierTable) - identifierTable->removeIdentifierHash(this); -} - -IdentifierHash::IdentifierHash(ExecutionEngine *engine) -{ - d = new IdentifierHashData(engine->identifierTable, 3); -} - -void IdentifierHash::detach() -{ - if (!d || d->refCount.loadAcquire() == 1) - return; - IdentifierHashData *newData = new IdentifierHashData(d); - if (d && !d->refCount.deref()) - delete d; - d = newData; -} - - -IdentifierHashEntry *IdentifierHash::addEntry(PropertyKey identifier) -{ - Q_ASSERT(identifier.isStringOrSymbol()); - - // fill up to max 50% - bool grow = (d->alloc <= d->size*2); - - if (grow) { - ++d->numBits; - int newAlloc = qPrimeForNumBits(d->numBits); - IdentifierHashEntry *newEntries = (IdentifierHashEntry *)malloc(newAlloc * sizeof(IdentifierHashEntry)); - memset(newEntries, 0, newAlloc*sizeof(IdentifierHashEntry)); - for (int i = 0; i < d->alloc; ++i) { - const IdentifierHashEntry &e = d->entries[i]; - if (!e.identifier.isValid()) - continue; - uint idx = e.identifier.id() % newAlloc; - while (newEntries[idx].identifier.isValid()) { - ++idx; - idx %= newAlloc; - } - newEntries[idx] = e; - } - free(d->entries); - d->entries = newEntries; - d->alloc = newAlloc; - } - - uint idx = identifier.id() % d->alloc; - while (d->entries[idx].identifier.isValid()) { - Q_ASSERT(d->entries[idx].identifier != identifier); - ++idx; - idx %= d->alloc; - } - d->entries[idx].identifier = identifier; - ++d->size; - return d->entries + idx; -} - -const IdentifierHashEntry *IdentifierHash::lookup(PropertyKey identifier) const -{ - if (!d || !identifier.isStringOrSymbol()) - return nullptr; - Q_ASSERT(d->entries); - - uint idx = identifier.id() % d->alloc; - while (1) { - if (!d->entries[idx].identifier.isValid()) - return nullptr; - if (d->entries[idx].identifier == identifier) - return d->entries + idx; - ++idx; - idx %= d->alloc; - } -} - -const IdentifierHashEntry *IdentifierHash::lookup(const QString &str) const -{ - if (!d) - return nullptr; - - PropertyKey id = d->identifierTable->asPropertyKey(str); - return lookup(id); -} - -const IdentifierHashEntry *IdentifierHash::lookup(String *str) const -{ - if (!d) - return nullptr; - PropertyKey id = d->identifierTable->asPropertyKey(str); - if (id.isValid()) - return lookup(id); - return lookup(str->toQString()); -} - -const PropertyKey IdentifierHash::toIdentifier(const QString &str) const -{ - Q_ASSERT(d); - return d->identifierTable->asPropertyKey(str); -} - -const PropertyKey IdentifierHash::toIdentifier(Heap::String *str) const -{ - Q_ASSERT(d); - return d->identifierTable->asPropertyKey(str); -} - -QString QV4::IdentifierHash::findId(int value) const -{ - IdentifierHashEntry *e = d->entries; - IdentifierHashEntry *end = e + d->alloc; - while (e < end) { - if (e->identifier.isValid() && e->value == value) - return e->identifier.toQString(); - ++e; - } - return QString(); -} - -void IdentifierHashData::markObjects(MarkStack *markStack) const -{ - IdentifierHashEntry *e = entries; - IdentifierHashEntry *end = e + alloc; - while (e < end) { - if (Heap::Base *o = e->identifier.asStringOrSymbol()) - o->mark(markStack); - ++e; - } -} - - -} - -QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4identifier_p.h b/src/qml/jsruntime/qv4identifier_p.h deleted file mode 100644 index 32de8b7c8d..0000000000 --- a/src/qml/jsruntime/qv4identifier_p.h +++ /dev/null @@ -1,173 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QV4IDENTIFIER_H -#define QV4IDENTIFIER_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 <qstring.h> -#include <private/qv4global_p.h> -#include <private/qv4propertykey_p.h> - -QT_BEGIN_NAMESPACE - -namespace QV4 { - -struct IdentifierHashEntry { - PropertyKey identifier; - int value; -}; - -struct IdentifierHashData -{ - IdentifierHashData(IdentifierTable *table, int numBits); - explicit IdentifierHashData(IdentifierHashData *other); - ~IdentifierHashData(); - void markObjects(MarkStack *markStack) const; - - QBasicAtomicInt refCount; - int alloc; - int size; - int numBits; - IdentifierTable *identifierTable; - IdentifierHashEntry *entries; -}; - -struct IdentifierHash -{ - - IdentifierHashData *d = nullptr; - - IdentifierHash() {} - IdentifierHash(ExecutionEngine *engine); - inline IdentifierHash(const IdentifierHash &other); - inline ~IdentifierHash(); - inline IdentifierHash &operator=(const IdentifierHash &other); - - bool isEmpty() const { return !d; } - - inline int count() const; - - void detach(); - - void add(const QString &str, int value); - void add(Heap::String *str, int value); - - inline int value(const QString &str) const; - inline int value(String *str) const; - QString findId(int value) const; - -protected: - IdentifierHashEntry *addEntry(PropertyKey i); - const IdentifierHashEntry *lookup(PropertyKey identifier) const; - const IdentifierHashEntry *lookup(const QString &str) const; - const IdentifierHashEntry *lookup(String *str) const; - const PropertyKey toIdentifier(const QString &str) const; - const PropertyKey toIdentifier(Heap::String *str) const; -}; - - -inline IdentifierHash::IdentifierHash(const IdentifierHash &other) -{ - d = other.d; - if (d) - d->refCount.ref(); -} - -inline IdentifierHash::~IdentifierHash() -{ - if (d && !d->refCount.deref()) - delete d; -} - -IdentifierHash &IdentifierHash::operator=(const IdentifierHash &other) -{ - if (other.d) - other.d->refCount.ref(); - if (d && !d->refCount.deref()) - delete d; - d = other.d; - return *this; -} - -inline int IdentifierHash::count() const -{ - return d ? d->size : 0; -} - -inline -void IdentifierHash::add(const QString &str, int value) -{ - IdentifierHashEntry *e = addEntry(toIdentifier(str)); - e->value = value; -} - -inline -void IdentifierHash::add(Heap::String *str, int value) -{ - IdentifierHashEntry *e = addEntry(toIdentifier(str)); - e->value = value; -} - -inline int IdentifierHash::value(const QString &str) const -{ - const IdentifierHashEntry *e = lookup(str); - return e ? e->value : -1; -} - -inline int IdentifierHash::value(String *str) const -{ - const IdentifierHashEntry *e = lookup(str); - return e ? e->value : -1; -} - -} - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/jsruntime/qv4identifierhash.cpp b/src/qml/jsruntime/qv4identifierhash.cpp new file mode 100644 index 0000000000..48df2283f0 --- /dev/null +++ b/src/qml/jsruntime/qv4identifierhash.cpp @@ -0,0 +1,190 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <private/qv4identifierhash_p.h> +#include <private/qv4identifiertable_p.h> +#include <private/qv4string_p.h> +#include <private/qv4identifierhashdata_p.h> +#include <private/qprimefornumbits_p.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +IdentifierHash::IdentifierHash(ExecutionEngine *engine) +{ + d = new IdentifierHashData(engine->identifierTable, 3); + Q_ASSERT(!isEmpty()); +} + +void IdentifierHash::detach() +{ + if (!d || d->refCount.loadAcquire() == 1) + return; + IdentifierHashData *newData = new IdentifierHashData(d); + if (d && !d->refCount.deref()) + delete d; + d = newData; +} + +inline +IdentifierHashEntry *IdentifierHash::addEntry(PropertyKey identifier) +{ + Q_ASSERT(identifier.isStringOrSymbol()); + + // fill up to max 50% + bool grow = (d->alloc <= d->size*2); + + if (grow) { + ++d->numBits; + int newAlloc = qPrimeForNumBits(d->numBits); + IdentifierHashEntry *newEntries = (IdentifierHashEntry *)malloc(newAlloc * sizeof(IdentifierHashEntry)); + memset(newEntries, 0, newAlloc*sizeof(IdentifierHashEntry)); + for (int i = 0; i < d->alloc; ++i) { + const IdentifierHashEntry &e = d->entries[i]; + if (!e.identifier.isValid()) + continue; + uint idx = e.identifier.id() % newAlloc; + while (newEntries[idx].identifier.isValid()) { + ++idx; + idx %= newAlloc; + } + newEntries[idx] = e; + } + free(d->entries); + d->entries = newEntries; + d->alloc = newAlloc; + } + + uint idx = identifier.id() % d->alloc; + while (d->entries[idx].identifier.isValid()) { + Q_ASSERT(d->entries[idx].identifier != identifier); + ++idx; + idx %= d->alloc; + } + d->entries[idx].identifier = identifier; + ++d->size; + return d->entries + idx; +} + +inline +const IdentifierHashEntry *IdentifierHash::lookup(PropertyKey identifier) const +{ + if (!d || !identifier.isStringOrSymbol()) + return nullptr; + Q_ASSERT(d->entries); + + uint idx = identifier.id() % d->alloc; + while (1) { + if (!d->entries[idx].identifier.isValid()) + return nullptr; + if (d->entries[idx].identifier == identifier) + return d->entries + idx; + ++idx; + idx %= d->alloc; + } +} + +inline +const IdentifierHashEntry *IdentifierHash::lookup(const QString &str) const +{ + if (!d) + return nullptr; + + PropertyKey id = d->identifierTable->asPropertyKey(str, IdentifierTable::ForceConversionToId); + return lookup(id); +} + +inline +const IdentifierHashEntry *IdentifierHash::lookup(String *str) const +{ + if (!d) + return nullptr; + PropertyKey id = d->identifierTable->asPropertyKey(str); + if (id.isValid()) + return lookup(id); + return lookup(str->toQString()); +} + +inline +const PropertyKey IdentifierHash::toIdentifier(const QString &str) const +{ + Q_ASSERT(d); + return d->identifierTable->asPropertyKey(str, IdentifierTable::ForceConversionToId); +} + +inline +const PropertyKey IdentifierHash::toIdentifier(Heap::String *str) const +{ + Q_ASSERT(d); + return d->identifierTable->asPropertyKey(str); +} + +QString QV4::IdentifierHash::findId(int value) const +{ + IdentifierHashEntry *e = d->entries; + IdentifierHashEntry *end = e + d->alloc; + while (e < end) { + if (e->identifier.isValid() && e->value == value) + return e->identifier.toQString(); + ++e; + } + return QString(); +} + +QV4::IdentifierHash::IdentifierHash(const IdentifierHash &other) +{ + d = other.d; + if (d) + d->refCount.ref(); +} + +QV4::IdentifierHash::~IdentifierHash() +{ + if (d && !d->refCount.deref()) + delete d; +} + +IdentifierHash &QV4::IdentifierHash::operator=(const IdentifierHash &other) +{ + if (other.d) + other.d->refCount.ref(); + if (d && !d->refCount.deref()) + delete d; + d = other.d; + return *this; +} + +int QV4::IdentifierHash::count() const +{ + return d ? d->size : 0; +} + +void QV4::IdentifierHash::add(const QString &str, int value) +{ + IdentifierHashEntry *e = addEntry(toIdentifier(str)); + e->value = value; +} + +void QV4::IdentifierHash::add(Heap::String *str, int value) +{ + IdentifierHashEntry *e = addEntry(toIdentifier(str)); + e->value = value; +} + +int QV4::IdentifierHash::value(const QString &str) const +{ + const IdentifierHashEntry *e = lookup(str); + return e ? e->value : -1; +} + +int QV4::IdentifierHash::value(String *str) const +{ + const IdentifierHashEntry *e = lookup(str); + return e ? e->value : -1; +} + + +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4identifierhash_p.h b/src/qml/jsruntime/qv4identifierhash_p.h new file mode 100644 index 0000000000..6c77a78f85 --- /dev/null +++ b/src/qml/jsruntime/qv4identifierhash_p.h @@ -0,0 +1,63 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#ifndef QV4IDENTIFIERHASH_P_H +#define QV4IDENTIFIERHASH_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 <qstring.h> +#include <private/qv4global_p.h> +#include <private/qv4propertykey_p.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct IdentifierHashEntry; +struct IdentifierHashData; +struct Q_QML_EXPORT IdentifierHash +{ + IdentifierHash() = default; + IdentifierHash(ExecutionEngine *engine); + IdentifierHash(const IdentifierHash &other); + ~IdentifierHash(); + IdentifierHash &operator=(const IdentifierHash &other); + + bool isEmpty() const { return !d; } + + int count() const; + + void detach(); + + void add(const QString &str, int value); + void add(Heap::String *str, int value); + + int value(const QString &str) const; + int value(String *str) const; + QString findId(int value) const; + +private: + inline IdentifierHashEntry *addEntry(PropertyKey i); + inline const IdentifierHashEntry *lookup(PropertyKey identifier) const; + inline const IdentifierHashEntry *lookup(const QString &str) const; + inline const IdentifierHashEntry *lookup(String *str) const; + inline const PropertyKey toIdentifier(const QString &str) const; + inline const PropertyKey toIdentifier(Heap::String *str) const; + + IdentifierHashData *d = nullptr; +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4_IDENTIFIERHASH_P_H diff --git a/src/qml/jsruntime/qv4identifierhashdata_p.h b/src/qml/jsruntime/qv4identifierhashdata_p.h new file mode 100644 index 0000000000..664e8e803d --- /dev/null +++ b/src/qml/jsruntime/qv4identifierhashdata_p.h @@ -0,0 +1,86 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#ifndef QV4IDENTIFIERHASHDATA_H +#define QV4IDENTIFIERHASHDATA_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qv4global_p.h> +#include <private/qv4propertykey_p.h> +#include <private/qv4identifiertable_p.h> +#include <QtCore/qatomic.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct IdentifierHashEntry { + PropertyKey identifier; + int value; +}; + +struct IdentifierHashData +{ + IdentifierHashData(IdentifierTable *table, int numBits) + : size(0) + , numBits(numBits) + , identifierTable(table) + { + refCount.storeRelaxed(1); + alloc = qPrimeForNumBits(numBits); + entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); + memset(entries, 0, alloc*sizeof(IdentifierHashEntry)); + identifierTable->addIdentifierHash(this); + } + + explicit IdentifierHashData(IdentifierHashData *other) + : size(other->size) + , numBits(other->numBits) + , identifierTable(other->identifierTable) + { + refCount.storeRelaxed(1); + alloc = other->alloc; + entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); + memcpy(entries, other->entries, alloc*sizeof(IdentifierHashEntry)); + identifierTable->addIdentifierHash(this); + } + + ~IdentifierHashData() { + free(entries); + if (identifierTable) + identifierTable->removeIdentifierHash(this); + } + + void markObjects(MarkStack *markStack) const + { + IdentifierHashEntry *e = entries; + IdentifierHashEntry *end = e + alloc; + while (e < end) { + if (Heap::Base *o = e->identifier.asStringOrSymbol()) + o->mark(markStack); + ++e; + } + } + + QBasicAtomicInt refCount; + int alloc; + int size; + int numBits; + IdentifierTable *identifierTable; + IdentifierHashEntry *entries; +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4IDENTIFIERHASHDATA_P_H diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp index 21b47c3909..4c915442f4 100644 --- a/src/qml/jsruntime/qv4identifiertable.cpp +++ b/src/qml/jsruntime/qv4identifiertable.cpp @@ -1,43 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4identifiertable_p.h" #include "qv4symbol_p.h" +#include <private/qv4identifierhashdata_p.h> #include <private/qprimefornumbits_p.h> QT_BEGIN_NAMESPACE @@ -60,7 +25,7 @@ IdentifierTable::~IdentifierTable() { free(entriesByHash); free(entriesById); - for (const auto &h : qAsConst(idHashes)) + for (const auto &h : std::as_const(idHashes)) h->identifierTable = nullptr; } @@ -71,7 +36,7 @@ void IdentifierTable::addEntry(Heap::StringOrSymbol *str) if (str->subtype == Heap::String::StringType_ArrayIndex) return; - str->identifier = PropertyKey::fromStringOrSymbol(str); + str->identifier = PropertyKey::fromStringOrSymbol(engine, str); bool grow = (alloc <= size*2); @@ -135,13 +100,19 @@ void IdentifierTable::addEntry(Heap::StringOrSymbol *str) Heap::String *IdentifierTable::insertString(const QString &s) { uint subtype; - uint hash = String::createHashValue(s.constData(), s.length(), &subtype); + uint hash = String::createHashValue(s.constData(), s.size(), &subtype); if (subtype == Heap::String::StringType_ArrayIndex) { Heap::String *str = engine->newString(s); str->stringHash = hash; str->subtype = subtype; + str->identifier = PropertyKey::fromArrayIndex(hash); return str; } + return resolveStringEntry(s, hash, subtype); +} + +Heap::String *IdentifierTable::resolveStringEntry(const QString &s, uint hash, uint subtype) +{ uint idx = hash % alloc; while (Heap::StringOrSymbol *e = entriesByHash[idx]) { if (e->stringHash == hash && e->toQString() == s) @@ -162,7 +133,7 @@ Heap::Symbol *IdentifierTable::insertSymbol(const QString &s) Q_ASSERT(s.at(0) == QLatin1Char('@')); uint subtype; - uint hash = String::createHashValue(s.constData(), s.length(), &subtype); + uint hash = String::createHashValue(s.constData(), s.size(), &subtype); uint idx = hash % alloc; while (Heap::StringOrSymbol *e = entriesByHash[idx]) { if (e->stringHash == hash && e->toQString() == s) @@ -194,6 +165,9 @@ PropertyKey IdentifierTable::asPropertyKeyImpl(const Heap::String *str) while (Heap::StringOrSymbol *e = entriesByHash[idx]) { if (e->stringHash == hash && e->toQString() == str->toQString()) { str->identifier = e->identifier; + QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack) { + e->identifier.asStringOrSymbol()->mark(stack); + }); return e->identifier; } ++idx; @@ -278,32 +252,18 @@ void IdentifierTable::sweep() size -= freed; } -PropertyKey IdentifierTable::asPropertyKey(const QString &s) -{ - return insertString(s)->identifier; -} - -PropertyKey IdentifierTable::asPropertyKey(const char *s, int len) +PropertyKey IdentifierTable::asPropertyKey(const QString &s, + IdentifierTable::KeyConversionBehavior conversionBehvior) { uint subtype; - uint hash = String::createHashValue(s, len, &subtype); - if (hash == UINT_MAX) - return asPropertyKey(QString::fromUtf8(s, len)); - - QLatin1String latin(s, len); - uint idx = hash % alloc; - while (Heap::StringOrSymbol *e = entriesByHash[idx]) { - if (e->stringHash == hash && e->toQString() == latin) - return e->identifier; - ++idx; - idx %= alloc; + uint hash = String::createHashValue(s.constData(), s.size(), &subtype); + if (subtype == Heap::String::StringType_ArrayIndex) { + if (Q_UNLIKELY(conversionBehvior == ForceConversionToId)) + hash = String::createHashValueDisallowingArrayIndex(s.constData(), s.size(), &subtype); + else + return PropertyKey::fromArrayIndex(hash); } - - Heap::String *str = engine->newString(QString::fromLatin1(s, len)); - str->stringHash = hash; - str->subtype = subtype; - addEntry(str); - return str->identifier; + return resolveStringEntry(s, hash, subtype)->identifier; } } diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h index 78e2b6620e..2ecd4a7294 100644 --- a/src/qml/jsruntime/qv4identifiertable_p.h +++ b/src/qml/jsruntime/qv4identifiertable_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4IDENTIFIERTABLE_H #define QV4IDENTIFIERTABLE_H @@ -50,7 +14,7 @@ // We mean it. // -#include "qv4identifier_p.h" +#include "qv4identifierhash_p.h" #include "qv4string_p.h" #include "qv4engine_p.h" #include <qset.h> @@ -60,7 +24,7 @@ QT_BEGIN_NAMESPACE namespace QV4 { -struct Q_QML_PRIVATE_EXPORT IdentifierTable +struct Q_QML_EXPORT IdentifierTable { ExecutionEngine *engine; @@ -91,8 +55,8 @@ public: return asPropertyKey(str->d()); } - PropertyKey asPropertyKey(const QString &s); - PropertyKey asPropertyKey(const char *s, int len); + enum KeyConversionBehavior { Default, ForceConversionToId }; + PropertyKey asPropertyKey(const QString &s, KeyConversionBehavior conversionBehavior = Default); PropertyKey asPropertyKeyImpl(const Heap::String *str); @@ -109,6 +73,9 @@ public: void removeIdentifierHash(IdentifierHashData *h) { idHashes.remove(h); } + +private: + Heap::String *resolveStringEntry(const QString &s, uint hash, uint subtype); }; } diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp index 92face6f94..76545ba692 100644 --- a/src/qml/jsruntime/qv4include.cpp +++ b/src/qml/jsruntime/qv4include.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4include_p.h" #include "qv4scopedvalue_p.h" @@ -59,20 +23,21 @@ QT_BEGIN_NAMESPACE QV4Include::QV4Include(const QUrl &url, QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &callback) - : v4(engine), m_url(url) + : QObject(engine->jsEngine()) + , v4(engine), m_url(url) #if QT_CONFIG(qml_network) - , m_redirectCount(0), m_network(nullptr) , m_reply(nullptr) + , m_network(nullptr) , m_reply(nullptr) #endif { if (qmlContext) - m_qmlContext.set(engine, *qmlContext); + m_qmlContext.set(v4, *qmlContext); if (callback.as<QV4::FunctionObject>()) - m_callbackFunction.set(engine, callback); + m_callbackFunction.set(v4, callback); m_resultObject.set(v4, resultValue(v4)); #if QT_CONFIG(qml_network) - if (QQmlEngine *qmlEngine = engine->qmlEngine()) { + if (QQmlEngine *qmlEngine = v4->qmlEngine()) { m_network = qmlEngine->networkAccessManager(); QNetworkRequest request; @@ -126,9 +91,9 @@ void QV4Include::callback(const QV4::Value &callback, const QV4::Value &status) if (!f) return; - QV4::JSCallData jsCallData(scope, 1); - *jsCallData->thisObject = v4->globalObject->asReturnedValue(); - jsCallData->args[0] = status; + QV4::JSCallArguments jsCallData(scope, 1); + *jsCallData.thisObject = v4->globalObject->asReturnedValue(); + jsCallData.args[0] = status; f->call(jsCallData); if (scope.hasException()) scope.engine->catchException(); @@ -139,27 +104,9 @@ QV4::ReturnedValue QV4Include::result() return m_resultObject.value(); } -#define INCLUDE_MAXIMUM_REDIRECT_RECURSION 15 void QV4Include::finished() { #if QT_CONFIG(qml_network) - 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; - } - } - QV4::Scope scope(v4); QV4::ScopedObject resultObj(scope, m_resultObject.value()); QV4::ScopedString status(scope, v4->newString(QStringLiteral("status"))); @@ -172,9 +119,9 @@ void QV4Include::finished() QV4::Script script(v4, qml, /*parse as QML binding*/false, code, m_url.toString()); script.parse(); - if (!scope.engine->hasException) + if (!scope.hasException()) script.run(); - if (scope.engine->hasException) { + if (scope.hasException()) { QV4::ScopedValue ex(scope, scope.engine->catchException()); resultObj->put(status, QV4::ScopedValue(scope, QV4::Value::fromInt32(Exception))); QV4::ScopedString exception(scope, v4->newString(QStringLiteral("exception"))); @@ -202,37 +149,40 @@ void QV4Include::finished() /* Documented in qv4engine.cpp */ -QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *argv, int argc) +QJSValue QV4Include::method_include(QV4::ExecutionEngine *engine, const QUrl &url, + const QJSValue &callbackFunction) { - QV4::Scope scope(b); - if (!argc) - RETURN_UNDEFINED(); + QQmlRefPointer<QQmlContextData> context = engine->callingQmlContext(); - QQmlContextData *context = scope.engine->callingQmlContext(); - - if ((!context || !context->isJSContext) && scope.engine->qmlEngine()) - RETURN_RESULT(scope.engine->throwError(QString::fromUtf8("Qt.include(): Can only be called from JavaScript files"))); + if ((!context || !context->isJSContext()) && engine->qmlEngine()) { + return QJSValuePrivate::fromReturnedValue( + engine->throwError( + QString::fromUtf8( + "Qt.include(): Can only be called from JavaScript files"))); + } - QV4::ScopedValue callbackFunction(scope, QV4::Value::undefinedValue()); - if (argc >= 2 && argv[1].as<QV4::FunctionObject>()) - callbackFunction = argv[1]; - QUrl url(scope.engine->resolvedUrl(argv[0].toQStringNoThrow())); - if (scope.engine->qmlEngine() && scope.engine->qmlEngine()->urlInterceptor()) - url = scope.engine->qmlEngine()->urlInterceptor()->intercept(url, QQmlAbstractUrlInterceptor::JavaScriptFile); + QV4::Scope scope(engine); + QV4::ScopedValue scopedCallbackFunction(scope, QV4::Value::undefinedValue()); + if (auto function = QJSValuePrivate::asManagedType<QV4::FunctionObject>(&callbackFunction)) + scopedCallbackFunction = *function; - QString localFile = QQmlFile::urlToLocalFileOrQrc(url); + const QQmlEngine *qmlEngine = engine->qmlEngine(); + const QUrl intercepted = qmlEngine + ? qmlEngine->interceptUrl(url, QQmlAbstractUrlInterceptor::JavaScriptFile) + : url; + QString localFile = QQmlFile::urlToLocalFileOrQrc(intercepted); QV4::ScopedValue result(scope); QV4::Scoped<QV4::QmlContext> qmlcontext(scope, scope.engine->qmlContext()); if (localFile.isEmpty()) { #if QT_CONFIG(qml_network) - QV4Include *i = new QV4Include(url, scope.engine, qmlcontext, callbackFunction); + QV4Include *i = new QV4Include(url, engine, qmlcontext, scopedCallbackFunction); result = i->result(); #else result = resultValue(scope.engine, NetworkError); - callback(callbackFunction, result); + callback(scopedCallbackFunction, result); #endif } else { QScopedPointer<QV4::Script> script; @@ -241,9 +191,9 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons if (!script.isNull()) { script->parse(); - if (!scope.engine->hasException) + if (!scope.hasException()) script->run(); - if (scope.engine->hasException) { + if (scope.hasException()) { QV4::ScopedValue ex(scope, scope.engine->catchException()); result = resultValue(scope.engine, Exception); QV4::ScopedString exception(scope, scope.engine->newString(QStringLiteral("exception"))); @@ -255,10 +205,10 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons result = resultValue(scope.engine, NetworkError, error); } - callback(callbackFunction, result); + callback(scopedCallbackFunction, result); } - return result->asReturnedValue(); + return QJSValuePrivate::fromReturnedValue(result->asReturnedValue()); } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4include_p.h b/src/qml/jsruntime/qv4include_p.h index 70ccfbf223..c6ac98c761 100644 --- a/src/qml/jsruntime/qv4include_p.h +++ b/src/qml/jsruntime/qv4include_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4INCLUDE_P_H #define QV4INCLUDE_P_H @@ -53,15 +17,16 @@ #include <QtCore/qobject.h> #include <QtCore/qurl.h> - -#include <private/qqmlcontext_p.h> +#include <QtCore/qpointer.h> #include <private/qv4value_p.h> #include <private/qv4context_p.h> +#include <private/qv4persistent_p.h> QT_BEGIN_NAMESPACE -class QQmlEngine; +class QJSEngine; +class QJSValue; #if QT_CONFIG(qml_network) class QNetworkAccessManager; #endif @@ -77,13 +42,15 @@ public: Exception = 3 }; - static QV4::ReturnedValue method_include(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); + static QJSValue method_include(QV4::ExecutionEngine *engine, const QUrl &url, + const QJSValue &callbackFunction); private Q_SLOTS: void finished(); private: - QV4Include(const QUrl &url, QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &callback); + QV4Include(const QUrl &url, QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, + const QV4::Value &callback); ~QV4Include(); QV4::ReturnedValue result(); @@ -96,7 +63,6 @@ private: QUrl m_url; #if QT_CONFIG(qml_network) - int m_redirectCount; QNetworkAccessManager *m_network; QPointer<QNetworkReply> m_reply; #endif diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 70849775cb..228a6bcd36 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -1,48 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <qv4internalclass_p.h> #include <qv4string_p.h> #include <qv4engine_p.h> -#include <qv4identifier_p.h> +#include <qv4identifierhash_p.h> #include "qv4object_p.h" -#include "qv4identifiertable_p.h" #include "qv4value_p.h" #include "qv4mm_p.h" #include <private/qprimefornumbits_p.h> @@ -78,34 +41,6 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize) ++d->size; } -int PropertyHash::removeIdentifier(PropertyKey identifier, int classSize) -{ - int val = -1; - PropertyHashData *dd = new PropertyHashData(d->numBits); - for (int i = 0; i < d->alloc; ++i) { - const Entry &e = d->entries[i]; - if (!e.identifier.isValid() || e.index >= static_cast<unsigned>(classSize)) - continue; - if (e.identifier == identifier) { - val = e.index; - continue; - } - uint idx = e.identifier.id() % dd->alloc; - while (dd->entries[idx].identifier.isValid()) { - ++idx; - idx %= dd->alloc; - } - dd->entries[idx] = e; - } - dd->size = classSize; - if (!--d->refCount) - delete d; - d = dd; - - Q_ASSERT(val != -1); - return val; -} - void PropertyHash::detach(bool grow, int classSize) { if (d->refCount == 1 && !grow) @@ -132,12 +67,11 @@ void PropertyHash::detach(bool grow, int classSize) SharedInternalClassDataPrivate<PropertyKey>::SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyKey> &other) : refcount(1), - engine(other.engine), - data(nullptr) + engine(other.engine) { if (other.alloc()) { const uint s = other.size(); - data = MemberData::allocate(engine, other.alloc(), other.data); + data.set(engine, MemberData::allocate(engine, other.alloc(), other.data)); setSize(s); } } @@ -147,7 +81,7 @@ SharedInternalClassDataPrivate<PropertyKey>::SharedInternalClassDataPrivate(cons : refcount(1), engine(other.engine) { - data = MemberData::allocate(engine, other.alloc(), nullptr); + data.set(engine, MemberData::allocate(engine, other.alloc(), nullptr)); memcpy(data, other.data, sizeof(Heap::MemberData) - sizeof(Value) + pos*sizeof(Value)); data->values.size = pos + 1; data->values.set(engine, pos, Value::fromReturnedValue(value.id())); @@ -157,7 +91,7 @@ void SharedInternalClassDataPrivate<PropertyKey>::grow() { const uint a = alloc() * 2; const uint s = size(); - data = MemberData::allocate(engine, a, data); + data.set(engine, MemberData::allocate(engine, a, data)); setSize(s); Q_ASSERT(alloc() >= a); } @@ -178,7 +112,7 @@ void SharedInternalClassDataPrivate<PropertyKey>::setSize(uint s) data->values.size = s; } -PropertyKey SharedInternalClassDataPrivate<PropertyKey>::at(uint i) +PropertyKey SharedInternalClassDataPrivate<PropertyKey>::at(uint i) const { Q_ASSERT(data && i < size()); return PropertyKey::fromId(data->values.values[i].rawValue()); @@ -187,6 +121,11 @@ PropertyKey SharedInternalClassDataPrivate<PropertyKey>::at(uint i) void SharedInternalClassDataPrivate<PropertyKey>::set(uint i, PropertyKey t) { Q_ASSERT(data && i < size()); + QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack) { + if constexpr (QV4::WriteBarrier::isInsertionBarrier) + if (auto string = t.asStringOrSymbol()) + string->mark(stack); + }); data->values.values[i].rawValueRef() = t.id(); } @@ -205,11 +144,20 @@ SharedInternalClassDataPrivate<PropertyAttributes>::SharedInternalClassDataPriva m_engine(other.m_engine) { Q_ASSERT(m_size <= m_alloc); + Q_ASSERT(m_alloc > 0); + m_engine->memoryManager->changeUnmanagedHeapSizeUsage(m_alloc * sizeof(PropertyAttributes)); - data = new PropertyAttributes[m_alloc]; - if (other.data) - memcpy(data, other.data, (m_size - 1) * sizeof(PropertyAttributes)); - data[pos] = value; + const PropertyAttributes *source = other.m_alloc > NumAttributesInPointer + ? other.m_data + : other.m_inlineData; + PropertyAttributes *target; + if (m_alloc > NumAttributesInPointer) + m_data = target = new PropertyAttributes[m_alloc]; + else + target = m_inlineData; + + memcpy(target, source, (m_size - 1) * sizeof(PropertyAttributes)); + target[pos] = value; } SharedInternalClassDataPrivate<PropertyAttributes>::SharedInternalClassDataPrivate( @@ -219,12 +167,14 @@ SharedInternalClassDataPrivate<PropertyAttributes>::SharedInternalClassDataPriva m_size(other.m_size), m_engine(other.m_engine) { - if (m_alloc) { - m_engine->memoryManager->changeUnmanagedHeapSizeUsage(m_alloc * sizeof(PropertyAttributes)); - data = new PropertyAttributes[m_alloc]; - memcpy(data, other.data, m_size*sizeof(PropertyAttributes)); + m_engine->memoryManager->changeUnmanagedHeapSizeUsage(m_alloc * sizeof(PropertyAttributes)); + if (m_alloc > NumAttributesInPointer) { + m_data = new PropertyAttributes[m_alloc]; + memcpy(m_data, other.m_data, m_size*sizeof(PropertyAttributes)); + } else if (m_alloc > 0) { + memcpy(m_inlineData, other.m_inlineData, m_alloc * sizeof(PropertyAttributes)); } else { - data = nullptr; + m_data = nullptr; } } @@ -232,13 +182,14 @@ SharedInternalClassDataPrivate<PropertyAttributes>::~SharedInternalClassDataPriv { m_engine->memoryManager->changeUnmanagedHeapSizeUsage( -qptrdiff(m_alloc * sizeof(PropertyAttributes))); - delete [] data; + if (m_alloc > NumAttributesInPointer) + delete [] m_data; } void SharedInternalClassDataPrivate<PropertyAttributes>::grow() { uint alloc; if (!m_alloc) { - alloc = 8; + alloc = NumAttributesInPointer; m_engine->memoryManager->changeUnmanagedHeapSizeUsage(alloc * sizeof(PropertyAttributes)); } else { // yes, signed. We don't want to deal with stuff > 2G @@ -252,12 +203,16 @@ void SharedInternalClassDataPrivate<PropertyAttributes>::grow() { (alloc - m_alloc) * sizeof(PropertyAttributes)); } - auto *n = new PropertyAttributes[alloc]; - if (data) { - memcpy(n, data, m_alloc*sizeof(PropertyAttributes)); - delete [] data; + if (alloc > NumAttributesInPointer) { + auto *n = new PropertyAttributes[alloc]; + if (m_alloc > NumAttributesInPointer) { + memcpy(n, m_data, m_alloc * sizeof(PropertyAttributes)); + delete [] m_data; + } else if (m_alloc > 0) { + memcpy(n, m_inlineData, m_alloc * sizeof(PropertyAttributes)); + } + m_data = n; } - data = n; m_alloc = alloc; } @@ -265,21 +220,21 @@ namespace Heap { void InternalClass::init(ExecutionEngine *engine) { +// InternalClass is automatically zeroed during allocation: +// prototype = nullptr; +// parent = nullptr; +// size = 0; +// numRedundantTransitions = 0; +// flags = 0; + Base::init(); new (&propertyTable) PropertyHash(); new (&nameMap) SharedInternalClassData<PropertyKey>(engine); new (&propertyData) SharedInternalClassData<PropertyAttributes>(engine); - new (&transitions) std::vector<Transition>(); + new (&transitions) QVarLengthArray<Transition, 1>(); this->engine = engine; vtable = QV4::InternalClass::staticVTable(); -// prototype = nullptr; -// parent = nullptr; -// size = 0; - extensible = true; - isFrozen = false; - isSealed = false; - isUsedAsProto = false; protoId = engine->newProtoId(); // Also internal classes need an internal class pointer. Simply make it point to itself @@ -293,20 +248,23 @@ void InternalClass::init(Heap::InternalClass *other) new (&propertyTable) PropertyHash(other->propertyTable); new (&nameMap) SharedInternalClassData<PropertyKey>(other->nameMap); new (&propertyData) SharedInternalClassData<PropertyAttributes>(other->propertyData); - new (&transitions) std::vector<Transition>(); + new (&transitions) QVarLengthArray<Transition, 1>(); engine = other->engine; vtable = other->vtable; prototype = other->prototype; parent = other; size = other->size; - extensible = other->extensible; - isSealed = other->isSealed; - isFrozen = other->isFrozen; - isUsedAsProto = other->isUsedAsProto; + numRedundantTransitions = other->numRedundantTransitions; + flags = other->flags; protoId = engine->newProtoId(); internalClass.set(engine, other->internalClass); + QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack) { + if constexpr (QV4::WriteBarrier::isInsertionBarrier) { + other->mark(stack); + } + }); } void InternalClass::destroy() @@ -326,14 +284,20 @@ void InternalClass::destroy() propertyTable.~PropertyHash(); nameMap.~SharedInternalClassData<PropertyKey>(); propertyData.~SharedInternalClassData<PropertyAttributes>(); - transitions.~vector<Transition>(); + transitions.~QVarLengthArray<Transition, 1>(); engine = nullptr; Base::destroy(); } -QString InternalClass::keyAt(uint index) const +ReturnedValue InternalClass::keyAt(uint index) const { - return nameMap.at(index).toQString(); + PropertyKey key = nameMap.at(index); + if (!key.isValid()) + return Encode::undefined(); + if (key.isArrayIndex()) + return Encode(key.asArrayIndex()); + Q_ASSERT(key.isStringOrSymbol()); + return key.asStringOrSymbol()->asReturnedValue(); } void InternalClass::changeMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, InternalClassEntry *entry) @@ -347,7 +311,7 @@ void InternalClass::changeMember(QV4::Object *object, PropertyKey id, PropertyAt InternalClassTransition &InternalClass::lookupOrInsertTransition(const InternalClassTransition &t) { - std::vector<Transition>::iterator it = std::lower_bound(transitions.begin(), transitions.end(), t); + QVarLengthArray<Transition, 1>::iterator it = std::lower_bound(transitions.begin(), transitions.end(), t); if (it != transitions.end() && *it == t) { return *it; } else { @@ -365,7 +329,102 @@ static void addDummyEntry(InternalClass *newClass, PropertyHash::Entry e) ++newClass->size; } -Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry) +static PropertyAttributes attributesFromFlags(int flags) +{ + PropertyAttributes attributes; + attributes.m_all = uchar(flags); + return attributes; +} + +static Heap::InternalClass *cleanInternalClass(Heap::InternalClass *orig) +{ + if (++orig->numRedundantTransitions < Heap::InternalClass::MaxRedundantTransitions) + return orig; + + // We will generally add quite a few transitions here. We have 255 redundant ones. + // We can expect at least as many significant ones in addition. + QVarLengthArray<InternalClassTransition, 1> transitions; + + Scope scope(orig->engine); + Scoped<QV4::InternalClass> child(scope, orig); + + { + quint8 remainingRedundantTransitions = orig->numRedundantTransitions; + QSet<PropertyKey> properties; + int structureChanges = 0; + + Scoped<QV4::InternalClass> parent(scope, orig->parent); + while (parent && remainingRedundantTransitions > 0) { + Q_ASSERT(child->d() != scope.engine->classes[ExecutionEngine::Class_Empty]); + const auto it = std::find_if( + parent->d()->transitions.begin(), parent->d()->transitions.end(), + [&child](const InternalClassTransition &t) { + return child->d() == t.lookup; + }); + Q_ASSERT(it != parent->d()->transitions.end()); + + if (it->flags & InternalClassTransition::StructureChange) { + // A structural change. Each kind of structural change has to be recorded only once. + if ((structureChanges & it->flags) != it->flags) { + transitions.push_back(*it); + structureChanges |= it->flags; + } else { + --remainingRedundantTransitions; + } + } else if (!properties.contains(it->id)) { + // We only need the final state of the property. + properties.insert(it->id); + + // Property removal creates _two_ redundant transitions. + // We don't have to replay either, but numRedundantTransitions only records one. + if (it->flags != 0) + transitions.push_back(*it); + } else { + --remainingRedundantTransitions; + } + + child = parent->d(); + parent = child->d()->parent; + Q_ASSERT(child->d() != parent->d()); + } + } + + for (auto it = transitions.rbegin(); it != transitions.rend(); ++it) { + switch (it->flags) { + case InternalClassTransition::NotExtensible: + child = child->d()->nonExtensible(); + continue; + case InternalClassTransition::VTableChange: + child = child->d()->changeVTable(it->vtable); + continue; + case InternalClassTransition::PrototypeChange: + child = child->d()->changePrototype(it->prototype); + continue; + case InternalClassTransition::ProtoClass: + child = child->d()->asProtoClass(); + continue; + case InternalClassTransition::Sealed: + child = child->d()->sealed(); + continue; + case InternalClassTransition::Frozen: + child = child->d()->frozen(); + continue; + case InternalClassTransition::Locked: + child = child->d()->locked(); + continue; + default: + Q_ASSERT(it->flags != 0); + Q_ASSERT(it->flags < InternalClassTransition::StructureChange); + child = child->addMember(it->id, attributesFromFlags(it->flags)); + continue; + } + } + + return child->d(); +} + +Heap::InternalClass *InternalClass::changeMember( + PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry) { if (!data.isEmpty()) data.resolve(); @@ -381,7 +440,7 @@ Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, Propert } if (data == propertyData.at(idx)) - return static_cast<Heap::InternalClass *>(this); + return this; Transition temp = { { identifier }, nullptr, int(data.all()) }; Transition &t = lookupOrInsertTransition(temp); @@ -394,7 +453,8 @@ Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, Propert Q_ASSERT(!propertyData.at(idx).isAccessor()); // add a dummy entry for the accessor - entry->setterIndex = newClass->size; + if (entry) + entry->setterIndex = newClass->size; e->setterIndex = newClass->size; addDummyEntry(newClass, *e); } @@ -403,7 +463,8 @@ Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, Propert t.lookup = newClass; Q_ASSERT(t.lookup); - return newClass; + + return cleanInternalClass(newClass); } Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) @@ -413,7 +474,7 @@ Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) if (proto) proto->setUsedAsProto(); Q_ASSERT(prototype != proto); - Q_ASSERT(!proto || proto->internalClass->isUsedAsProto); + Q_ASSERT(!proto || proto->internalClass->isUsedAsProto()); Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::PrototypeChange }; temp.prototype = proto; @@ -424,11 +485,14 @@ Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) // create a new class and add it to the tree Heap::InternalClass *newClass = engine->newClass(this); + QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack) { + if (proto && QV4::WriteBarrier::isInsertionBarrier) + proto->mark(stack); + }); newClass->prototype = proto; t.lookup = newClass; - - return newClass; + return prototype ? cleanInternalClass(newClass) : newClass; } Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt) @@ -449,12 +513,14 @@ Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt) t.lookup = newClass; Q_ASSERT(t.lookup); Q_ASSERT(newClass->vtable); - return newClass; + return vtable == QV4::InternalClass::staticVTable() + ? newClass + : cleanInternalClass(newClass); } Heap::InternalClass *InternalClass::nonExtensible() { - if (!extensible) + if (!isExtensible()) return this; Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::NotExtensible}; @@ -463,7 +529,25 @@ Heap::InternalClass *InternalClass::nonExtensible() return t.lookup; Heap::InternalClass *newClass = engine->newClass(this); - newClass->extensible = false; + newClass->flags |= NotExtensible; + + t.lookup = newClass; + Q_ASSERT(t.lookup); + return newClass; +} + +InternalClass *InternalClass::locked() +{ + if (isLocked()) + return this; + + Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::Locked}; + Transition &t = lookupOrInsertTransition(temp); + if (t.lookup) + return t.lookup; + + Heap::InternalClass *newClass = engine->newClass(this); + newClass->flags |= Locked; t.lookup = newClass; Q_ASSERT(t.lookup); @@ -500,7 +584,7 @@ Heap::InternalClass *InternalClass::addMember(PropertyKey identifier, PropertyAt Heap::InternalClass *InternalClass::addMemberImpl(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry) { - Transition temp = { { identifier }, nullptr, (int)data.flags() }; + Transition temp = { { identifier }, nullptr, int(data.all()) }; Transition &t = lookupOrInsertTransition(temp); if (entry) { @@ -553,21 +637,23 @@ void InternalClass::removeMember(QV4::Object *object, PropertyKey identifier) changeMember(object, identifier, Attr_Invalid); #ifndef QT_NO_DEBUG - // we didn't remove the data slot, just made it inaccessible - Q_ASSERT(object->internalClass()->size == oldClass->size); + // We didn't remove the data slot, just made it inaccessible. + // ... unless we've rebuilt the whole class. Then all the deleted properties are gone. + Q_ASSERT(object->internalClass()->numRedundantTransitions == 0 + || object->internalClass()->size == oldClass->size); #endif } Heap::InternalClass *InternalClass::sealed() { - if (isSealed) + if (isSealed()) return this; Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Sealed }; Transition &t = lookupOrInsertTransition(temp); if (t.lookup) { - Q_ASSERT(t.lookup && t.lookup->isSealed); + Q_ASSERT(t.lookup && t.lookup->isSealed()); return t.lookup; } @@ -575,7 +661,7 @@ Heap::InternalClass *InternalClass::sealed() Scoped<QV4::InternalClass> ic(scope, engine->newClass(this)); Heap::InternalClass *s = ic->d(); - if (!isFrozen) { // freezing also makes all properties non-configurable + if (!isFrozen()) { // freezing also makes all properties non-configurable for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); if (attrs.isEmpty()) @@ -584,7 +670,7 @@ Heap::InternalClass *InternalClass::sealed() s->propertyData.set(i, attrs); } } - s->isSealed = true; + s->flags |= Sealed; t.lookup = s; return s; @@ -592,14 +678,14 @@ Heap::InternalClass *InternalClass::sealed() Heap::InternalClass *InternalClass::frozen() { - if (isFrozen) + if (isFrozen()) return this; Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Frozen }; Transition &t = lookupOrInsertTransition(temp); if (t.lookup) { - Q_ASSERT(t.lookup && t.lookup->isFrozen); + Q_ASSERT(t.lookup && t.lookup->isFrozen()); return t.lookup; } @@ -616,7 +702,7 @@ Heap::InternalClass *InternalClass::frozen() attrs.setConfigurable(false); f->propertyData.set(i, attrs); } - f->isFrozen = true; + f->flags |= Frozen; t.lookup = f; return f; @@ -640,7 +726,7 @@ InternalClass *InternalClass::cryopreserved() bool InternalClass::isImplicitlyFrozen() const { - if (isFrozen) + if (isFrozen()) return true; for (uint i = 0; i < size; ++i) { @@ -656,7 +742,7 @@ bool InternalClass::isImplicitlyFrozen() const Heap::InternalClass *InternalClass::asProtoClass() { - if (isUsedAsProto) + if (isUsedAsProto()) return this; Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::ProtoClass }; @@ -665,7 +751,7 @@ Heap::InternalClass *InternalClass::asProtoClass() return t.lookup; Heap::InternalClass *newClass = engine->newClass(this); - newClass->isUsedAsProto = true; + newClass->flags |= UsedAsProto; t.lookup = newClass; Q_ASSERT(t.lookup); @@ -685,7 +771,7 @@ static void updateProtoUsage(Heap::Object *o, Heap::InternalClass *ic) void InternalClass::updateProtoUsage(Heap::Object *o) { - Q_ASSERT(isUsedAsProto); + Q_ASSERT(isUsedAsProto()); Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_Empty); Q_ASSERT(!ic->prototype); @@ -698,6 +784,9 @@ void InternalClass::markObjects(Heap::Base *b, MarkStack *stack) if (ic->prototype) ic->prototype->mark(stack); + if (ic->parent) + ic->parent->mark(stack); + ic->nameMap.mark(stack); } diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index 403702ae55..56ce787859 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4INTERNALCLASS_H #define QV4INTERNALCLASS_H @@ -53,6 +17,8 @@ #include "qv4global_p.h" #include <QHash> +#include <QVarLengthArray> +#include <climits> // for UINT_MAX #include <private/qv4propertykey_p.h> #include <private/qv4heap_p.h> @@ -88,7 +54,6 @@ struct PropertyHash void addEntry(const Entry &entry, int classSize); Entry *lookup(PropertyKey identifier) const; - int removeIdentifier(PropertyKey identifier, int classSize); void detach(bool grow, int classSize); }; @@ -158,7 +123,7 @@ struct SharedInternalClassDataPrivate<PropertyAttributes> { : refcount(1), m_alloc(0), m_size(0), - data(nullptr), + m_data(nullptr), m_engine(engine) { } SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyAttributes> &other); @@ -168,12 +133,14 @@ struct SharedInternalClassDataPrivate<PropertyAttributes> { void grow(); + void markIfNecessary(const PropertyAttributes &) {} + uint alloc() const { return m_alloc; } uint size() const { return m_size; } void setSize(uint s) { m_size = s; } - PropertyAttributes at(uint i) { Q_ASSERT(data && i < m_alloc); return data[i]; } - void set(uint i, PropertyAttributes t) { Q_ASSERT(data && i < m_alloc); data[i] = t; } + PropertyAttributes at(uint i) const { Q_ASSERT(i < m_alloc); return data(i); } + void set(uint i, PropertyAttributes t) { Q_ASSERT(i < m_alloc); setData(i, t); } void mark(MarkStack *) {} @@ -181,23 +148,49 @@ struct SharedInternalClassDataPrivate<PropertyAttributes> { private: uint m_alloc; uint m_size; - PropertyAttributes *data; + + enum { + SizeOfAttributesPointer = sizeof(PropertyAttributes *), + SizeOfAttributes = sizeof(PropertyAttributes), + NumAttributesInPointer = SizeOfAttributesPointer / SizeOfAttributes, + }; + + static_assert(NumAttributesInPointer > 0); + + PropertyAttributes data(uint i) const { + return m_alloc > NumAttributesInPointer ? m_data[i] : m_inlineData[i]; + } + + void setData(uint i, PropertyAttributes t) { + if (m_alloc > NumAttributesInPointer) + m_data[i] = t; + else + m_inlineData[i] = t; + } + + union { + PropertyAttributes *m_data; + PropertyAttributes m_inlineData[NumAttributesInPointer]; + }; ExecutionEngine *m_engine; }; template<> struct SharedInternalClassDataPrivate<PropertyKey> { - SharedInternalClassDataPrivate(ExecutionEngine *e) : refcount(1), engine(e), data(nullptr) {} + SharedInternalClassDataPrivate(ExecutionEngine *e) : refcount(1), engine(e) {} SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other); SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other, uint pos, PropertyKey value); ~SharedInternalClassDataPrivate() {} + template<typename StringOrSymbol = Heap::StringOrSymbol> + void markIfNecessary(const PropertyKey &value); + void grow(); uint alloc() const; uint size() const; void setSize(uint s); - PropertyKey at(uint i); + PropertyKey at(uint i) const; void set(uint i, PropertyKey t); void mark(MarkStack *s); @@ -205,9 +198,20 @@ struct SharedInternalClassDataPrivate<PropertyKey> { int refcount = 1; private: ExecutionEngine *engine; - Heap::MemberData *data; + WriteBarrier::Pointer<Heap::MemberData> data; }; +template<typename StringOrSymbol> +void QV4::SharedInternalClassDataPrivate<PropertyKey>::markIfNecessary(const PropertyKey &value) +{ + QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack) { + if constexpr (QV4::WriteBarrier::isInsertionBarrier) { + if (auto s = value.asStringOrSymbol<StringOrSymbol>()) + s->mark(stack); + } + }); +} + template <typename T> struct SharedInternalClassData { using Private = SharedInternalClassDataPrivate<T>; @@ -235,12 +239,12 @@ struct SharedInternalClassData { } void add(uint pos, T value) { + d->markIfNecessary(value); if (pos < d->size()) { Q_ASSERT(d->refcount > 1); // need to detach Private *dd = new Private(*d, pos, value); - if (!--d->refcount) - delete d; + --d->refcount; d = dd; return; } @@ -257,11 +261,11 @@ struct SharedInternalClassData { void set(uint pos, T value) { Q_ASSERT(pos < d->size()); + d->markIfNecessary(value); if (d->refcount > 1) { // need to detach Private *dd = new Private(*d); - if (!--d->refcount) - delete d; + --d->refcount; d = dd; } d->set(pos, value); @@ -290,24 +294,35 @@ struct InternalClassTransition int flags; enum { // range 0-0xff is reserved for attribute changes - NotExtensible = 0x100, - VTableChange = 0x200, - PrototypeChange = 0x201, - ProtoClass = 0x202, - Sealed = 0x203, - Frozen = 0x204 + StructureChange = 0x100, + NotExtensible = StructureChange | (1 << 0), + VTableChange = StructureChange | (1 << 1), + PrototypeChange = StructureChange | (1 << 2), + ProtoClass = StructureChange | (1 << 3), + Sealed = StructureChange | (1 << 4), + Frozen = StructureChange | (1 << 5), + Locked = StructureChange | (1 << 6), }; bool operator==(const InternalClassTransition &other) const { return id == other.id && flags == other.flags; } bool operator<(const InternalClassTransition &other) const - { return id < other.id || (id == other.id && flags < other.flags); } + { return flags < other.flags || (flags == other.flags && id < other.id); } }; namespace Heap { struct InternalClass : Base { + enum Flag { + NotExtensible = 1 << 0, + Sealed = 1 << 1, + Frozen = 1 << 2, + UsedAsProto = 1 << 3, + Locked = 1 << 4, + }; + enum { MaxRedundantTransitions = 255 }; + ExecutionEngine *engine; const VTable *vtable; quintptr protoId; // unique across the engine, gets changed whenever the proto chain changes @@ -319,21 +334,26 @@ struct InternalClass : Base { SharedInternalClassData<PropertyAttributes> propertyData; typedef InternalClassTransition Transition; - std::vector<Transition> transitions; + QVarLengthArray<Transition, 1> transitions; InternalClassTransition &lookupOrInsertTransition(const InternalClassTransition &t); uint size; - bool extensible; - bool isSealed; - bool isFrozen; - bool isUsedAsProto; + quint8 numRedundantTransitions; + quint8 flags; + + bool isExtensible() const { return !(flags & NotExtensible); } + bool isSealed() const { return flags & Sealed; } + bool isFrozen() const { return flags & Frozen; } + bool isUsedAsProto() const { return flags & UsedAsProto; } + bool isLocked() const { return flags & Locked; } void init(ExecutionEngine *engine); void init(InternalClass *other); void destroy(); - Q_QML_PRIVATE_EXPORT QString keyAt(uint index) const; + Q_QML_EXPORT ReturnedValue keyAt(uint index) const; Q_REQUIRED_RESULT InternalClass *nonExtensible(); + Q_REQUIRED_RESULT InternalClass *locked(); static void addMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, InternalClassEntry *entry); Q_REQUIRED_RESULT InternalClass *addMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry = nullptr); diff --git a/src/qml/jsruntime/qv4iterator.cpp b/src/qml/jsruntime/qv4iterator.cpp index a543565b37..617037ecdc 100644 --- a/src/qml/jsruntime/qv4iterator.cpp +++ b/src/qml/jsruntime/qv4iterator.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <qv4iterator_p.h> #include <qv4symbol_p.h> #include <qv4engine_p.h> diff --git a/src/qml/jsruntime/qv4iterator_p.h b/src/qml/jsruntime/qv4iterator_p.h index 28e337d21b..46e48864ed 100644 --- a/src/qml/jsruntime/qv4iterator_p.h +++ b/src/qml/jsruntime/qv4iterator_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4ITERATOR_P_H #define QV4ITERATOR_P_H @@ -52,7 +16,6 @@ // #include "qv4object_p.h" -#include "qv4arraydata_p.h" QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4jscall.cpp b/src/qml/jsruntime/qv4jscall.cpp new file mode 100644 index 0000000000..513ae59145 --- /dev/null +++ b/src/qml/jsruntime/qv4jscall.cpp @@ -0,0 +1,46 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qv4jscall_p.h" + +#include <QtQml/qqmlinfo.h> + +#include <private/qqmlengine_p.h> +#include <private/qv4qobjectwrapper_p.h> + +QT_BEGIN_NAMESPACE + +/*! \internal + + Sets the arguments of JSCallData from type erased \a args based on type + information provided by \a types + */ +void QV4::populateJSCallArguments(ExecutionEngine *v4, JSCallArguments &jsCall, + int argc, void **args, const QMetaType *types) +{ + for (int ii = 0; ii < argc; ++ii) + jsCall.args[ii] = v4->metaTypeToJS(types[ii], args[ii + 1]); +} + +void QV4::warnAboutCoercionToVoid( + ExecutionEngine *engine, const Value &value, CoercionProblem problem) +{ + auto log = qCritical().nospace().noquote(); + if (const CppStackFrame *frame = engine->currentStackFrame) + log << frame->source() << ':' << frame->lineNumber() << ": "; + log << value.toQStringNoThrow() + << " should be coerced to void because"; + switch (problem) { + case InsufficientAnnotation: + log << " the function called is insufficiently annotated."; + break; + case InvalidListType: + log << " the target type, a list of unknown elements, cannot be resolved."; + break; + } + + log << " The original value is retained. This will change in a future version of Qt."; +} + + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h index 31689b1ba1..43776e4b62 100644 --- a/src/qml/jsruntime/qv4jscall_p.h +++ b/src/qml/jsruntime/qv4jscall_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4JSCALL_H #define QV4JSCALL_H @@ -50,54 +14,83 @@ // We mean it. // -#include "qv4object_p.h" -#include "qv4function_p.h" -#include "qv4functionobject_p.h" -#include "qv4context_p.h" -#include "qv4scopedvalue_p.h" -#include "qv4stackframe_p.h" +#include <private/qqmllistwrapper_p.h> +#include <private/qqmlvaluetypewrapper_p.h> + +#include <private/qv4alloca_p.h> +#include <private/qv4dateobject_p.h> +#include <private/qv4function_p.h> +#include <private/qv4functionobject_p.h> +#include <private/qv4qobjectwrapper_p.h> +#include <private/qv4regexpobject_p.h> +#include <private/qv4scopedvalue_p.h> +#include <private/qv4sequenceobject_p.h> +#include <private/qv4urlobject_p.h> +#include <private/qv4variantobject_p.h> + +#if QT_CONFIG(regularexpression) +#include <QtCore/qregularexpression.h> +#endif QT_BEGIN_NAMESPACE namespace QV4 { -struct JSCallData { - JSCallData(const Scope &scope, int argc = 0, const Value *argv = nullptr, const Value *thisObject = nullptr) - : scope(scope), argc(argc) +template<typename Args> +CallData *callDatafromJS(const Scope &scope, const Args *args, const FunctionObject *f = nullptr) +{ + int size = int(offsetof(QV4::CallData, args)/sizeof(QV4::Value)) + args->argc; + CallData *ptr = reinterpret_cast<CallData *>(scope.alloc<Scope::Uninitialized>(size)); + ptr->function = Encode::undefined(); + ptr->context = Encode::undefined(); + ptr->accumulator = Encode::undefined(); + ptr->thisObject = args->thisObject ? args->thisObject->asReturnedValue() : Encode::undefined(); + ptr->newTarget = Encode::undefined(); + ptr->setArgc(args->argc); + if (args->argc) + memcpy(ptr->args, args->args, args->argc*sizeof(Value)); + if (f) + ptr->function = f->asReturnedValue(); + return ptr; +} + +struct JSCallArguments +{ + JSCallArguments(const Scope &scope, int argc = 0) + : thisObject(scope.alloc()), args(scope.alloc(argc)), argc(argc) { - if (thisObject) - this->thisObject = const_cast<Value *>(thisObject); - else - this->thisObject = scope.alloc(); - if (argv) - this->args = const_cast<Value *>(argv); - else - this->args = scope.alloc(argc); - } - - JSCallData *operator->() { - return this; - } - - CallData *callData(const FunctionObject *f = nullptr) const { - int size = int(offsetof(QV4::CallData, args)/sizeof(QV4::Value)) + argc; - CallData *ptr = reinterpret_cast<CallData *>(scope.alloc<Scope::Uninitialized>(size)); - ptr->function = Encode::undefined(); - ptr->context = Encode::undefined(); - ptr->accumulator = Encode::undefined(); - ptr->thisObject = thisObject->asReturnedValue(); - ptr->newTarget = Encode::undefined(); - ptr->setArgc(argc); - if (argc) - memcpy(ptr->args, args, argc*sizeof(Value)); - if (f) - ptr->function = f->asReturnedValue(); - return ptr; - } - const Scope &scope; - int argc; - Value *args; + } + + CallData *callData(const Scope &scope, const FunctionObject *f = nullptr) const + { + return callDatafromJS(scope, this, f); + } + Value *thisObject; + Value *args; + const int argc; +}; + +struct JSCallData +{ + JSCallData(const Value *thisObject, const Value *argv, int argc) + : thisObject(thisObject), args(argv), argc(argc) + { + } + + Q_IMPLICIT JSCallData(const JSCallArguments &args) + : thisObject(args.thisObject), args(args.args), argc(args.argc) + { + } + + CallData *callData(const Scope &scope, const FunctionObject *f = nullptr) const + { + return callDatafromJS(scope, this, f); + } + + const Value *thisObject; + const Value *args; + const int argc; }; inline @@ -112,29 +105,483 @@ ReturnedValue FunctionObject::call(const JSCallData &data) const return call(data.thisObject, data.args, data.argc); } +void populateJSCallArguments(ExecutionEngine *v4, JSCallArguments &jsCall, int argc, + void **args, const QMetaType *types); -struct ScopedStackFrame { - Scope &scope; - CppStackFrame frame; +template<typename Callable> +ReturnedValue convertAndCall( + ExecutionEngine *engine, const Function::AOTCompiledFunction *aotFunction, + const Value *thisObject, const Value *argv, int argc, Callable call) +{ + const qsizetype numFunctionArguments = aotFunction->types.length() - 1; + Q_ALLOCA_VAR(void *, values, (numFunctionArguments + 1) * sizeof(void *)); + Q_ALLOCA_VAR(QMetaType, types, (numFunctionArguments + 1) * sizeof(QMetaType)); - ScopedStackFrame(Scope &scope, Heap::ExecutionContext *context) - : scope(scope) - { - frame.parent = scope.engine->currentStackFrame; - if (!context) - return; - frame.jsFrame = reinterpret_cast<CallData *>(scope.alloc(sizeof(CallData)/sizeof(Value))); - frame.jsFrame->context = context; - frame.v4Function = frame.parent ? frame.parent->v4Function : nullptr; - scope.engine->currentStackFrame = &frame; + for (qsizetype i = 0; i < numFunctionArguments; ++i) { + const QMetaType argumentType = aotFunction->types[i + 1]; + types[i + 1] = argumentType; + if (const qsizetype argumentSize = argumentType.sizeOf()) { + Q_ALLOCA_VAR(void, argument, argumentSize); + if (argumentType.flags() & QMetaType::NeedsConstruction) { + argumentType.construct(argument); + if (i < argc) + ExecutionEngine::metaTypeFromJS(argv[i], argumentType, argument); + } else if (i >= argc + || !ExecutionEngine::metaTypeFromJS(argv[i], argumentType, argument)) { + // If we can't convert the argument, we need to default-construct it even if it + // doesn't formally need construction. + // E.g. an int doesn't need construction, but we still want it to be 0. + argumentType.construct(argument); + } + + values[i + 1] = argument; + } else { + values[i + 1] = nullptr; + } + } + + Q_ALLOCA_DECLARE(void, returnValue); + types[0] = aotFunction->types[0]; + if (const qsizetype returnSize = types[0].sizeOf()) { + Q_ALLOCA_ASSIGN(void, returnValue, returnSize); + values[0] = returnValue; + if (types[0].flags() & QMetaType::NeedsConstruction) + types[0].construct(returnValue); + } else { + values[0] = nullptr; + } + + if (const QV4::QObjectWrapper *cppThisObject = thisObject + ? thisObject->as<QV4::QObjectWrapper>() + : nullptr) { + call(cppThisObject->object(), values, types, argc); + } else { + call(nullptr, values, types, argc); } - ~ScopedStackFrame() { - scope.engine->currentStackFrame = frame.parent; + + ReturnedValue result; + if (values[0]) { + result = engine->metaTypeToJS(types[0], values[0]); + if (types[0].flags() & QMetaType::NeedsDestruction) + types[0].destruct(values[0]); + } else { + result = Encode::undefined(); } + + for (qsizetype i = 1, end = numFunctionArguments + 1; i < end; ++i) { + if (types[i].flags() & QMetaType::NeedsDestruction) + types[i].destruct(values[i]); + } + + return result; +} + +template<typename Callable> +bool convertAndCall(ExecutionEngine *engine, QObject *thisObject, + void **a, const QMetaType *types, int argc, Callable call) +{ + Scope scope(engine); + QV4::JSCallArguments jsCallData(scope, argc); + + for (int ii = 0; ii < argc; ++ii) + jsCallData.args[ii] = engine->metaTypeToJS(types[ii + 1], a[ii + 1]); + + ScopedObject jsThisObject(scope); + if (thisObject) { + // The result of wrap() can only be null, undefined, or an object. + jsThisObject = QV4::QObjectWrapper::wrap(engine, thisObject); + if (!jsThisObject) + jsThisObject = engine->globalObject; + } else { + jsThisObject = engine->globalObject; + } + + ScopedValue jsResult(scope, call(jsThisObject, jsCallData.args, argc)); + void *result = a[0]; + if (!result) + return !jsResult->isUndefined(); + + const QMetaType resultType = types[0]; + if (scope.hasException()) { + // Clear the return value + resultType.destruct(result); + resultType.construct(result); + } else if (resultType == QMetaType::fromType<QVariant>()) { + // When the return type is QVariant, JS objects are to be returned as + // QJSValue wrapped in QVariant. metaTypeFromJS unwraps them, unfortunately. + *static_cast<QVariant *>(result) = ExecutionEngine::toVariant(jsResult, QMetaType {}); + } else if (!ExecutionEngine::metaTypeFromJS(jsResult, resultType, result)) { + // If we cannot convert, also clear the return value. + // The caller may have given us an uninitialized QObject*, expecting it to be overwritten. + resultType.destruct(result); + resultType.construct(result); + } + return !jsResult->isUndefined(); +} + +inline ReturnedValue coerce( + ExecutionEngine *engine, const Value &value, const QQmlType &qmlType, bool isList); + +inline QObject *coerceQObject(const Value &value, const QQmlType &qmlType) +{ + QObject *o; + if (const QV4::QObjectWrapper *wrapper = value.as<QV4::QObjectWrapper>()) + o = wrapper->object(); + else if (const QV4::QQmlTypeWrapper *wrapper = value.as<QQmlTypeWrapper>()) + o = wrapper->object(); + else + return nullptr; + + return (o && qmlobject_can_qml_cast(o, qmlType)) ? o : nullptr; +} + +enum CoercionProblem +{ + InsufficientAnnotation, + InvalidListType }; +Q_QML_EXPORT void warnAboutCoercionToVoid( + ExecutionEngine *engine, const Value &value, CoercionProblem problem); + +inline ReturnedValue coerceListType( + ExecutionEngine *engine, const Value &value, const QQmlType &qmlType) +{ + QMetaType type = qmlType.qListTypeId(); + const auto metaSequence = [&]() { + // TODO: We should really add the metasequence to the same QQmlType that holds + // all the other type information. Then we can get rid of the extra + // QQmlMetaType::qmlListType() here. + return qmlType.isSequentialContainer() + ? qmlType.listMetaSequence() + : QQmlMetaType::qmlListType(type).listMetaSequence(); + }; + + if (const QV4::Sequence *sequence = value.as<QV4::Sequence>()) { + if (sequence->d()->listType() == type) + return value.asReturnedValue(); + } + + if (const QmlListWrapper *list = value.as<QmlListWrapper>()) { + if (list->d()->propertyType() == type) + return value.asReturnedValue(); + } + + QMetaType listValueType = qmlType.typeId(); + if (!listValueType.isValid()) { + warnAboutCoercionToVoid(engine, value, InvalidListType); + return value.asReturnedValue(); + } + + QV4::Scope scope(engine); + + const ArrayObject *array = value.as<ArrayObject>(); + if (!array) { + return (listValueType.flags() & QMetaType::PointerToQObject) + ? QmlListWrapper::create(engine, listValueType) + : SequencePrototype::fromData(engine, type, metaSequence(), nullptr); + } + + if (listValueType.flags() & QMetaType::PointerToQObject) { + QV4::Scoped<QmlListWrapper> newList(scope, QmlListWrapper::create(engine, type)); + QQmlListProperty<QObject> *listProperty = newList->d()->property(); + + const qsizetype length = array->getLength(); + qsizetype i = 0; + for (; i < length; ++i) { + ScopedValue v(scope, array->get(i)); + listProperty->append(listProperty, coerceQObject(v, qmlType)); + } + + return newList->asReturnedValue(); + } + + QV4::Scoped<Sequence> sequence( + scope, SequencePrototype::fromData(engine, type, metaSequence(), nullptr)); + const qsizetype length = array->getLength(); + for (qsizetype i = 0; i < length; ++i) + sequence->containerPutIndexed(i, array->get(i)); + return sequence->asReturnedValue(); +} + +inline ReturnedValue coerce( + ExecutionEngine *engine, const Value &value, const QQmlType &qmlType, bool isList) +{ + // These are all the named non-list, non-QObject builtins. Only those need special handling. + // Some of them may be wrapped in VariantObject because that is how they are stored in VME + // properties. + if (isList) + return coerceListType(engine, value, qmlType); + + const QMetaType metaType = qmlType.typeId(); + if (!metaType.isValid()) { + if (!value.isUndefined()) + warnAboutCoercionToVoid(engine, value, InsufficientAnnotation); + return value.asReturnedValue(); + } + + switch (metaType.id()) { + case QMetaType::Void: + return Encode::undefined(); + case QMetaType::QVariant: + return value.asReturnedValue(); + case QMetaType::Int: + return Encode(value.toInt32()); + case QMetaType::Double: + return value.convertedToNumber(); + case QMetaType::QString: + return value.toString(engine)->asReturnedValue(); + case QMetaType::Bool: + return Encode(value.toBoolean()); + case QMetaType::QDateTime: + if (value.as<DateObject>()) + return value.asReturnedValue(); + if (const VariantObject *varObject = value.as<VariantObject>()) { + const QVariant &var = varObject->d()->data(); + switch (var.metaType().id()) { + case QMetaType::QDateTime: + return engine->newDateObject(var.value<QDateTime>())->asReturnedValue(); + case QMetaType::QTime: + return engine->newDateObject(var.value<QTime>(), nullptr, -1, 0)->asReturnedValue(); + case QMetaType::QDate: + return engine->newDateObject(var.value<QDate>(), nullptr, -1, 0)->asReturnedValue(); + default: + break; + } + } + return engine->newDateObject(QDateTime())->asReturnedValue(); + case QMetaType::QUrl: + if (value.as<UrlObject>()) + return value.asReturnedValue(); + if (const VariantObject *varObject = value.as<VariantObject>()) { + const QVariant &var = varObject->d()->data(); + return var.metaType() == QMetaType::fromType<QUrl>() + ? engine->newUrlObject(var.value<QUrl>())->asReturnedValue() + : engine->newUrlObject()->asReturnedValue(); + } + // Since URL properties are stored as string, we need to support the string conversion here. + if (const String *string = value.stringValue()) + return engine->newUrlObject(QUrl(string->toQString()))->asReturnedValue(); + return engine->newUrlObject()->asReturnedValue(); +#if QT_CONFIG(regularexpression) + case QMetaType::QRegularExpression: + if (value.as<RegExpObject>()) + return value.asReturnedValue(); + if (const VariantObject *varObject = value.as<VariantObject>()) { + const QVariant &var = varObject->d()->data(); + if (var.metaType() == QMetaType::fromType<QRegularExpression>()) + return engine->newRegExpObject(var.value<QRegularExpression>())->asReturnedValue(); + } + return engine->newRegExpObject(QString(), 0)->asReturnedValue(); +#endif + default: + break; + } + + if (metaType.flags() & QMetaType::PointerToQObject) { + return coerceQObject(value, qmlType) + ? value.asReturnedValue() + : Encode::null(); + } + + if (const QQmlValueTypeWrapper *wrapper = value.as<QQmlValueTypeWrapper>()) { + if (wrapper->type() == metaType) + return value.asReturnedValue(); + } + + if (void *target = QQmlValueTypeProvider::heapCreateValueType(qmlType, value)) { + Heap::QQmlValueTypeWrapper *wrapper = engine->memoryManager->allocate<QQmlValueTypeWrapper>( + nullptr, metaType, qmlType.metaObjectForValueType(), + nullptr, -1, Heap::ReferenceObject::NoFlag); + Q_ASSERT(!wrapper->gadgetPtr()); + wrapper->setGadgetPtr(target); + return wrapper->asReturnedValue(); + } + + return Encode::undefined(); +} + +template<typename Callable> +ReturnedValue coerceAndCall( + ExecutionEngine *engine, + const Function::JSTypedFunction *typedFunction, const CompiledData::Function *compiledFunction, + const Value *argv, int argc, Callable call) +{ + Scope scope(engine); + + QV4::JSCallArguments jsCallData(scope, typedFunction->types.size() - 1); + const CompiledData::Parameter *formals = compiledFunction->formalsTable(); + for (qsizetype i = 0; i < jsCallData.argc; ++i) { + jsCallData.args[i] = coerce( + engine, i < argc ? argv[i] : Encode::undefined(), + typedFunction->types[i + 1], formals[i].type.isList()); + } + + ScopedValue result(scope, call(jsCallData.args, jsCallData.argc)); + return coerce(engine, result, typedFunction->types[0], compiledFunction->returnType.isList()); +} + +// Note: \a to is unininitialized here! This is in contrast to most other related functions. +inline void coerce( + ExecutionEngine *engine, QMetaType fromType, const void *from, QMetaType toType, void *to) +{ + if ((fromType.flags() & QMetaType::PointerToQObject) + && (toType.flags() & QMetaType::PointerToQObject)) { + QObject *fromObj = *static_cast<QObject * const*>(from); + *static_cast<QObject **>(to) + = (fromObj && fromObj->metaObject()->inherits(toType.metaObject())) + ? fromObj + : nullptr; + return; + } + + if (toType == QMetaType::fromType<QVariant>()) { + new (to) QVariant(fromType, from); + return; + } + + if (toType == QMetaType::fromType<QJSPrimitiveValue>()) { + new (to) QJSPrimitiveValue(fromType, from); + return; + } + + if (fromType == QMetaType::fromType<QVariant>()) { + const QVariant *fromVariant = static_cast<const QVariant *>(from); + if (fromVariant->metaType() == toType) + toType.construct(to, fromVariant->data()); + else + coerce(engine, fromVariant->metaType(), fromVariant->data(), toType, to); + return; + } + + if (fromType == QMetaType::fromType<QJSPrimitiveValue>()) { + const QJSPrimitiveValue *fromPrimitive = static_cast<const QJSPrimitiveValue *>(from); + if (fromPrimitive->metaType() == toType) + toType.construct(to, fromPrimitive->data()); + else + coerce(engine, fromPrimitive->metaType(), fromPrimitive->data(), toType, to); + return; + } + + // TODO: This is expensive. We might establish a direct C++-to-C++ type coercion, like we have + // for JS-to-JS. However, we shouldn't need this very often. Most of the time the compiler + // will generate code that passes the right arguments. + if (toType.flags() & QMetaType::NeedsConstruction) + toType.construct(to); + QV4::Scope scope(engine); + QV4::ScopedValue value(scope, engine->fromData(fromType, from)); + if (!ExecutionEngine::metaTypeFromJS(value, toType, to)) + QMetaType::convert(fromType, from, toType, to); } +template<typename TypedFunction, typename Callable> +void coerceAndCall( + ExecutionEngine *engine, const TypedFunction *typedFunction, + void **argv, const QMetaType *types, int argc, Callable call) +{ + const qsizetype numFunctionArguments = typedFunction->parameterCount(); + + Q_ALLOCA_DECLARE(void *, transformedArguments); + Q_ALLOCA_DECLARE(void, transformedResult); + + const QMetaType returnType = typedFunction->returnMetaType(); + const QMetaType frameReturn = types[0]; + bool returnsQVariantWrapper = false; + if (argv[0] && returnType != frameReturn) { + Q_ALLOCA_ASSIGN(void *, transformedArguments, (numFunctionArguments + 1) * sizeof(void *)); + memcpy(transformedArguments, argv, (argc + 1) * sizeof(void *)); + + if (frameReturn == QMetaType::fromType<QVariant>()) { + QVariant *returnValue = static_cast<QVariant *>(argv[0]); + *returnValue = QVariant(returnType); + transformedResult = transformedArguments[0] = returnValue->data(); + returnsQVariantWrapper = true; + } else if (returnType.sizeOf() > 0) { + Q_ALLOCA_ASSIGN(void, transformedResult, returnType.sizeOf()); + transformedArguments[0] = transformedResult; + if (returnType.flags() & QMetaType::NeedsConstruction) + returnType.construct(transformedResult); + } else { + transformedResult = transformedArguments[0] = &argc; // Some non-null marker value + } + } + + for (qsizetype i = 0; i < numFunctionArguments; ++i) { + const bool isValid = argc > i; + const QMetaType frameType = isValid ? types[i + 1] : QMetaType(); + + const QMetaType argumentType = typedFunction->parameterMetaType(i); + if (isValid && argumentType == frameType) + continue; + + if (transformedArguments == nullptr) { + Q_ALLOCA_ASSIGN(void *, transformedArguments, (numFunctionArguments + 1) * sizeof(void *)); + memcpy(transformedArguments, argv, (argc + 1) * sizeof(void *)); + } + + if (argumentType.sizeOf() == 0) { + transformedArguments[i + 1] = nullptr; + continue; + } + + void *frameVal = isValid ? argv[i + 1] : nullptr; + if (isValid && frameType == QMetaType::fromType<QVariant>()) { + QVariant *variant = static_cast<QVariant *>(frameVal); + + const QMetaType variantType = variant->metaType(); + if (variantType == argumentType) { + // Slightly nasty, but we're allowed to do this. + // We don't want to destruct() the QVariant's data() below. + transformedArguments[i + 1] = argv[i + 1] = variant->data(); + } else { + Q_ALLOCA_VAR(void, arg, argumentType.sizeOf()); + coerce(engine, variantType, variant->constData(), argumentType, arg); + transformedArguments[i + 1] = arg; + } + continue; + } + + Q_ALLOCA_VAR(void, arg, argumentType.sizeOf()); + + if (isValid) + coerce(engine, frameType, frameVal, argumentType, arg); + else + argumentType.construct(arg); + + transformedArguments[i + 1] = arg; + } + + if (!transformedArguments) { + call(argv, numFunctionArguments); + return; + } + + call(transformedArguments, numFunctionArguments); + + if (transformedResult && !returnsQVariantWrapper) { + if (frameReturn.sizeOf() > 0) { + if (frameReturn.flags() & QMetaType::NeedsDestruction) + frameReturn.destruct(argv[0]); + coerce(engine, returnType, transformedResult, frameReturn, argv[0]); + } + if (returnType.flags() & QMetaType::NeedsDestruction) + returnType.destruct(transformedResult); + } + + for (qsizetype i = 0; i < numFunctionArguments; ++i) { + void *arg = transformedArguments[i + 1]; + if (arg == nullptr) + continue; + if (i >= argc || arg != argv[i + 1]) { + const QMetaType argumentType = typedFunction->parameterMetaType(i); + if (argumentType.flags() & QMetaType::NeedsDestruction) + argumentType.destruct(arg); + } + } +} + +} // namespace QV4 + QT_END_NAMESPACE #endif // QV4JSCALL_H diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index 936c032fad..d78d09113a 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <qv4jsonobject_p.h> #include <qv4objectproto_p.h> #include <qv4numberobject_p.h> @@ -45,7 +9,6 @@ #include <qv4scopedvalue_p.h> #include <qv4runtime_p.h> #include <qv4variantobject_p.h> -#include "qv4string_p.h" #include "qv4jscall_p.h" #include <qv4symbol_p.h> @@ -125,12 +88,13 @@ enum { bool JsonParser::eatSpace() { while (json < end) { - if (*json > Space) + const char16_t ch = json->unicode(); + if (ch > Space) break; - if (*json != Space && - *json != Tab && - *json != LineFeed && - *json != Return) + if (ch != Space && + ch != Tab && + ch != LineFeed && + ch != Return) break; ++json; } @@ -140,7 +104,7 @@ bool JsonParser::eatSpace() QChar JsonParser::nextToken() { if (!eatSpace()) - return 0; + return u'\0'; QChar token = *json++; switch (token.unicode()) { case BeginArray: @@ -150,10 +114,11 @@ QChar JsonParser::nextToken() case EndArray: case EndObject: eatSpace(); + break; case Quote: break; default: - token = 0; + token = u'\0'; break; } return token; @@ -216,21 +181,21 @@ ReturnedValue JsonParser::parseObject() ScopedObject o(scope, engine->newObject()); QChar token = nextToken(); - while (token == Quote) { + while (token.unicode() == Quote) { if (!parseMember(o)) return Encode::undefined(); token = nextToken(); - if (token != ValueSeparator) + if (token.unicode() != ValueSeparator) break; token = nextToken(); - if (token == EndObject) { + if (token.unicode() == EndObject) { lastError = QJsonParseError::MissingObject; return Encode::undefined(); } } DEBUG << "end token=" << token; - if (token != EndObject) { + if (token.unicode() != EndObject) { lastError = QJsonParseError::UnterminatedObject; return Encode::undefined(); } @@ -253,7 +218,7 @@ bool JsonParser::parseMember(Object *o) if (!parseString(&key)) return false; QChar token = nextToken(); - if (token != NameSeparator) { + if (token.unicode() != NameSeparator) { lastError = QJsonParseError::MissingNameSeparator; return false; } @@ -292,7 +257,7 @@ ReturnedValue JsonParser::parseArray() lastError = QJsonParseError::UnterminatedArray; return Encode::undefined(); } - if (*json == EndArray) { + if (json->unicode() == EndArray) { nextToken(); } else { uint index = 0; @@ -302,9 +267,9 @@ ReturnedValue JsonParser::parseArray() return Encode::undefined(); array->arraySet(index, val); QChar token = nextToken(); - if (token == EndArray) + if (token.unicode() == EndArray) break; - else if (token != ValueSeparator) { + else if (token.unicode() != ValueSeparator) { if (!eatSpace()) lastError = QJsonParseError::UnterminatedArray; else @@ -332,14 +297,14 @@ bool JsonParser::parseValue(Value *val) BEGIN << "parse Value" << *json; switch ((json++)->unicode()) { - case 'n': + case u'n': if (end - json < 3) { lastError = QJsonParseError::IllegalValue; return false; } - if (*json++ == 'u' && - *json++ == 'l' && - *json++ == 'l') { + if (*json++ == u'u' && + *json++ == u'l' && + *json++ == u'l') { *val = Value::nullValue(); DEBUG << "value: null"; END; @@ -347,14 +312,14 @@ bool JsonParser::parseValue(Value *val) } lastError = QJsonParseError::IllegalValue; return false; - case 't': + case u't': if (end - json < 3) { lastError = QJsonParseError::IllegalValue; return false; } - if (*json++ == 'r' && - *json++ == 'u' && - *json++ == 'e') { + if (*json++ == u'r' && + *json++ == u'u' && + *json++ == u'e') { *val = Value::fromBoolean(true); DEBUG << "value: true"; END; @@ -362,15 +327,15 @@ bool JsonParser::parseValue(Value *val) } lastError = QJsonParseError::IllegalValue; return false; - case 'f': + case u'f': if (end - json < 4) { lastError = QJsonParseError::IllegalValue; return false; } - if (*json++ == 'a' && - *json++ == 'l' && - *json++ == 's' && - *json++ == 'e') { + if (*json++ == u'a' && + *json++ == u'l' && + *json++ == u's' && + *json++ == u'e') { *val = Value::fromBoolean(false); DEBUG << "value: false"; END; @@ -443,32 +408,32 @@ bool JsonParser::parseNumber(Value *val) bool isInt = true; // minus - if (json < end && *json == '-') + if (json < end && *json == u'-') ++json; // int = zero / ( digit1-9 *DIGIT ) - if (json < end && *json == '0') { + if (json < end && *json == u'0') { ++json; } else { - while (json < end && *json >= '0' && *json <= '9') + while (json < end && *json >= u'0' && *json <= u'9') ++json; } // frac = decimal-point 1*DIGIT - if (json < end && *json == '.') { + if (json < end && *json == u'.') { isInt = false; ++json; - while (json < end && *json >= '0' && *json <= '9') + while (json < end && *json >= u'0' && *json <= u'9') ++json; } // exp = e [ minus / plus ] 1*DIGIT - if (json < end && (*json == 'e' || *json == 'E')) { + if (json < end && (*json == u'e' || *json == u'E')) { isInt = false; ++json; - if (json < end && (*json == '-' || *json == '+')) + if (json < end && (*json == u'-' || *json == u'+')) ++json; - while (json < end && *json >= '0' && *json <= '9') + while (json < end && *json >= u'0' && *json <= u'9') ++json; } @@ -526,12 +491,12 @@ 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; + if (d >= u'0' && d <= u'9') + *result |= (d - u'0'); + else if (d >= u'a' && d <= u'f') + *result |= (d - u'a') + 10; + else if (d >= u'A' && d <= u'F') + *result |= (d - u'A') + 10; else return false; return true; @@ -546,23 +511,23 @@ static inline bool scanEscapeSequence(const QChar *&json, const QChar *end, uint DEBUG << "scan escape"; uint escaped = (json++)->unicode(); switch (escaped) { - case '"': + case u'"': *ch = '"'; break; - case '\\': + case u'\\': *ch = '\\'; break; - case '/': + case u'/': *ch = '/'; break; - case 'b': + case u'b': *ch = 0x8; break; - case 'f': + case u'f': *ch = 0xc; break; - case 'n': + case u'n': *ch = 0xa; break; - case 'r': + case u'r': *ch = 0xd; break; - case 't': + case u't': *ch = 0x9; break; - case 'u': { + case u'u': { *ch = 0; if (json > end - 4) return false; @@ -585,9 +550,9 @@ bool JsonParser::parseString(QString *string) BEGIN << "parse string stringPos=" << json; while (json < end) { - if (*json == '"') + if (*json == u'"') break; - else if (*json == '\\') { + else if (*json == u'\\') { uint ch = 0; if (!scanEscapeSequence(json, end, &ch)) { lastError = QJsonParseError::IllegalEscapeSequence; @@ -645,47 +610,69 @@ struct Stringify QString makeMember(const QString &key, const Value &v); }; +class [[nodiscard]] CallDepthAndCycleChecker +{ + Q_DISABLE_COPY_MOVE(CallDepthAndCycleChecker); + +public: + CallDepthAndCycleChecker(Stringify *stringify, Object *o) + : m_callDepthRecorder(stringify->v4) + { + if (stringify->stackContains(o)) { + stringify->v4->throwTypeError( + QStringLiteral("Cannot convert circular structure to JSON")); + } + + stringify->v4->checkStackLimits(); + } + + bool foundProblem() const { return m_callDepthRecorder.ee->hasException; } + +private: + ExecutionEngineCallDepthRecorder<1> m_callDepthRecorder; +}; + static QString quote(const QString &str) { QString product; - const int length = str.length(); + const int length = str.size(); product.reserve(length + 2); - product += QLatin1Char('"'); + product += u'"'; for (int i = 0; i < length; ++i) { QChar c = str.at(i); switch (c.unicode()) { - case '"': + case u'"': product += QLatin1String("\\\""); break; - case '\\': + case u'\\': product += QLatin1String("\\\\"); break; - case '\b': + case u'\b': product += QLatin1String("\\b"); break; - case '\f': + case u'\f': product += QLatin1String("\\f"); break; - case '\n': + case u'\n': product += QLatin1String("\\n"); break; - case '\r': + case u'\r': product += QLatin1String("\\r"); break; - case '\t': + case u'\t': product += QLatin1String("\\t"); break; default: if (c.unicode() <= 0x1f) { product += QLatin1String("\\u00"); - product += (c.unicode() > 0xf ? QLatin1Char('1') : QLatin1Char('0')) + + product += (c.unicode() > 0xf ? u'1' : u'0') + QLatin1Char("0123456789abcdef"[c.unicode() & 0xf]); } else { product += c; } } } - product += QLatin1Char('"'); + product += u'"'; return product; } @@ -699,21 +686,31 @@ QString Stringify::Str(const QString &key, const Value &v) ScopedString s(scope, v4->newString(QStringLiteral("toJSON"))); ScopedFunctionObject toJSON(scope, o->get(s)); if (!!toJSON) { - JSCallData jsCallData(scope, 1); - *jsCallData->thisObject = value; - jsCallData->args[0] = v4->newString(key); + JSCallArguments jsCallData(scope, 1); + *jsCallData.thisObject = value; + jsCallData.args[0] = v4->newString(key); value = toJSON->call(jsCallData); + if (v4->hasException) + return QString(); } } if (replacerFunction) { - ScopedObject holder(scope, v4->newObject()); - holder->put(scope.engine->id_empty(), value); - JSCallData jsCallData(scope, 2); - jsCallData->args[0] = v4->newString(key); - jsCallData->args[1] = value; - *jsCallData->thisObject = holder; + JSCallArguments jsCallData(scope, 2); + jsCallData.args[0] = v4->newString(key); + jsCallData.args[1] = value; + + if (stack.isEmpty()) { + ScopedObject holder(scope, v4->newObject()); + holder->put(scope.engine->id_empty(), v); + *jsCallData.thisObject = holder; + } else { + *jsCallData.thisObject = stack.top(); + } + value = replacerFunction->call(jsCallData); + if (v4->hasException) + return QString(); } o = value->asReturnedValue(); @@ -760,9 +757,9 @@ QString Stringify::makeMember(const QString &key, const Value &v) { QString strP = Str(key, v); if (!strP.isEmpty()) { - QString member = quote(key) + QLatin1Char(':'); + QString member = quote(key) + u':'; if (!gap.isEmpty()) - member += QLatin1Char(' '); + member += u' '; member += strP; return member; } @@ -771,10 +768,9 @@ QString Stringify::makeMember(const QString &key, const Value &v) QString Stringify::JO(Object *o) { - if (stackContains(o)) { - v4->throwTypeError(); + CallDepthAndCycleChecker check(this, o); + if (check.foundProblem()) return QString(); - } Scope scope(v4); @@ -817,11 +813,11 @@ QString Stringify::JO(Object *o) if (partial.isEmpty()) { result = QStringLiteral("{}"); } else if (gap.isEmpty()) { - result = QLatin1Char('{') + partial.join(QLatin1Char(',')) + QLatin1Char('}'); + result = u'{' + partial.join(u',') + u'}'; } else { QString separator = QLatin1String(",\n") + indent; - result = QLatin1String("{\n") + indent + partial.join(separator) + QLatin1Char('\n') - + stepback + QLatin1Char('}'); + result = QLatin1String("{\n") + indent + partial.join(separator) + u'\n' + + stepback + u'}'; } indent = stepback; @@ -831,10 +827,9 @@ QString Stringify::JO(Object *o) QString Stringify::JA(Object *a) { - if (stackContains(a)) { - v4->throwTypeError(); + CallDepthAndCycleChecker check(this, a); + if (check.foundProblem()) return QString(); - } Scope scope(a->engine()); @@ -863,10 +858,10 @@ QString Stringify::JA(Object *a) if (partial.isEmpty()) { result = QStringLiteral("[]"); } else if (gap.isEmpty()) { - result = QLatin1Char('[') + partial.join(QLatin1Char(',')) + QLatin1Char(']'); + result = u'[' + partial.join(u',') + u']'; } else { QString separator = QLatin1String(",\n") + indent; - result = QLatin1String("[\n") + indent + partial.join(separator) + QLatin1Char('\n') + stepback + QLatin1Char(']'); + result = QLatin1String("[\n") + indent + partial.join(separator) + u'\n' + stepback + u']'; } indent = stepback; @@ -896,7 +891,7 @@ ReturnedValue JsonObject::method_parse(const FunctionObject *b, const Value *, c jtext = argv[0].toQString(); DEBUG << "parsing source = " << jtext; - JsonParser parser(v4, jtext.constData(), jtext.length()); + JsonParser parser(v4, jtext.constData(), jtext.size()); QJsonParseError error; ReturnedValue result = parser.parse(&error); if (error.error != QJsonParseError::NoError) { @@ -916,9 +911,10 @@ ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value if (o) { stringify.replacerFunction = o->as<FunctionObject>(); if (o->isArrayObject()) { - uint arrayLen = o->getLength(); + int arrayLen = scope.engine->safeForAllocLength(o->getLength()); + CHECK_EXCEPTION(); stringify.propertyList = static_cast<QV4::String *>(scope.alloc(arrayLen)); - for (uint i = 0; i < arrayLen; ++i) { + for (int i = 0; i < arrayLen; ++i) { Value *v = stringify.propertyList + i; *v = o->get(i); if (v->as<NumberObject>() || v->as<StringObject>() || v->isNumber()) @@ -926,7 +922,7 @@ ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value if (!v->isString()) { v->setM(nullptr); } else { - for (uint j = 0; j <i; ++j) { + for (int j = 0; j <i; ++j) { if (stringify.propertyList[j].m() == v->m()) { v->setM(nullptr); break; @@ -944,7 +940,7 @@ ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value s = so->d()->string; if (s->isNumber()) { - stringify.gap = QString(qMin(10, (int)s->toInteger()), ' '); + stringify.gap = QString(qMin(10, (int)s->toInteger()), u' '); } else if (String *str = s->stringValue()) { stringify.gap = str->toQString().left(10); } @@ -952,7 +948,7 @@ ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value ScopedValue arg0(scope, argc ? argv[0] : Value::undefinedValue()); QString result = stringify.Str(QString(), arg0); - if (result.isEmpty() || scope.engine->hasException) + if (result.isEmpty() || scope.hasException()) RETURN_UNDEFINED(); return Encode(scope.engine->newString(result)); } @@ -992,12 +988,16 @@ QJsonValue JsonObject::toJsonValue(const Value &value, V4ObjectSet &visitedObjec Q_ASSERT(value.isObject()); Scope scope(value.as<Object>()->engine()); - ScopedArrayObject a(scope, value); - if (a) + if (ScopedArrayObject a{ scope, value }) { return toJsonArray(a, visitedObjects); - ScopedObject o(scope, value); - if (o) + } else if (Scoped<QV4::Sequence> a{ scope, value }) { + return toJsonArray(a, visitedObjects); + } else if (Scoped<QmlListWrapper> lw{ scope, value }) { + return toJsonArray(lw, visitedObjects); + } else if (ScopedObject o{ scope, value }) { return toJsonObject(o, visitedObjects); + } + return QJsonValue(value.toQString()); } @@ -1062,7 +1062,7 @@ QV4::ReturnedValue JsonObject::fromJsonArray(ExecutionEngine *engine, const QJso return a.asReturnedValue(); } -QJsonArray JsonObject::toJsonArray(const ArrayObject *a, V4ObjectSet &visitedObjects) +QJsonArray JsonObject::toJsonArray(const Object *a, V4ObjectSet &visitedObjects) { QJsonArray result; if (!a) diff --git a/src/qml/jsruntime/qv4jsonobject_p.h b/src/qml/jsruntime/qv4jsonobject_p.h index 7d9f204910..f6f63d7eb3 100644 --- a/src/qml/jsruntime/qv4jsonobject_p.h +++ b/src/qml/jsruntime/qv4jsonobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4JSONOBJECT_H #define QV4JSONOBJECT_H @@ -77,7 +41,7 @@ struct ObjectItem { inline bool operator ==(const ObjectItem &a, const ObjectItem &b) { return a.o->d() == b.o->d(); } -inline int qHash(const ObjectItem &i, uint seed = 0) +inline size_t qHash(const ObjectItem &i, size_t seed = 0) { return ::qHash((void *)i.o->d(), seed); } struct JsonObject : Object { @@ -99,14 +63,13 @@ public: { V4ObjectSet visitedObjects; return toJsonValue(value, visitedObjects); } static inline QJsonObject toJsonObject(const QV4::Object *o) { V4ObjectSet visitedObjects; return toJsonObject(o, visitedObjects); } - static inline QJsonArray toJsonArray(const QV4::ArrayObject *a) - { V4ObjectSet visitedObjects; return toJsonArray(a, visitedObjects); } + static inline QJsonArray toJsonArray(const QV4::Object *o) + { V4ObjectSet visitedObjects; return toJsonArray(o, visitedObjects); } private: static QJsonValue toJsonValue(const QV4::Value &value, V4ObjectSet &visitedObjects); static QJsonObject toJsonObject(const Object *o, V4ObjectSet &visitedObjects); - static QJsonArray toJsonArray(const ArrayObject *a, V4ObjectSet &visitedObjects); - + static QJsonArray toJsonArray(const Object *o, V4ObjectSet &visitedObjects); }; class JsonParser diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 0cda6b864a..654275a709 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -1,46 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "qv4lookup_p.h" -#include "qv4functionobject_p.h" -#include "qv4jscall_p.h" -#include "qv4string_p.h" +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <private/qv4functionobject_p.h> #include <private/qv4identifiertable_p.h> +#include <private/qv4lookup_p.h> +#include <private/qv4qobjectwrapper_p.h> +#include <private/qv4runtime_p.h> +#include <private/qv4stackframe_p.h> QT_BEGIN_NAMESPACE @@ -74,6 +40,9 @@ ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *objec ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + primitiveLookup.type = object.type(); switch (primitiveLookup.type) { case Value::Undefined_Type: @@ -85,12 +54,12 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu return engine->throwTypeError(message); } case Value::Boolean_Type: - primitiveLookup.proto = engine->booleanPrototype()->d(); + primitiveLookup.proto.set(engine, engine->booleanPrototype()->d()); break; case Value::Managed_Type: { // ### Should move this over to the Object path, as strings also have an internalClass Q_ASSERT(object.isStringOrSymbol()); - primitiveLookup.proto = static_cast<const Managed &>(object).internalClass()->prototype; + primitiveLookup.proto.set(engine, static_cast<const Managed &>(object).internalClass()->prototype); Q_ASSERT(primitiveLookup.proto); Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); @@ -103,7 +72,7 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu } case Value::Integer_Type: default: // Number - primitiveLookup.proto = engine->numberPrototype()->d(); + primitiveLookup.proto.set(engine, engine->numberPrototype()->d()); } PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); @@ -119,6 +88,9 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu ReturnedValue Lookup::resolveGlobalGetter(ExecutionEngine *engine) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + Object *o = engine->globalObject; PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); protoLookup.protoId = o->internalClass()->protoId; @@ -144,47 +116,78 @@ ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Va return l->resolvePrimitiveGetter(engine, object); } +static inline void setupObjectLookupTwoClasses(Lookup *l, const Lookup &first, const Lookup &second) +{ + Heap::InternalClass *ic1 = first.objectLookup.ic; + const uint offset1 = first.objectLookup.offset; + Heap::InternalClass *ic2 = second.objectLookup.ic; + const uint offset2 = second.objectLookup.offset; + auto engine = ic1->engine; + + l->objectLookupTwoClasses.ic.set(engine, ic1); + l->objectLookupTwoClasses.ic2.set(engine, ic2); + l->objectLookupTwoClasses.offset = offset1; + l->objectLookupTwoClasses.offset2 = offset2; +} + +static inline void setupProtoLookupTwoClasses(Lookup *l, const Lookup &first, const Lookup &second) +{ + const quintptr protoId1 = first.protoLookup.protoId; + const Value *data1 = first.protoLookup.data; + const quintptr protoId2 = second.protoLookup.protoId; + const Value *data2 = second.protoLookup.data; + + l->protoLookupTwoClasses.protoId = protoId1; + l->protoLookupTwoClasses.protoId2 = protoId2; + l->protoLookupTwoClasses.data = data1; + l->protoLookupTwoClasses.data2 = data2; +} + ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object) { if (const Object *o = object.as<Object>()) { - Lookup first = *l; - Lookup second = *l; - - ReturnedValue result = second.resolveGetter(engine, o); - if (first.getter == getter0Inline && (second.getter == getter0Inline || second.getter == getter0MemberData)) { - l->objectLookupTwoClasses.ic = first.objectLookup.ic; - l->objectLookupTwoClasses.ic2 = second.objectLookup.ic; - l->objectLookupTwoClasses.offset = first.objectLookup.offset; - l->objectLookupTwoClasses.offset2 = second.objectLookup.offset; - l->getter = second.getter == getter0Inline ? getter0Inlinegetter0Inline : getter0Inlinegetter0MemberData; + // Do the resolution on a second lookup, then merge. + Lookup second; + memset(&second, 0, sizeof(Lookup)); + second.nameIndex = l->nameIndex; + second.forCall = l->forCall; + second.getter = getterGeneric; + const ReturnedValue result = second.resolveGetter(engine, o); + + if (l->getter == getter0Inline + && (second.getter == getter0Inline || second.getter == getter0MemberData)) { + setupObjectLookupTwoClasses(l, *l, second); + l->getter = (second.getter == getter0Inline) + ? getter0Inlinegetter0Inline + : getter0Inlinegetter0MemberData; return result; } - if (first.getter == getter0MemberData && (second.getter == getter0Inline || second.getter == getter0MemberData)) { - l->objectLookupTwoClasses.ic = second.objectLookup.ic; - l->objectLookupTwoClasses.ic2 = first.objectLookup.ic; - l->objectLookupTwoClasses.offset = second.objectLookup.offset; - l->objectLookupTwoClasses.offset2 = first.objectLookup.offset; - l->getter = second.getter == getter0Inline ? getter0Inlinegetter0MemberData : getter0MemberDatagetter0MemberData; + + if (l->getter == getter0MemberData + && (second.getter == getter0Inline || second.getter == getter0MemberData)) { + setupObjectLookupTwoClasses(l, second, *l); + l->getter = (second.getter == getter0Inline) + ? getter0Inlinegetter0MemberData + : getter0MemberDatagetter0MemberData; return result; } - if (first.getter == getterProto && second.getter == getterProto) { - l->protoLookupTwoClasses.protoId = first.protoLookup.protoId; - l->protoLookupTwoClasses.protoId2 = second.protoLookup.protoId; - l->protoLookupTwoClasses.data = first.protoLookup.data; - l->protoLookupTwoClasses.data2 = second.protoLookup.data; + + + if (l->getter == getterProto && second.getter == getterProto) { + setupProtoLookupTwoClasses(l, *l, second); l->getter = getterProtoTwoClasses; return result; } - if (first.getter == getterProtoAccessor && second.getter == getterProtoAccessor) { - l->protoLookupTwoClasses.protoId = first.protoLookup.protoId; - l->protoLookupTwoClasses.protoId2 = second.protoLookup.protoId; - l->protoLookupTwoClasses.data = first.protoLookup.data; - l->protoLookupTwoClasses.data2 = second.protoLookup.data; + + if (l->getter == getterProtoAccessor && second.getter == getterProtoAccessor) { + setupProtoLookupTwoClasses(l, *l, second); l->getter = getterProtoAccessorTwoClasses; return result; } + // If any of the above options were true, the propertyCache was inactive. + second.releasePropertyCache(); } l->getter = getterFallback; @@ -201,6 +204,21 @@ ReturnedValue Lookup::getterFallback(Lookup *l, ExecutionEngine *engine, const V return o->get(name); } +ReturnedValue Lookup::getterFallbackAsVariant( + Lookup *l, ExecutionEngine *engine, const Value &object) +{ + if (&Lookup::getterFallback == &Lookup::getterFallbackAsVariant) { + // Certain compilers, e.g. MSVC, will "helpfully" deduplicate methods that are completely + // equal. As a result, the pointers are the same, which wreaks havoc on the logic that + // decides how to retrieve the property. + qFatal("Your C++ compiler is broken."); + } + + // This getter just marks the presence of a fallback lookup with variant conversion. + // It only does anything with it when running AOT-compiled code. + return getterFallback(l, engine, object); +} + ReturnedValue Lookup::getter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object) { // we can safely cast to a QV4::Object here. If object is actually a string, @@ -227,6 +245,9 @@ ReturnedValue Lookup::getter0Inline(Lookup *l, ExecutionEngine *engine, const Va ReturnedValue Lookup::getterProto(Lookup *l, ExecutionEngine *engine, const Value &object) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + // we can safely cast to a QV4::Object here. If object is actually a string, // the internal class won't match Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); @@ -284,6 +305,9 @@ ReturnedValue Lookup::getter0MemberDatagetter0MemberData(Lookup *l, ExecutionEng ReturnedValue Lookup::getterProtoTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + // we can safely cast to a QV4::Object here. If object is actually a string, // the internal class won't match Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); @@ -309,7 +333,8 @@ ReturnedValue Lookup::getterAccessor(Lookup *l, ExecutionEngine *engine, const V if (!getter->isFunctionObject()) // ### catch at resolve time return Encode::undefined(); - return static_cast<const FunctionObject *>(getter)->call(&object, nullptr, 0); + return checkedResult(engine, static_cast<const FunctionObject *>(getter)->call( + &object, nullptr, 0)); } } l->getter = getterFallback; @@ -318,6 +343,9 @@ ReturnedValue Lookup::getterAccessor(Lookup *l, ExecutionEngine *engine, const V ReturnedValue Lookup::getterProtoAccessor(Lookup *l, ExecutionEngine *engine, const Value &object) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + // we can safely cast to a QV4::Object here. If object is actually a string, // the internal class won't match Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); @@ -326,13 +354,17 @@ ReturnedValue Lookup::getterProtoAccessor(Lookup *l, ExecutionEngine *engine, co if (!getter->isFunctionObject()) // ### catch at resolve time return Encode::undefined(); - return static_cast<const FunctionObject *>(getter)->call(&object, nullptr, 0); + return checkedResult(engine, static_cast<const FunctionObject *>(getter)->call( + &object, nullptr, 0)); } return getterTwoClasses(l, engine, object); } ReturnedValue Lookup::getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + // we can safely cast to a QV4::Object here. If object is actually a string, // the internal class won't match Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); @@ -346,7 +378,8 @@ ReturnedValue Lookup::getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine * if (!getter->isFunctionObject()) // ### catch at resolve time return Encode::undefined(); - return static_cast<const FunctionObject *>(getter)->call(&object, nullptr, 0); + return checkedResult(engine, static_cast<const FunctionObject *>(getter)->call( + &object, nullptr, 0)); } } l->getter = getterFallback; @@ -368,11 +401,60 @@ ReturnedValue Lookup::getterIndexed(Lookup *l, ExecutionEngine *engine, const Va } l->getter = getterFallback; return getterFallback(l, engine, object); +} + +ReturnedValue Lookup::getterQObject(Lookup *lookup, ExecutionEngine *engine, const Value &object) +{ + const auto revertLookup = [lookup, engine, &object]() { + lookup->qobjectLookup.propertyCache->release(); + lookup->qobjectLookup.propertyCache = nullptr; + lookup->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(lookup, engine, object); + }; + + const QObjectWrapper::Flags flags = lookup->forCall + ? QObjectWrapper::AllowOverride + : (QObjectWrapper::AllowOverride | QObjectWrapper::AttachMethods); + return QObjectWrapper::lookupPropertyGetterImpl(lookup, engine, object, flags, revertLookup); +} + +ReturnedValue Lookup::getterQObjectAsVariant( + Lookup *lookup, ExecutionEngine *engine, const Value &object) +{ + if (&Lookup::getterQObject == &Lookup::getterQObjectAsVariant) { + // Certain compilers, e.g. MSVC, will "helpfully" deduplicate methods that are completely + // equal. As a result, the pointers are the same, which wreaks havoc on the logic that + // decides how to retrieve the property. + qFatal("Your C++ compiler is broken."); + } + + // This getter marks the presence of a qobjectlookup with variant conversion. + // It only does anything with it when running AOT-compiled code. + return getterQObject(lookup, engine, object); +} + +ReturnedValue Lookup::getterQObjectMethod(Lookup *lookup, ExecutionEngine *engine, const Value &object) +{ + const auto revertLookup = [lookup, engine, &object]() { + lookup->qobjectMethodLookup.propertyCache->release(); + lookup->qobjectMethodLookup.propertyCache = nullptr; + lookup->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(lookup, engine, object); + }; + + const QObjectWrapper::Flags flags = lookup->forCall + ? QObjectWrapper::AllowOverride + : (QObjectWrapper::AllowOverride | QObjectWrapper::AttachMethods); + + return QObjectWrapper::lookupMethodGetterImpl(lookup, engine, object, flags, revertLookup); } ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + if (object.type() == l->primitiveLookup.type && !object.isObject()) { Heap::Object *o = l->primitiveLookup.proto; if (l->primitiveLookup.protoId == o->internalClass->protoId) @@ -384,6 +466,9 @@ ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, c ReturnedValue Lookup::primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + if (object.type() == l->primitiveLookup.type && !object.isObject()) { Heap::Object *o = l->primitiveLookup.proto; if (l->primitiveLookup.protoId == o->internalClass->protoId) { @@ -391,7 +476,8 @@ ReturnedValue Lookup::primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine if (!getter->isFunctionObject()) // ### catch at resolve time return Encode::undefined(); - return static_cast<const FunctionObject *>(getter)->call(&object, nullptr, 0); + return checkedResult(engine, static_cast<const FunctionObject *>(getter)->call( + &object, nullptr, 0)); } } l->getter = getterGeneric; @@ -414,6 +500,9 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetterProto(Lookup *l, ExecutionEngine *engine) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + Heap::Object *o = engine->globalObject->d(); if (l->protoLookup.protoId == o->internalClass->protoId) return l->protoLookup.data->asReturnedValue(); @@ -423,13 +512,17 @@ ReturnedValue Lookup::globalGetterProto(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetterProtoAccessor(Lookup *l, ExecutionEngine *engine) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + Heap::Object *o = engine->globalObject->d(); if (l->protoLookup.protoId == o->internalClass->protoId) { const Value *getter = l->protoLookup.data; if (!getter->isFunctionObject()) // ### catch at resolve time return Encode::undefined(); - return static_cast<const FunctionObject *>(getter)->call(engine->globalObject, nullptr, 0); + return checkedResult(engine, static_cast<const FunctionObject *>(getter)->call( + engine->globalObject, nullptr, 0)); } l->globalGetter = globalGetterGeneric; return globalGetterGeneric(l, engine); @@ -458,23 +551,31 @@ bool Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, co bool Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { - Lookup first = *l; - Lookup second = *l; + // A precondition of this method is that l->objectLookup is the active variant of the union. + Q_ASSERT(l->setter == setter0MemberData || l->setter == setter0Inline); if (object.isObject()) { + + // As l->objectLookup is active, we can stash some members here, before resolving. + Heap::InternalClass *ic = l->objectLookup.ic; + const uint index = l->objectLookup.index; + if (!l->resolveSetter(engine, static_cast<Object *>(&object), value)) { l->setter = setterFallback; return false; } if (l->setter == Lookup::setter0MemberData || l->setter == Lookup::setter0Inline) { - l->objectLookupTwoClasses.ic = first.objectLookup.ic; - l->objectLookupTwoClasses.ic2 = second.objectLookup.ic; - l->objectLookupTwoClasses.offset = first.objectLookup.index; - l->objectLookupTwoClasses.offset2 = second.objectLookup.index; + auto engine = ic->engine; + l->objectLookupTwoClasses.ic.set(engine, ic); + l->objectLookupTwoClasses.ic2.set(engine, ic); + l->objectLookupTwoClasses.offset = index; + l->objectLookupTwoClasses.offset2 = index; l->setter = setter0setter0; return true; } + + l->releasePropertyCache(); } l->setter = setterFallback; @@ -492,6 +593,21 @@ bool Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, c return o->put(name, value); } +bool Lookup::setterFallbackAsVariant( + Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +{ + if (&Lookup::setterFallback == &Lookup::setterFallbackAsVariant) { + // Certain compilers, e.g. MSVC, will "helpfully" deduplicate methods that are completely + // equal. As a result, the pointers are the same, which wreaks havoc on the logic that + // decides how to retrieve the property. + qFatal("Your C++ compiler is broken."); + } + + // This setter just marks the presence of a fallback lookup with QVariant conversion. + // It only does anything with it when running AOT-compiled code. + return setterFallback(l, engine, object, value); +} + bool Lookup::setter0MemberData(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); @@ -534,6 +650,9 @@ bool Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, c bool Lookup::setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + Object *o = static_cast<Object *>(object.managed()); if (o && o->internalClass()->protoId == l->insertionLookup.protoId) { o->setInternalClass(l->insertionLookup.newClass); @@ -545,6 +664,29 @@ bool Lookup::setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, con return setterFallback(l, engine, object, value); } +bool Lookup::setterQObject(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v) +{ + // This setter just marks the presence of a qobjectlookup. It only does anything with it when + // running AOT-compiled code, though. + return setterFallback(l, engine, object, v); +} + +bool Lookup::setterQObjectAsVariant( + Lookup *l, ExecutionEngine *engine, Value &object, const Value &v) +{ + if (&Lookup::setterQObject == &Lookup::setterQObjectAsVariant) { + // Certain compilers, e.g. MSVC, will "helpfully" deduplicate methods that are completely + // equal. As a result, the pointers are the same, which wreaks havoc on the logic that + // decides how to retrieve the property. + qFatal("Your C++ compiler is broken."); + } + + // This setter marks the presence of a qobjectlookup with QVariant conversion. + // It only does anything with it when running AOT-compiled code. + return setterQObject(l, engine, object, v); +} + + bool Lookup::arrayLengthSetter(Lookup *, ExecutionEngine *engine, Value &object, const Value &value) { Q_ASSERT(object.isObject() && static_cast<Object &>(object).isArrayObject()); diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 31c90b31f6..258184cd37 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4LOOKUP_H #define QV4LOOKUP_H @@ -50,18 +14,28 @@ // We mean it. // -#include "qv4global_p.h" -#include "qv4runtime_p.h" #include "qv4engine_p.h" -#include "qv4context_p.h" #include "qv4object_p.h" #include "qv4internalclass_p.h" +#include "qv4qmlcontext_p.h" +#include <private/qqmltypewrapper_p.h> +#include <private/qv4mm_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -struct Q_QML_PRIVATE_EXPORT Lookup { +namespace Heap { + struct QObjectMethod; +} + +template <typename T, int PhantomTag> +using HeapObjectWrapper = WriteBarrier::HeapObjectWrapper<T, PhantomTag>; + +// Note: We cannot hide the copy ctor and assignment operator of this class because it needs to +// be trivially copyable. But you should never ever copy it. There are refcounted members +// in there. +struct Q_QML_EXPORT Lookup { union { ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object); ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine); @@ -69,6 +43,7 @@ struct Q_QML_PRIVATE_EXPORT Lookup { bool (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v); }; // NOTE: gc assumes the first two entries in the struct are pointers to heap objects or null + // or that the least significant bit is 1 (see the Lookup::markObjects function) union { struct { Heap::Base *h1; @@ -77,7 +52,7 @@ struct Q_QML_PRIVATE_EXPORT Lookup { quintptr unused2; } markDef; struct { - Heap::InternalClass *ic; + HeapObjectWrapper<Heap::InternalClass, 0> ic; quintptr unused; uint index; uint offset; @@ -88,8 +63,8 @@ struct Q_QML_PRIVATE_EXPORT Lookup { const Value *data; } protoLookup; struct { - Heap::InternalClass *ic; - Heap::InternalClass *ic2; + HeapObjectWrapper<Heap::InternalClass, 1> ic; + HeapObjectWrapper<Heap::InternalClass, 2> ic2; uint offset; uint offset2; } objectLookupTwoClasses; @@ -102,12 +77,12 @@ struct Q_QML_PRIVATE_EXPORT Lookup { struct { // Make sure the next two values are in sync with protoLookup quintptr protoId; - Heap::Object *proto; + HeapObjectWrapper<Heap::Object, 3> proto; const Value *data; quintptr type; } primitiveLookup; struct { - Heap::InternalClass *newClass; + HeapObjectWrapper<Heap::InternalClass, 4> newClass; quintptr protoId; uint offset; uint unused; @@ -119,16 +94,30 @@ struct Q_QML_PRIVATE_EXPORT Lookup { uint unused; } indexedLookup; struct { - Heap::InternalClass *ic; - Heap::InternalClass *qmlTypeIc; // only used when lookup goes through QQmlTypeWrapper - QQmlPropertyCache *propertyCache; - QQmlPropertyData *propertyData; + HeapObjectWrapper<Heap::InternalClass, 5> ic; + HeapObjectWrapper<Heap::InternalClass, 6> qmlTypeIc; // only used when lookup goes through QQmlTypeWrapper + const QQmlPropertyCache *propertyCache; + const QQmlPropertyData *propertyData; } qobjectLookup; struct { - Heap::InternalClass *ic; - quintptr unused; - QQmlPropertyCache *propertyCache; - QQmlPropertyData *propertyData; + HeapObjectWrapper<Heap::InternalClass, 7> ic; + HeapObjectWrapper<Heap::QObjectMethod, 8> method; + const QQmlPropertyCache *propertyCache; + const QQmlPropertyData *propertyData; + } qobjectMethodLookup; + struct { + quintptr isConstant; // This is a bool, encoded as 0 or 1. Both values are ignored by gc + quintptr metaObject; // a (const QMetaObject* & 1) or nullptr + int coreIndex; + int notifyIndex; + } qobjectFallbackLookup; + struct { + HeapObjectWrapper<Heap::InternalClass, 9> ic; + quintptr metaObject; // a (const QMetaObject* & 1) or nullptr + const QtPrivate::QMetaTypeInterface *metaType; // cannot use QMetaType; class must be trivial + quint16 coreIndex; + bool isFunction; + bool isEnum; } qgadgetLookup; struct { quintptr unused1; @@ -136,8 +125,9 @@ struct Q_QML_PRIVATE_EXPORT Lookup { int scriptIndex; } qmlContextScriptLookup; struct { - Heap::Object *singleton; - quintptr unused; + HeapObjectWrapper<Heap::Base, 10> singletonObject; + quintptr unused2; + QV4::ReturnedValue singletonValue; } qmlContextSingletonLookup; struct { quintptr unused1; @@ -152,20 +142,24 @@ struct Q_QML_PRIVATE_EXPORT Lookup { ReturnedValue (*getterTrampoline)(Lookup *l, ExecutionEngine *engine); } qmlContextGlobalLookup; struct { - Heap::Object *qmlTypeWrapper; + HeapObjectWrapper<Heap::Base, 11> qmlTypeWrapper; quintptr unused2; } qmlTypeLookup; struct { - Heap::InternalClass *ic; + HeapObjectWrapper<Heap::InternalClass, 12> ic; quintptr unused; ReturnedValue encodedEnumValue; + const QtPrivate::QMetaTypeInterface *metaType; } qmlEnumValueLookup; struct { - Heap::InternalClass *ic; - Heap::Object *qmlScopedEnumWrapper; + HeapObjectWrapper<Heap::InternalClass, 13> ic; + HeapObjectWrapper<Heap::Object, 14> qmlScopedEnumWrapper; } qmlScopedEnumWrapperLookup; }; - uint nameIndex; + + uint nameIndex: 28; // Same number of bits we store in the compilation unit for name indices + uint forCall: 1; // Whether we are looking up a value in order to call it right away + uint reserved: 3; ReturnedValue resolveGetter(ExecutionEngine *engine, const Object *object); ReturnedValue resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object); @@ -175,6 +169,7 @@ struct Q_QML_PRIVATE_EXPORT Lookup { static ReturnedValue getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterFallback(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getterFallbackAsVariant(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object); @@ -187,6 +182,9 @@ struct Q_QML_PRIVATE_EXPORT Lookup { static ReturnedValue getterProtoAccessor(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getterQObject(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getterQObjectAsVariant(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getterQObjectMethod(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object); @@ -200,10 +198,13 @@ struct Q_QML_PRIVATE_EXPORT Lookup { static bool setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); Q_NEVER_INLINE static bool setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setterFallbackAsVariant(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setter0MemberData(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setterQObject(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setterQObjectAsVariant(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool arrayLengthSetter(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); void markObjects(MarkStack *stack) { @@ -213,8 +214,24 @@ struct Q_QML_PRIVATE_EXPORT Lookup { markDef.h2->mark(stack); } - void clear() { - memset(&markDef, 0, sizeof(markDef)); + void releasePropertyCache() + { + if (getter == getterQObject + || getter == QQmlTypeWrapper::lookupSingletonProperty + || setter == setterQObject + || qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty + || qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectProperty + || getter == getterQObjectAsVariant + || setter == setterQObjectAsVariant) { + if (const QQmlPropertyCache *pc = qobjectLookup.propertyCache) + pc->release(); + } else if (getter == getterQObjectMethod + || getter == QQmlTypeWrapper::lookupSingletonMethod + || qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectMethod + || qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectMethod) { + if (const QQmlPropertyCache *pc = qobjectMethodLookup.propertyCache) + pc->release(); + } } }; @@ -223,6 +240,57 @@ Q_STATIC_ASSERT(std::is_standard_layout<Lookup>::value); // across 32-bit and 64-bit (matters when cross-compiling). Q_STATIC_ASSERT(offsetof(Lookup, getter) == 0); +inline void setupQObjectLookup( + Lookup *lookup, const QQmlData *ddata, const QQmlPropertyData *propertyData) +{ + lookup->releasePropertyCache(); + Q_ASSERT(!ddata->propertyCache.isNull()); + lookup->qobjectLookup.propertyCache = ddata->propertyCache.data(); + lookup->qobjectLookup.propertyCache->addref(); + lookup->qobjectLookup.propertyData = propertyData; +} + +inline void setupQObjectLookup( + Lookup *lookup, const QQmlData *ddata, const QQmlPropertyData *propertyData, + const Object *self) +{ + setupQObjectLookup(lookup, ddata, propertyData); + lookup->qobjectLookup.ic.set(self->engine(), self->internalClass()); +} + + +inline void setupQObjectLookup( + Lookup *lookup, const QQmlData *ddata, const QQmlPropertyData *propertyData, + const Object *self, const Object *qmlType) +{ + setupQObjectLookup(lookup, ddata, propertyData, self); + lookup->qobjectLookup.qmlTypeIc.set(self->engine(), qmlType->internalClass()); +} + +// template parameter is an ugly trick to avoid pulling in the QObjectMethod header here +template<typename QObjectMethod = Heap::QObjectMethod> +inline void setupQObjectMethodLookup( + Lookup *lookup, const QQmlData *ddata, const QQmlPropertyData *propertyData, + const Object *self, QObjectMethod *method) +{ + lookup->releasePropertyCache(); + Q_ASSERT(!ddata->propertyCache.isNull()); + auto engine = self->engine(); + lookup->qobjectMethodLookup.method.set(engine, method); + lookup->qobjectMethodLookup.ic.set(engine, self->internalClass()); + lookup->qobjectMethodLookup.propertyCache = ddata->propertyCache.data(); + lookup->qobjectMethodLookup.propertyCache->addref(); + lookup->qobjectMethodLookup.propertyData = propertyData; +} + +inline bool qualifiesForMethodLookup(const QQmlPropertyData *propertyData) +{ + return propertyData->isFunction() + && !propertyData->isSignalHandler() // TODO: Optimize SignalHandler, too + && !propertyData->isVMEFunction() // Handled by QObjectLookup + && !propertyData->isVarProperty(); +} + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp index d51b03d90b..3524a1adcb 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -1,45 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4managed_p.h" #include <private/qv4mm_p.h> -#include "qv4errorobject_p.h" using namespace QV4; @@ -105,6 +68,12 @@ QString Managed::className() const case Type_MathObject: s = "Math"; break; + case Type_UrlObject: + s = "URL"; + break; + case Type_UrlSearchParamsObject: + s = "URLSearchParams"; + break; case Type_ExecutionContext: s = "__ExecutionContext"; @@ -131,8 +100,11 @@ QString Managed::className() const s = "__RegExp"; break; - case Type_QmlSequence: - s = "QmlSequence"; + case Type_V4Sequence: + s = "V4Sequence"; + break; + case Type_QmlListProperty: + s = "QML List"; break; } return QString::fromLatin1(s); diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index 4f22dc7330..aeac8c4914 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QMLJS_MANAGED_H #define QMLJS_MANAGED_H @@ -92,10 +56,10 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} #define V4_MANAGED(DataClass, superClass) \ private: \ - DataClass() Q_DECL_EQ_DELETE; \ + DataClass() = delete; \ Q_DISABLE_COPY(DataClass) \ V4_MANAGED_ITSELF(DataClass, superClass) \ - Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value); + Q_STATIC_ASSERT(std::is_trivial_v<QV4::Heap::DataClass>); #define Q_MANAGED_TYPE(type) \ public: \ @@ -105,7 +69,7 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} static Heap::InternalClass *defaultInternalClass(QV4::EngineBase *e) \ { return e->internalClasses(QV4::EngineBase::Class_##c); } -struct Q_QML_PRIVATE_EXPORT Managed : Value, VTableBase +struct Q_QML_EXPORT Managed : Value, VTableBase { V4_MANAGED_ITSELF(Base, Managed) enum { @@ -113,13 +77,13 @@ struct Q_QML_PRIVATE_EXPORT Managed : Value, VTableBase IsString = false, IsStringOrSymbol = false, IsObject = false, - IsFunctionObject = false, + IsTailCallable = false, IsErrorObject = false, IsArrayData = false }; private: void *operator new(size_t); - Managed() Q_DECL_EQ_DELETE; + Managed() = delete; Q_DISABLE_COPY(Managed) public: @@ -144,6 +108,8 @@ public: Type_JsonObject, Type_MathObject, Type_ProxyObject, + Type_UrlObject, + Type_UrlSearchParamsObject, Type_ExecutionContext, Type_InternalClass, @@ -154,7 +120,9 @@ public: Type_ForInIterator, Type_RegExp, - Type_QmlSequence + Type_V4Sequence, + Type_QmlListProperty, + }; Q_MANAGED_TYPE(Invalid) @@ -162,8 +130,9 @@ public: const VTable *vtable() const { return d()->internalClass->vtable; } inline ExecutionEngine *engine() const { return internalClass()->engine; } - bool isListType() const { return d()->internalClass->vtable->type == Type_QmlSequence; } - bool isArrayLike() const { return isArrayObject() || isListType(); } + bool isV4SequenceType() const { return d()->internalClass->vtable->type == Type_V4Sequence; } + bool isQmlListPropertyType() const { return d()->internalClass->vtable->type == Type_QmlListProperty; } + bool isArrayLike() const { return isArrayObject() || isV4SequenceType() || isQmlListPropertyType(); } bool isArrayObject() const { return d()->internalClass->vtable->type == Type_ArrayObject; } bool isStringObject() const { return d()->internalClass->vtable->type == Type_StringObject; } diff --git a/src/qml/jsruntime/qv4mapiterator.cpp b/src/qml/jsruntime/qv4mapiterator.cpp index cd5fbb8e63..494ee94865 100644 --- a/src/qml/jsruntime/qv4mapiterator.cpp +++ b/src/qml/jsruntime/qv4mapiterator.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Crimson AS <info@crimson.no> -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 Crimson AS <info@crimson.no> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <private/qv4iterator_p.h> #include <private/qv4estable_p.h> diff --git a/src/qml/jsruntime/qv4mapiterator_p.h b/src/qml/jsruntime/qv4mapiterator_p.h index 836ba14663..97a72db85c 100644 --- a/src/qml/jsruntime/qv4mapiterator_p.h +++ b/src/qml/jsruntime/qv4mapiterator_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Crimson AS <info@crimson.no> -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 Crimson AS <info@crimson.no> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4MAPITERATOR_P_H #define QV4MAPITERATOR_P_H @@ -66,7 +30,7 @@ namespace Heap { Member(class, NoMark, quint32, mapNextIndex) DECLARE_HEAP_OBJECT(MapIteratorObject, Object) { - DECLARE_MARKOBJECTS(MapIteratorObject); + DECLARE_MARKOBJECTS(MapIteratorObject) void init(Object *obj, QV4::ExecutionEngine *engine) { Object::init(); diff --git a/src/qml/jsruntime/qv4mapobject.cpp b/src/qml/jsruntime/qv4mapobject.cpp index 90e1908a84..5e7f92a339 100644 --- a/src/qml/jsruntime/qv4mapobject.cpp +++ b/src/qml/jsruntime/qv4mapobject.cpp @@ -1,43 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Crimson AS <info@crimson.no> -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4setobject_p.h" // ### temporary +// Copyright (C) 2018 Crimson AS <info@crimson.no> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + #include "qv4mapobject_p.h" #include "qv4mapiterator_p.h" #include "qv4estable_p.h" @@ -49,14 +12,14 @@ DEFINE_OBJECT_VTABLE(WeakMapCtor); DEFINE_OBJECT_VTABLE(MapCtor); DEFINE_OBJECT_VTABLE(MapObject); -void Heap::WeakMapCtor::init(QV4::ExecutionContext *scope) +void Heap::WeakMapCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("WeakMap")); + Heap::FunctionObject::init(engine, QStringLiteral("WeakMap")); } -void Heap::MapCtor::init(QV4::ExecutionContext *scope) +void Heap::MapCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("Map")); + Heap::FunctionObject::init(engine, QStringLiteral("Map")); } ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget, bool weakMap) @@ -111,8 +74,7 @@ ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv, if (scope.hasException()) break; } - ScopedValue falsey(scope, Encode(false)); - return Runtime::IteratorClose::call(scope.engine, iter, falsey); + return Runtime::IteratorClose::call(scope.engine, iter); } } return a->asReturnedValue(); @@ -252,6 +214,17 @@ ReturnedValue WeakMapPrototype::method_set(const FunctionObject *b, const Value (!argc || !argv[0].isObject())) return scope.engine->throwTypeError(); + QV4::WriteBarrier::markCustom(scope.engine, [&](QV4::MarkStack *ms) { + if (scope.engine->memoryManager->gcStateMachine->state <= GCState::FreeWeakMaps) + return; + Q_ASSERT(argv[0].heapObject()); + argv[0].heapObject()->mark(ms); + if (argc > 1) { + if (auto *h = argv[1].heapObject()) + h->mark(ms); + } + }); + that->d()->esTable->set(argv[0], argc > 1 ? argv[1] : Value::undefinedValue()); return that.asReturnedValue(); } @@ -355,6 +328,15 @@ ReturnedValue MapPrototype::method_set(const FunctionObject *b, const Value *thi if (!that || that->d()->isWeakMap) return scope.engine->throwTypeError(); + QV4::WriteBarrier::markCustom(scope.engine, [&](QV4::MarkStack *ms) { + if (auto *h = argv[0].heapObject()) + h->mark(ms); + if (argc > 1) { + if (auto *h = argv[1].heapObject()) + h->mark(ms); + } + }); + that->d()->esTable->set(argc ? argv[0] : Value::undefinedValue(), argc > 1 ? argv[1] : Value::undefinedValue()); return that.asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4mapobject_p.h b/src/qml/jsruntime/qv4mapobject_p.h index a0fae2a14a..e7ff02c13a 100644 --- a/src/qml/jsruntime/qv4mapobject_p.h +++ b/src/qml/jsruntime/qv4mapobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Crimson AS <info@crimson.no> -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 Crimson AS <info@crimson.no> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4MAPOBJECT_P_H #define QV4MAPOBJECT_P_H @@ -52,9 +16,7 @@ // #include "qv4object_p.h" -#include "qv4objectproto_p.h" #include "qv4functionobject_p.h" -#include "qv4string_p.h" QT_BEGIN_NAMESPACE @@ -65,11 +27,11 @@ class ESTable; namespace Heap { struct WeakMapCtor : FunctionObject { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; struct MapCtor : WeakMapCtor { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; struct MapObject : Object { @@ -116,7 +78,7 @@ struct WeakMapPrototype : Object static ReturnedValue method_delete(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + Q_AUTOTEST_EXPORT static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; struct MapPrototype : WeakMapPrototype @@ -130,7 +92,7 @@ struct MapPrototype : WeakMapPrototype static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + Q_AUTOTEST_EXPORT static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_get_size(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; diff --git a/src/qml/jsruntime/qv4math_p.h b/src/qml/jsruntime/qv4math_p.h index 6632d69c27..b12990700d 100644 --- a/src/qml/jsruntime/qv4math_p.h +++ b/src/qml/jsruntime/qv4math_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QMLJS_MATH_H #define QMLJS_MATH_H @@ -70,7 +34,7 @@ namespace QV4 { static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b) { int result; - if (Q_UNLIKELY(add_overflow(a, b, &result))) + if (Q_UNLIKELY(qAddOverflow(a, b, &result))) return StaticValue::fromDouble(static_cast<double>(a) + b).asReturnedValue(); return StaticValue::fromInt32(result).asReturnedValue(); } @@ -78,7 +42,7 @@ static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b) static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b) { int result; - if (Q_UNLIKELY(sub_overflow(a, b, &result))) + if (Q_UNLIKELY(qSubOverflow(a, b, &result))) return StaticValue::fromDouble(static_cast<double>(a) - b).asReturnedValue(); return StaticValue::fromInt32(result).asReturnedValue(); } @@ -86,8 +50,11 @@ static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b) static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b) { int result; - if (Q_UNLIKELY(mul_overflow(a, b, &result))) + if (Q_UNLIKELY(qMulOverflow(a, b, &result))) return StaticValue::fromDouble(static_cast<double>(a) * b).asReturnedValue(); + // need to handle the case where one number is negative and the other 0 ==> -0 + if (((a < 0) xor (b < 0)) && (result == 0)) + return StaticValue::fromDouble(-0.0).asReturnedValue(); return StaticValue::fromInt32(result).asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp index 07440047d4..71ff6ec0f8 100644 --- a/src/qml/jsruntime/qv4mathobject.cpp +++ b/src/qml/jsruntime/qv4mathobject.cpp @@ -1,44 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4mathobject_p.h" -#include "qv4objectproto_p.h" #include "qv4symbol_p.h" #include <QtCore/qdatetime.h> @@ -47,7 +10,6 @@ #include <QtCore/private/qnumeric_p.h> #include <QtCore/qthreadstorage.h> -#include <math.h> #include <cmath> using namespace QV4; @@ -146,11 +108,12 @@ ReturnedValue MathObject::method_acosh(const FunctionObject *, const Value *, co if (v < 1) RETURN_RESULT(Encode(qt_qnan())); -#ifdef Q_OS_ANDROID // incomplete std :-( - RETURN_RESULT(Encode(std::log(v +std::sqrt(v + 1) * std::sqrt(v - 1)))); -#else - RETURN_RESULT(Encode(std::acosh(v))); +#ifdef Q_CC_MINGW + // Mingw has a broken std::acosh(). It returns NaN when passed Infinity. + if (std::isinf(v)) + RETURN_RESULT(Encode(v)); #endif + RETURN_RESULT(Encode(std::acosh(v))); } ReturnedValue MathObject::method_asin(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -167,12 +130,7 @@ ReturnedValue MathObject::method_asinh(const FunctionObject *, const Value *, co double v = argc ? argv[0].toNumber() : 2; if (v == 0.0) RETURN_RESULT(Encode(v)); - -#ifdef Q_OS_ANDROID // incomplete std :-( - RETURN_RESULT(Encode(std::log(v +std::sqrt(1 + v * v)))); -#else RETURN_RESULT(Encode(std::asinh(v))); -#endif } ReturnedValue MathObject::method_atan(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -190,17 +148,7 @@ ReturnedValue MathObject::method_atanh(const FunctionObject *, const Value *, co if (v == 0.0) RETURN_RESULT(Encode(v)); -#ifdef Q_OS_ANDROID // incomplete std :-( - if (-1 < v && v < 1) - RETURN_RESULT(Encode(0.5 * (std::log(v + 1) - std::log(v - 1)))); - - if (v > 1 || v < -1) - RETURN_RESULT(Encode(qt_qnan())); - - RETURN_RESULT(Encode(copySign(qt_inf(), v))); -#else RETURN_RESULT(Encode(std::atanh(v))); -#endif } ReturnedValue MathObject::method_atan2(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -224,11 +172,7 @@ ReturnedValue MathObject::method_atan2(const FunctionObject *, const Value *, co ReturnedValue MathObject::method_cbrt(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); -#ifdef Q_OS_ANDROID // incomplete std :-( - RETURN_RESULT(Encode(copySign(std::exp(std::log(std::abs(v)) / 3), v))); -#else RETURN_RESULT(Encode(std::cbrt(v))); // cube root -#endif } ReturnedValue MathObject::method_ceil(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -282,11 +226,7 @@ ReturnedValue MathObject::method_expm1(const FunctionObject *, const Value *, co else RETURN_RESULT(Encode(qt_inf())); } else { -#ifdef Q_OS_ANDROID // incomplete std :-( - RETURN_RESULT(Encode(std::exp(v) - 1)); -#else RETURN_RESULT(Encode(std::expm1(v))); -#endif } } @@ -311,29 +251,14 @@ ReturnedValue MathObject::method_hypot(const FunctionObject *, const Value *, co { // ES6 Math.hypot(v1, ..., vn) -> sqrt(sum(vi**2)) but "should take care to // avoid the loss of precision from overflows and underflows" (as std::hypot does). - double v = argc ? argv[0].toNumber() : 0; + double v = 0; // Spec mandates +0 on no args; and says nothing about what to do if toNumber() signals ... -#ifdef Q_OS_ANDROID // incomplete std :-( - bool big = qt_is_inf(v), bad = std::isnan(v); - v *= v; - for (int i = 1; !big && i < argc; i++) { - double u = argv[i].toNumber(); - if (qt_is_inf(u)) - big = true; - if (std::isnan(u)) - bad = true; - v += u * u; + if (argc > 0) { + QtPrivate::QHypotHelper<double> h(argv[0].toNumber()); + for (int i = 1; i < argc; i++) + h = h.add(argv[i].toNumber()); + v = h.result(); } - if (big) - RETURN_RESULT(Encode(qt_inf())); - if (bad) - RETURN_RESULT(Encode(qt_qnan())); - // Should actually check for {und,ov}erflow, but too fiddly ! - RETURN_RESULT(Value::fromDouble(sqrt(v))); -#else - for (int i = 1; i < argc; i++) - v = std::hypot(v, argv[i].toNumber()); -#endif RETURN_RESULT(Value::fromDouble(v)); } @@ -381,13 +306,7 @@ ReturnedValue MathObject::method_log2(const FunctionObject *, const Value *, con if (v < 0) { RETURN_RESULT(Encode(qt_qnan())); } else { -#ifdef Q_OS_ANDROID // incomplete std :-( - // Android ndk r10e doesn't have std::log2, so fall back. - const double ln2 = std::log(2.0); - RETURN_RESULT(Encode(std::log(v) / ln2)); -#else RETURN_RESULT(Encode(std::log2(v))); -#endif } } @@ -422,49 +341,7 @@ ReturnedValue MathObject::method_pow(const FunctionObject *, const Value *, cons double x = argc > 0 ? argv[0].toNumber() : qt_qnan(); double y = argc > 1 ? argv[1].toNumber() : qt_qnan(); - if (std::isnan(y)) - RETURN_RESULT(Encode(qt_qnan())); - - if (y == 0) { - RETURN_RESULT(Encode(1)); - } else if (((x == 1) || (x == -1)) && std::isinf(y)) { - RETURN_RESULT(Encode(qt_qnan())); - } else if (((x == 0) && copySign(1.0, x) == 1.0) && (y < 0)) { - RETURN_RESULT(Encode(qInf())); - } else if ((x == 0) && copySign(1.0, x) == -1.0) { - if (y < 0) { - if (std::fmod(-y, 2.0) == 1.0) - RETURN_RESULT(Encode(-qt_inf())); - else - RETURN_RESULT(Encode(qt_inf())); - } else if (y > 0) { - if (std::fmod(y, 2.0) == 1.0) - RETURN_RESULT(Encode(copySign(0, -1.0))); - else - RETURN_RESULT(Encode(0)); - } - } - -#ifdef Q_OS_AIX - else if (qt_is_inf(x) && copySign(1.0, x) == -1.0) { - if (y > 0) { - if (std::fmod(y, 2.0) == 1.0) - RETURN_RESULT(Encode(-qt_inf())); - else - RETURN_RESULT(Encode(qt_inf())); - } else if (y < 0) { - if (std::fmod(-y, 2.0) == 1.0) - RETURN_RESULT(Encode(copySign(0, -1.0))); - else - RETURN_RESULT(Encode(0)); - } - } -#endif - else { - RETURN_RESULT(Encode(std::pow(x, y))); - } - // ### - RETURN_RESULT(Encode(qt_qnan())); + RETURN_RESULT(Encode(QQmlPrivate::jsExponentiate(x, y))); } ReturnedValue MathObject::method_random(const FunctionObject *, const Value *, const Value *, int) @@ -475,11 +352,14 @@ ReturnedValue MathObject::method_random(const FunctionObject *, const Value *, c ReturnedValue MathObject::method_round(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); - if (std::isnan(v) || qt_is_inf(v) || qIsNull(v)) + if (!std::isfinite(v)) RETURN_RESULT(Encode(v)); - v = copySign(std::floor(v + 0.5), v); - RETURN_RESULT(Encode(v)); + if (v < 0.5 && v >= -0.5) + v = std::copysign(0.0, v); + else + v = std::floor(v + 0.5); + RETURN_RESULT(Encode(v)); } ReturnedValue MathObject::method_sign(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -540,13 +420,5 @@ ReturnedValue MathObject::method_tanh(const FunctionObject *, const Value *, con ReturnedValue MathObject::method_trunc(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); -#ifdef Q_OS_ANDROID // incomplete std :-( - if (std::isnan(v) || qt_is_inf(v) || qIsNull(v)) - RETURN_RESULT(Encode(v)); - // Nearest integer not greater in magnitude: - quint64 whole = std::abs(v); - RETURN_RESULT(Encode(copySign(whole, v))); -#else RETURN_RESULT(Encode(std::trunc(v))); -#endif } diff --git a/src/qml/jsruntime/qv4mathobject_p.h b/src/qml/jsruntime/qv4mathobject_p.h index 2658e25438..5547cbab61 100644 --- a/src/qml/jsruntime/qv4mathobject_p.h +++ b/src/qml/jsruntime/qv4mathobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4MATHOBJECT_H #define QV4MATHOBJECT_H diff --git a/src/qml/jsruntime/qv4memberdata.cpp b/src/qml/jsruntime/qv4memberdata.cpp index 34b0c38ae6..0231641609 100644 --- a/src/qml/jsruntime/qv4memberdata.cpp +++ b/src/qml/jsruntime/qv4memberdata.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4memberdata_p.h" #include <private/qv4mm_p.h> @@ -82,7 +46,7 @@ Heap::MemberData *MemberData::allocate(ExecutionEngine *e, uint n, Heap::MemberD if (oldSize > alloc) alloc = oldSize; m = e->memoryManager->allocManaged<MemberData>(alloc); - // no write barrier required here + // no write barrier required here, as m gets marked later when member data is set memcpy(m, old, oldSize); } else { m = e->memoryManager->allocManaged<MemberData>(alloc); diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h index 186083b83a..672b058fef 100644 --- a/src/qml/jsruntime/qv4memberdata_p.h +++ b/src/qml/jsruntime/qv4memberdata_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4MEMBERDATA_H #define QV4MEMBERDATA_H @@ -63,9 +27,9 @@ namespace Heap { Member(class, ValueArray, ValueArray, values) DECLARE_HEAP_OBJECT(MemberData, Base) { - DECLARE_MARKOBJECTS(MemberData); + DECLARE_MARKOBJECTS(MemberData) }; -Q_STATIC_ASSERT(std::is_trivial< MemberData >::value); +Q_STATIC_ASSERT(std::is_trivial_v<MemberData>); } diff --git a/src/qml/jsruntime/qv4module.cpp b/src/qml/jsruntime/qv4module.cpp index 08a1900383..f701529096 100644 --- a/src/qml/jsruntime/qv4module.cpp +++ b/src/qml/jsruntime/qv4module.cpp @@ -1,52 +1,17 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4module_p.h" -#include <private/qv4mm_p.h> -#include <private/qv4vme_moth_p.h> #include <private/qv4context_p.h> -#include <private/qv4symbol_p.h> #include <private/qv4identifiertable_p.h> +#include <private/qv4mm_p.h> +#include <private/qv4stackframe_p.h> +#include <private/qv4symbol_p.h> +#include <private/qv4vme_moth_p.h> -#include <QScopeGuard> +#include <QtCore/qscopeguard.h> using namespace QV4; @@ -86,8 +51,8 @@ void Heap::Module::init(ExecutionEngine *engine, ExecutableCompilationUnit *modu { Scoped<QV4::InternalClass> ic(valueScope, scope->internalClass); - for (uint i = 0; i < unit->data->importEntryTableSize; ++i) { - const CompiledData::ImportEntry &import = unit->data->importEntryTable()[i]; + for (uint i = 0; i < unit->unitData()->importEntryTableSize; ++i) { + const CompiledData::ImportEntry &import = unit->unitData()->importEntryTable()[i]; ic = ic->addMember(engine->identifierTable->asPropertyKey(unit->runtimeStrings[import.localName]), Attr_NotConfigurable); } scope->internalClass.set(engine, ic->d()); @@ -111,16 +76,16 @@ void Module::evaluate() unit->evaluateModuleRequests(); ExecutionEngine *v4 = engine(); - Function *moduleFunction = unit->runtimeFunctions[unit->data->indexOfRootFunction]; - CppStackFrame frame; - frame.init(v4, moduleFunction, nullptr, 0); + Function *moduleFunction = unit->runtimeFunctions[unit->unitData()->indexOfRootFunction]; + JSTypesStackFrame frame; + frame.init(moduleFunction, nullptr, 0); frame.setupJSFrame(v4->jsStackTop, Value::undefinedValue(), d()->scope, Value::undefinedValue(), Value::undefinedValue()); - frame.push(); + frame.push(v4); v4->jsStackTop += frame.requiredJSStackFrameSize(); - auto frameCleanup = qScopeGuard([&frame]() { - frame.pop(); + auto frameCleanup = qScopeGuard([&frame, v4]() { + frame.pop(v4); }); Moth::VME::exec(&frame, v4); } @@ -231,7 +196,7 @@ struct ModuleNamespaceIterator : ObjectOwnPropertyKeyIterator PropertyKey ModuleNamespaceIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) { const Module *module = static_cast<const Module *>(o); - if (exportIndex < exportedNames.count()) { + if (exportIndex < exportedNames.size()) { if (attrs) *attrs = Attr_Data; Scope scope(module->engine()); @@ -258,9 +223,12 @@ OwnPropertyKeyIterator *Module::virtualOwnPropertyKeys(const Object *o, Value *t if (module->d()->unit->isESModule()) { names = module->d()->unit->exportedNames(); } else { - Heap::InternalClass *scopeClass = module->d()->scope->internalClass; - for (uint i = 0; i < scopeClass->size; ++i) - names << scopeClass->keyAt(i); + QV4::Scope scope(module->engine()); + QV4::Scoped<InternalClass> scopeClass(scope, module->d()->scope->internalClass); + for (uint i = 0, end = scopeClass->d()->size; i < end; ++i) { + QV4::ScopedValue key(scope, scopeClass->d()->keyAt(i)); + names << key->toQString(); + } } return new ModuleNamespaceIterator(names); diff --git a/src/qml/jsruntime/qv4module_p.h b/src/qml/jsruntime/qv4module_p.h index aabb2e005e..43cd995b06 100644 --- a/src/qml/jsruntime/qv4module_p.h +++ b/src/qml/jsruntime/qv4module_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4MODULE #define QV4MODULE diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index 13f6912371..5299a2568a 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -1,45 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4numberobject_p.h" #include "qv4runtime_p.h" -#include "qv4string_p.h" #include <QtCore/qnumeric.h> #include <QtCore/qmath.h> @@ -73,9 +36,9 @@ const NumberLocale *NumberLocale::instance() return numberLocaleHolder(); } -void Heap::NumberCtor::init(QV4::ExecutionContext *scope) +void Heap::NumberCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("Number")); + Heap::FunctionObject::init(engine, QStringLiteral("Number")); } ReturnedValue NumberCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) @@ -196,7 +159,7 @@ ReturnedValue NumberPrototype::method_isSafeInteger(const FunctionObject *, cons return Encode(false); double iv = v.toInteger(); - return Encode(dv == iv && std::fabs(iv) <= (2^53)-1); + return Encode(dv == iv && std::fabs(iv) <= (1LL << 53) - 1); } ReturnedValue NumberPrototype::method_isNaN(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -306,7 +269,7 @@ ReturnedValue NumberPrototype::method_toPrecision(const FunctionObject *b, const { Scope scope(b); ScopedValue v(scope, thisNumberValue(scope.engine, thisObject)); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); double d = v->asDouble(); @@ -314,7 +277,7 @@ ReturnedValue NumberPrototype::method_toPrecision(const FunctionObject *b, const return Encode(v->toString(scope.engine)); int precision = argv[0].toInt32(); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); if (std::isnan(d)) diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h index 576817cf36..3f0d6d9425 100644 --- a/src/qml/jsruntime/qv4numberobject_p.h +++ b/src/qml/jsruntime/qv4numberobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4NUMBEROBJECT_H #define QV4NUMBEROBJECT_H @@ -61,7 +25,7 @@ namespace QV4 { namespace Heap { struct NumberCtor : FunctionObject { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; } diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 89161433ed..4c265a6b23 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -1,77 +1,84 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4object_p.h" -#include "qv4objectproto_p.h" -#include "qv4stringobject_p.h" -#include "qv4argumentsobject_p.h" + +#include <private/qv4argumentsobject_p.h> +#include <private/qv4identifiertable_p.h> +#include <private/qv4jscall_p.h> +#include <private/qv4lookup_p.h> +#include <private/qv4memberdata_p.h> #include <private/qv4mm_p.h> -#include "qv4lookup_p.h" -#include "qv4scopedvalue_p.h" -#include "qv4memberdata_p.h" -#include "qv4objectiterator_p.h" -#include "qv4identifier_p.h" -#include "qv4string_p.h" -#include "qv4identifiertable_p.h" -#include "qv4jscall_p.h" -#include "qv4symbol_p.h" -#include "qv4proxy_p.h" +#include <private/qv4proxy_p.h> +#include <private/qv4scopedvalue_p.h> +#include <private/qv4stackframe_p.h> +#include <private/qv4stringobject_p.h> +#include <private/qv4symbol_p.h> + +#include <QtCore/qloggingcategory.h> #include <stdint.h> using namespace QV4; +using namespace Qt::Literals::StringLiterals; + +Q_STATIC_LOGGING_CATEGORY(lcJavaScriptGlobals, "qt.qml.js.globals") DEFINE_OBJECT_VTABLE(Object); void Object::setInternalClass(Heap::InternalClass *ic) { - d()->internalClass.set(engine(), ic); - if (ic->isUsedAsProto) - ic->updateProtoUsage(d()); Q_ASSERT(ic && ic->vtable); - uint nInline = d()->vtable()->nInlineProperties; - if (ic->size <= nInline) - return; - bool hasMD = d()->memberData != nullptr; - uint requiredSize = ic->size - nInline; - if (!(hasMD && requiredSize) || (hasMD && d()->memberData->values.size < requiredSize)) - d()->memberData.set(ic->engine, MemberData::allocate(ic->engine, requiredSize, d()->memberData)); + Heap::Object *p = d(); + + if (ic->numRedundantTransitions < p->internalClass.get()->numRedundantTransitions) { + // IC was rebuilt. The indices are different now. We need to move everything. + + Scope scope(engine()); + + // We allocate before setting the new IC. Protect it from GC. + Scoped<InternalClass> newIC(scope, ic); + + // Pick the members of the old IC that are still valid in the new IC. + // Order them by index in memberData (or inline data). + Scoped<MemberData> newMembers(scope, MemberData::allocate(scope.engine, ic->size)); + for (uint i = 0; i < ic->size; ++i) { + // Note that some members might have been deleted. The key may be invalid. + const PropertyKey key = ic->nameMap.at(i); + newMembers->set(scope.engine, i, key.isValid() ? get(key) : Encode::undefined()); + } + + p->internalClass.set(scope.engine, ic); + const uint nInline = p->vtable()->nInlineProperties; + + if (ic->size > nInline) + p->memberData.set(scope.engine, MemberData::allocate(ic->engine, ic->size - nInline)); + else + p->memberData.set(scope.engine, nullptr); + + const auto &memberValues = newMembers->d()->values; + for (uint i = 0; i < ic->size; ++i) + setProperty(i, memberValues[i]); + } else { + // The old indices are still the same. No need to move any values. + // We may need to re-allocate, though. + + p->internalClass.set(ic->engine, ic); + const uint nInline = p->vtable()->nInlineProperties; + if (ic->size > nInline) { + const uint requiredSize = ic->size - nInline; + if ((p->memberData ? p->memberData->values.size : 0) < requiredSize) { + p->memberData.set(ic->engine, MemberData::allocate( + ic->engine, requiredSize, p->memberData)); + } + } + } + + // Before the engine is done initializing, we cannot have any lookups. + // Therefore, there is no point in updating the proto IDs. + if (ic->engine->isInitialized && ic->isUsedAsProto()) + ic->updateProtoUsage(p); + } void Object::getProperty(const InternalClassEntry &entry, Property *p) const @@ -102,10 +109,10 @@ ReturnedValue Object::getValueAccessor(const Value *thisObject, const Value &v, return Encode::undefined(); Scope scope(f->engine()); - JSCallData jsCallData(scope); + JSCallArguments jsCallData(scope); if (thisObject) - *jsCallData->thisObject = *thisObject; - return f->call(jsCallData); + *jsCallData.thisObject = *thisObject; + return checkedResult(scope.engine, f->call(jsCallData)); } bool Object::putValue(uint memberIndex, PropertyAttributes attrs, const Value &value) @@ -119,9 +126,9 @@ bool Object::putValue(uint memberIndex, PropertyAttributes attrs, const Value &v if (set) { Scope scope(ic->engine); ScopedFunctionObject setter(scope, set); - JSCallData jsCallData(scope, 1); - jsCallData->args[0] = value; - *jsCallData->thisObject = this; + JSCallArguments jsCallData(scope, 1); + jsCallData.args[0] = value; + *jsCallData.thisObject = this; setter->call(jsCallData); return !ic->engine->hasException; } @@ -176,16 +183,16 @@ void Object::defineAccessorProperty(StringOrSymbol *name, VTable::Call getter, V QV4::Scope scope(v4); ScopedProperty p(scope); QString n = name->toQString(); - if (n.at(0) == QLatin1Char('@')) - n = QChar::fromLatin1('[') + n.midRef(1) + QChar::fromLatin1(']'); + if (!n.isEmpty() && n.at(0) == '@'_L1) + n = '['_L1 + QStringView{n}.mid(1) + ']'_L1; if (getter) { - ScopedString getName(scope, v4->newString(QString::fromLatin1("get ") + n)); + ScopedString getName(scope, v4->newString("get "_L1 + n)); p->setGetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(v4, getName, getter, 0))); } else { p->setGetter(nullptr); } if (setter) { - ScopedString setName(scope, v4->newString(QString::fromLatin1("set ") + n)); + ScopedString setName(scope, v4->newString("set "_L1 + n)); p->setSetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(v4, setName, setter, 0))); } else { p->setSetter(nullptr); @@ -460,7 +467,7 @@ ReturnedValue Object::internalGet(PropertyKey id, const Value *receiver, bool *h bool Object::internalPut(PropertyKey id, const Value &value, Value *receiver) { Scope scope(this); - if (scope.engine->hasException) + if (scope.hasException()) return false; Object *r = receiver->objectValue(); @@ -519,11 +526,11 @@ bool Object::internalPut(PropertyKey id, const Value &value, Value *receiver) ScopedFunctionObject setter(scope, p->setter()); if (!setter) return false; - JSCallData jsCallData(scope, 1); - jsCallData->args[0] = value; - *jsCallData->thisObject = *receiver; + JSCallArguments jsCallData(scope, 1); + jsCallData.args[0] = value; + *jsCallData.thisObject = *receiver; setter->call(jsCallData); - return !scope.engine->hasException; + return !scope.hasException(); } // Data property @@ -566,7 +573,7 @@ bool Object::internalDeleteProperty(PropertyKey id) if (id.isArrayIndex()) { uint index = id.asArrayIndex(); Scope scope(engine()); - if (scope.engine->hasException) + if (scope.hasException()) return false; Scoped<ArrayData> ad(scope, arrayData()); @@ -735,8 +742,17 @@ ReturnedValue Object::virtualInstanceOf(const Object *typeObject, const Value &v ReturnedValue Object::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + Heap::Object *obj = object->d(); PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]); + if (object->as<QV4::ProxyObject>()) { + // proxies invalidate assumptions that we normally maek in lookups + // so we always need to use the fallback path + lookup->getter = Lookup::getterFallback; + return lookup->getter(lookup, engine, *object); + } if (name.isArrayIndex()) { lookup->indexedLookup.index = name.asArrayIndex(); lookup->getter = Lookup::getterIndexed; @@ -758,7 +774,7 @@ ReturnedValue Object::virtualResolveLookupGetter(const Object *object, Execution } else { lookup->getter = Lookup::getterAccessor; } - lookup->objectLookup.ic = obj->internalClass; + lookup->objectLookup.ic.set(engine, obj->internalClass.get()); lookup->objectLookup.offset = index.index; return lookup->getter(lookup, engine, *object); } @@ -770,6 +786,9 @@ ReturnedValue Object::virtualResolveLookupGetter(const Object *object, Execution bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + Scope scope(engine); ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]); @@ -782,7 +801,7 @@ bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, lookup->setter = Lookup::arrayLengthSetter; return lookup->setter(lookup, engine, *object, value); } else if (idx.attrs.isData() && idx.attrs.isWritable()) { - lookup->objectLookup.ic = object->internalClass(); + lookup->objectLookup.ic.set(engine, object->internalClass()); lookup->objectLookup.index = idx.index; const auto nInline = object->d()->vtable()->nInlineProperties; if (idx.index < nInline) { @@ -816,12 +835,37 @@ bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, lookup->setter = Lookup::setterFallback; return false; } - lookup->insertionLookup.newClass = object->internalClass(); + lookup->insertionLookup.newClass.set(engine, object->internalClass()); lookup->insertionLookup.offset = idx.index; lookup->setter = Lookup::setterInsert; return true; } +/*! + * \internal + * + * This method is modeled after \l{QMetaObject::metacall}. It takes a JavaScript + * \a object and performs \a call on it, using \a index as identifier for the + * property/method/etc to be used and \a a as arguments. Like + * \l{QMetaObject::metacall} this method is overly generic in order to reduce the + * API surface. On a plain Object it does nothing and returns 0. Specific + * objects can override it and do some meaningful work. If the metacall succeeds + * they should return a non-0 value. Otherwise they should return 0. + * + * Most prominently, \l QMetaObject::ReadProperty and \l QMetaObject::WriteProperty + * calls can be used to manipulate properties of QObjects and Q_GADGETs wrapped + * by QV4::QObjectWrapper, QV4::QQmlTypeWrapper and QV4::QQmlValueTypeWrapper. + * They can also be used to manipulate elements in QV4::Sequence. + */ +int Object::virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a) +{ + Q_UNUSED(object); + Q_UNUSED(call); + Q_UNUSED(index); + Q_UNUSED(a); + return 0; +} + ReturnedValue Object::checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *f, const Value &var) { Scope scope(engine); @@ -939,12 +983,29 @@ bool Object::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property return o->internalDefineOwnProperty(scope.engine, index, nullptr, p, attrs); } - auto memberIndex = o->internalClass()->find(id); + Scoped<InternalClass> ic(scope, o->internalClass()); + auto memberIndex = ic->d()->find(id); if (!memberIndex.isValid()) { if (!o->isExtensible()) return false; + // If the IC is locked, you're not allowed to shadow any unconfigurable properties. + if (ic->d()->isLocked()) { + while (Heap::Object *prototype = ic->d()->prototype) { + ic = prototype->internalClass; + const auto entry = ic->d()->find(id); + if (entry.isValid()) { + if (entry.attributes.isConfigurable()) + break; + qCWarning(lcJavaScriptGlobals).noquote() + << QStringLiteral("You cannot shadow the locked property " + "'%1' in QML.").arg(id.toQString()); + return false; + } + } + } + Scoped<StringOrSymbol> name(scope, id.asStringOrSymbol()); ScopedProperty pd(scope); pd->copy(p, attrs); @@ -958,7 +1019,7 @@ bool Object::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property bool Object::virtualIsExtensible(const Managed *m) { - return m->d()->internalClass->extensible; + return m->d()->internalClass->isExtensible(); } bool Object::virtualPreventExtensions(Managed *m) @@ -978,11 +1039,12 @@ bool Object::virtualSetPrototypeOf(Managed *m, const Object *proto) { Q_ASSERT(m->isObject()); Object *o = static_cast<Object *>(m); - Heap::Object *current = o->internalClass()->prototype; + Heap::InternalClass *ic = o->internalClass(); + Heap::Object *current = ic->prototype; Heap::Object *protod = proto ? proto->d() : nullptr; if (current == protod) return true; - if (!o->internalClass()->extensible) + if (!ic->isExtensible() || ic->isLocked()) return false; Heap::Object *p = protod; while (p) { @@ -992,7 +1054,7 @@ bool Object::virtualSetPrototypeOf(Managed *m, const Object *proto) break; p = p->prototype(); } - o->setInternalClass(o->internalClass()->changePrototype(protod)); + o->setInternalClass(ic->changePrototype(protod)); return true; } @@ -1013,6 +1075,8 @@ bool Object::setArrayLength(uint newLen) } else { if (newLen >= 0x100000) initSparseArray(); + else + ArrayData::realloc(this, arrayType(), newLen, false); } setArrayLengthUnchecked(newLen); return ok; @@ -1103,7 +1167,7 @@ void Heap::ArrayObject::init(const QStringList &list) // 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(); + int len = list.size(); a->arrayReserve(len); ScopedValue v(scope); for (int ii = 0; ii < len; ++ii) @@ -1126,6 +1190,7 @@ QStringList ArrayObject::toQStringList() const ScopedValue v(scope); uint length = getLength(); + result.reserve(length); for (uint i = 0; i < length; ++i) { v = const_cast<ArrayObject *>(this)->get(i); result.append(v->toQStringNoThrow()); diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index f3375929a3..55d18fad52 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4_OBJECT_H #define QV4_OBJECT_H @@ -57,7 +21,6 @@ #include "qv4scopedvalue_p.h" #include "qv4value_p.h" #include "qv4internalclass_p.h" -#include "qv4string_p.h" QT_BEGIN_NAMESPACE @@ -157,7 +120,7 @@ struct Q_QML_EXPORT Object: Managed { const Value *propertyData(uint index) const { return d()->propertyData(index); } Heap::ArrayData *arrayData() const { return d()->arrayData; } - void setArrayData(ArrayData *a) { d()->arrayData.set(engine(), a->d()); } + void setArrayData(ArrayData *a) { d()->arrayData.set(engine(), a ? a->d() : nullptr); } void getProperty(const InternalClassEntry &entry, Property *p) const; void setProperty(const InternalClassEntry &entry, const Property *p); @@ -380,6 +343,9 @@ public: ReturnedValue resolveLookupSetter(ExecutionEngine *engine, Lookup *lookup, const Value &value) { return vtable()->resolveLookupSetter(this, engine, lookup, value); } + int metacall(QMetaObject::Call call, int index, void **a) + { return vtable()->metacall(this, call, index, a); } + protected: static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver,bool *hasProperty); static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); @@ -396,6 +362,7 @@ protected: static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var); static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value); + static int virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a); public: // qv4runtime uses this directly static ReturnedValue checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *typeObject, const Value &var); @@ -410,7 +377,7 @@ private: friend struct ObjectPrototype; }; -struct Q_QML_PRIVATE_EXPORT ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator +struct Q_QML_EXPORT ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator { uint arrayIndex = 0; uint memberIndex = 0; @@ -504,7 +471,12 @@ inline void Object::push_back(const Value &v) { arrayCreate(); - uint idx = getLength(); + const auto length = getLength(); + if (Q_UNLIKELY(length == std::numeric_limits<uint>::max())) { + engine()->throwRangeError(QLatin1String("Too many elements.")); + return; + } + uint idx = uint(length); arrayReserve(idx + 1); arrayPut(idx, v); setArrayLengthUnchecked(idx + 1); diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp index e529b8e86b..90eb326d65 100644 --- a/src/qml/jsruntime/qv4objectiterator.cpp +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -1,49 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4objectiterator_p.h" #include "qv4object_p.h" -#include "qv4stringobject_p.h" -#include "qv4identifier_p.h" -#include "qv4argumentsobject_p.h" -#include "qv4string_p.h" #include "qv4iterator_p.h" #include "qv4propertykey_p.h" +#include <QtQml/private/qv4functionobject_p.h> using namespace QV4; @@ -182,7 +143,7 @@ PropertyKey ForInIteratorObject::nextProperty() const if (d()->current != d()->object) { o = d()->object; bool shadowed = false; - while (o->d() != c->heapObject()) { + while (o && o->d() != c->heapObject()) { if (o->getOwnProperty(key) != Attr_Invalid) { shadowed = true; break; diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h index a20ce9cb88..7f06faf9d5 100644 --- a/src/qml/jsruntime/qv4objectiterator_p.h +++ b/src/qml/jsruntime/qv4objectiterator_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4OBJECTITERATOR_H #define QV4OBJECTITERATOR_H diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 3d3b3f413f..2a78bb4540 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -1,52 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Crimson AS <info@crimson.no> -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 Crimson AS <info@crimson.no> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4objectproto_p.h" #include "qv4argumentsobject_p.h" #include <private/qv4mm_p.h> #include "qv4scopedvalue_p.h" -#include "qv4runtime_p.h" #include "qv4objectiterator_p.h" -#include "qv4string_p.h" -#include "qv4jscall_p.h" #include "qv4symbol_p.h" #include "qv4propertykey_p.h" @@ -58,9 +19,9 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(ObjectCtor); -void Heap::ObjectCtor::init(QV4::ExecutionContext *scope) +void Heap::ObjectCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("Object")); + Heap::FunctionObject::init(engine, QStringLiteral("Object")); } ReturnedValue ObjectCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) @@ -137,7 +98,7 @@ ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, co return scope.engine->throwTypeError(); ScopedObject o(scope, argv[0].toObject(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); ScopedObject p(scope, o->getPrototypeOf()); @@ -160,7 +121,7 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const FunctionObj return scope.engine->throwTypeError(); ScopedObject O(scope, argv[0].toObject(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); if (ArgumentsObject::isNonStrictArgumentsObject(O)) @@ -168,7 +129,7 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const FunctionObj ScopedValue v(scope, argc > 1 ? argv[1] : Value::undefinedValue()); ScopedPropertyKey name(scope, v->toPropertyKey(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); ScopedProperty desc(scope); @@ -183,7 +144,7 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptors(const FunctionOb return scope.engine->throwTypeError(); ScopedObject o(scope, argv[0].toObject(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return Encode::undefined(); ScopedObject descriptors(scope, scope.engine->newObject()); @@ -212,7 +173,7 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyNames(const FunctionObject * return scope.engine->throwTypeError(); ScopedObject O(scope, argv[0].toObject(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); return Encode(getOwnPropertyNames(scope.engine, argv[0])); @@ -252,7 +213,7 @@ ReturnedValue ObjectPrototype::method_assign(const FunctionObject *b, const Valu return scope.engine->throwTypeError(); ScopedObject to(scope, argv[0].toObject(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); if (argc == 1) @@ -263,7 +224,7 @@ ReturnedValue ObjectPrototype::method_assign(const FunctionObject *b, const Valu continue; ScopedObject from(scope, argv[i].toObject(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); QV4::ScopedArrayObject keys(scope, QV4::ObjectPrototype::getOwnPropertyNames(scope.engine, from)); quint32 length = keys->getLength(); @@ -284,7 +245,7 @@ ReturnedValue ObjectPrototype::method_assign(const FunctionObject *b, const Valu propValue = from->get(nextKey); to->set(nextKey, propValue, Object::DoThrowOnRejection); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); } } @@ -322,14 +283,14 @@ ReturnedValue ObjectPrototype::method_defineProperty(const FunctionObject *b, co ScopedObject O(scope, argv[0]); ScopedPropertyKey name(scope, (argc > 1 ? argv[1] : Value::undefinedValue()).toPropertyKey(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); ScopedValue attributes(scope, argc > 2 ? argv[2] : Value::undefinedValue()); ScopedProperty pd(scope); PropertyAttributes attrs; toPropertyDescriptor(scope.engine, attributes, pd, &attrs); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); if (!O->defineOwnProperty(name, pd, attrs)) @@ -347,7 +308,7 @@ ReturnedValue ObjectPrototype::method_defineProperties(const FunctionObject *b, ScopedObject O(scope, argv[0]); ScopedObject o(scope, argv[1].toObject(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); ScopedValue val(scope); @@ -364,7 +325,7 @@ ReturnedValue ObjectPrototype::method_defineProperties(const FunctionObject *b, PropertyAttributes nattrs; val = o->getValue(pd->value, attrs); toPropertyDescriptor(scope.engine, val, n, &nattrs); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); bool ok = O->defineOwnProperty(key, n, nattrs); if (!ok) @@ -381,7 +342,7 @@ ReturnedValue ObjectPrototype::method_entries(const FunctionObject *f, const Val return scope.engine->throwTypeError(); ScopedObject o(scope, argv[0].toObject(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return Encode::undefined(); ScopedArrayObject a(scope, scope.engine->newArrayObject()); @@ -405,7 +366,7 @@ ReturnedValue ObjectPrototype::method_entries(const FunctionObject *f, const Val entry = a->get(PropertyKey::fromArrayIndex(i)); name = entry->get(PropertyKey::fromArrayIndex(0)); value = o->get(name->toPropertyKey()); - if (scope.engine->hasException) + if (scope.hasException()) return Encode::undefined(); entry->push_back(value); } @@ -560,7 +521,7 @@ ReturnedValue ObjectPrototype::method_keys(const FunctionObject *b, const Value return scope.engine->throwTypeError(); ScopedObject o(scope, argv[0].toObject(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); ScopedArrayObject a(scope, scope.engine->newArrayObject()); @@ -602,7 +563,7 @@ ReturnedValue ObjectPrototype::method_values(const FunctionObject *f, const Valu return scope.engine->throwTypeError(); ScopedObject o(scope, argv[0].toObject(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); ScopedArrayObject a(scope, scope.engine->newArrayObject()); @@ -658,6 +619,7 @@ ReturnedValue ObjectPrototype::method_toString(const FunctionObject *b, const Va ReturnedValue ObjectPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); + CHECK_STACK_LIMITS(scope.engine) ScopedObject o(scope, thisObject->toObject(scope.engine)); if (!o) RETURN_UNDEFINED(); @@ -666,7 +628,7 @@ ReturnedValue ObjectPrototype::method_toLocaleString(const FunctionObject *b, co if (!f) THROW_TYPE_ERROR(); - return f->call(thisObject, argv, argc); + return checkedResult(scope.engine, f->call(thisObject, argv, argc)); } ReturnedValue ObjectPrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int) @@ -678,10 +640,10 @@ ReturnedValue ObjectPrototype::method_hasOwnProperty(const FunctionObject *b, co { Scope scope(b); ScopedPropertyKey P(scope, (argc ? argv[0] : Value::undefinedValue()).toPropertyKey(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); ScopedObject O(scope, thisObject->toObject(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); bool r = O->getOwnProperty(P) != Attr_Invalid; return Encode(r); @@ -695,7 +657,7 @@ ReturnedValue ObjectPrototype::method_isPrototypeOf(const FunctionObject *b, con ScopedObject V(scope, argv[0]); ScopedObject O(scope, thisObject->toObject(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); ScopedObject proto(scope, V->getPrototypeOf()); while (proto) { @@ -710,11 +672,11 @@ ReturnedValue ObjectPrototype::method_propertyIsEnumerable(const FunctionObject { Scope scope(b); ScopedPropertyKey p(scope, (argc ? argv[0] : Value::undefinedValue()).toPropertyKey(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); ScopedObject o(scope, thisObject->toObject(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); PropertyAttributes attrs = o->getOwnProperty(p); return Encode(attrs.isEnumerable()); @@ -731,7 +693,7 @@ ReturnedValue ObjectPrototype::method_defineGetter(const FunctionObject *b, cons THROW_TYPE_ERROR(); ScopedString prop(scope, argv[0], ScopedString::Convert); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); ScopedObject o(scope, thisObject); @@ -761,7 +723,7 @@ ReturnedValue ObjectPrototype::method_defineSetter(const FunctionObject *b, cons THROW_TYPE_ERROR(); ScopedString prop(scope, argv[0], ScopedString::Convert); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); ScopedObject o(scope, thisObject); diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h index 8707305dc2..d74cd64926 100644 --- a/src/qml/jsruntime/qv4objectproto_p.h +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4ECMAOBJECTS_P_H #define QV4ECMAOBJECTS_P_H @@ -61,7 +25,7 @@ namespace QV4 { namespace Heap { struct ObjectCtor : FunctionObject { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; } @@ -74,7 +38,7 @@ struct ObjectCtor: FunctionObject static ReturnedValue virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc); }; -struct Q_QML_PRIVATE_EXPORT ObjectPrototype: Object +struct Q_QML_EXPORT ObjectPrototype: Object { void init(ExecutionEngine *engine, Object *ctor); diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp index 79c372348f..4f11d0a2ad 100644 --- a/src/qml/jsruntime/qv4persistent.cpp +++ b/src/qml/jsruntime/qv4persistent.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4persistent_p.h" #include <private/qv4mm_p.h> @@ -64,7 +28,7 @@ struct Page { Value values[1]; // Really kEntriesPerPage, but keep the compiler happy }; -Page *getPage(Value *val) { +Page *getPage(const Value *val) { return reinterpret_cast<Page *>(reinterpret_cast<quintptr>(val) & ~((quintptr)(WTF::pageSize() - 1))); } @@ -178,6 +142,7 @@ PersistentValueStorage::PersistentValueStorage(ExecutionEngine *engine) PersistentValueStorage::~PersistentValueStorage() { + clearFreePageHint(); Page *p = static_cast<Page *>(firstPage); while (p) { for (int i = 0; i < kEntriesPerPage; ++i) { @@ -195,7 +160,9 @@ PersistentValueStorage::~PersistentValueStorage() Value *PersistentValueStorage::allocate() { - Page *p = static_cast<Page *>(firstPage); + Page *p = static_cast<Page *>(freePageHint); + if (p && p->header.freeList == -1) + p = static_cast<Page *>(firstPage); while (p) { if (p->header.freeList != -1) break; @@ -207,9 +174,15 @@ Value *PersistentValueStorage::allocate() Value *v = p->values + p->header.freeList; p->header.freeList = v->int_32(); - if (p->header.freeList != -1 && p != firstPage) { - unlink(p); - insertInFront(this, p); + if (p->header.freeList != -1 && p != freePageHint) { + if (auto oldHint = static_cast<Page *>(freePageHint)) { + oldHint->header.refCount--; + // no need to free - if the old page were unused, + // we would have used it to serve the allocation + Q_ASSERT(oldHint->header.refCount); + } + freePageHint = p; + p->header.refCount++; } ++p->header.refCount; @@ -219,11 +192,9 @@ Value *PersistentValueStorage::allocate() return v; } -void PersistentValueStorage::free(Value *v) +void PersistentValueStorage::freeUnchecked(Value *v) { - if (!v) - return; - + Q_ASSERT(v); Page *p = getPage(v); *v = Encode(p->header.freeList); @@ -240,13 +211,23 @@ void PersistentValueStorage::mark(MarkStack *markStack) if (Managed *m = p->values[i].as<Managed>()) m->mark(markStack); } - markStack->drain(); p = p->header.next; } } -ExecutionEngine *PersistentValueStorage::getEngine(Value *v) +void PersistentValueStorage::clearFreePageHint() +{ + if (!freePageHint) + return; + auto page = static_cast<Page *>(freePageHint); + if (!--page->header.refCount) + freePage(page); + freePageHint = nullptr; + +} + +ExecutionEngine *PersistentValueStorage::getEngine(const Value *v) { return getPage(v)->header.engine; } @@ -262,22 +243,18 @@ void PersistentValueStorage::freePage(void *page) PersistentValue::PersistentValue(const PersistentValue &other) : val(nullptr) { - if (other.val) { - val = other.engine()->memoryManager->m_persistentValues->allocate(); - *val = *other.val; - } + if (other.val) + set(other.engine(), *other.val); } PersistentValue::PersistentValue(ExecutionEngine *engine, const Value &value) { - val = engine->memoryManager->m_persistentValues->allocate(); - *val = value; + set(engine, value); } PersistentValue::PersistentValue(ExecutionEngine *engine, ReturnedValue value) { - val = engine->memoryManager->m_persistentValues->allocate(); - *val = value; + set(engine, value); } PersistentValue::PersistentValue(ExecutionEngine *engine, Object *object) @@ -285,14 +262,7 @@ PersistentValue::PersistentValue(ExecutionEngine *engine, Object *object) { if (!object) return; - - val = engine->memoryManager->m_persistentValues->allocate(); - *val = object; -} - -PersistentValue::~PersistentValue() -{ - PersistentValueStorage::free(val); + set(engine, *object); } PersistentValue &PersistentValue::operator=(const PersistentValue &other) @@ -315,19 +285,16 @@ PersistentValue &PersistentValue::operator=(const PersistentValue &other) PersistentValue &PersistentValue::operator=(const WeakValue &other) { - if (!val) { - if (!other.valueRef()) - return *this; - val = other.engine()->memoryManager->m_persistentValues->allocate(); - } + if (!val && !other.valueRef()) + return *this; if (!other.valueRef()) { *val = Encode::undefined(); return *this; } - Q_ASSERT(engine() == other.engine()); + Q_ASSERT(!engine() || engine() == other.engine()); - *val = *other.valueRef(); + set(other.engine(), *other.valueRef()); return *this; } @@ -337,10 +304,7 @@ PersistentValue &PersistentValue::operator=(Object *object) PersistentValueStorage::free(val); return *this; } - if (!val) - val = object->engine()->memoryManager->m_persistentValues->allocate(); - - *val = object; + set(object->engine(), *object); return *this; } @@ -348,6 +312,10 @@ void PersistentValue::set(ExecutionEngine *engine, const Value &value) { if (!val) val = engine->memoryManager->m_persistentValues->allocate(); + QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack){ + if (QV4::WriteBarrier::isInsertionBarrier && value.isManaged()) + value.heapObject()->mark(stack); + }); *val = value; } @@ -355,6 +323,13 @@ void PersistentValue::set(ExecutionEngine *engine, ReturnedValue value) { if (!val) val = engine->memoryManager->m_persistentValues->allocate(); + QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack){ + if constexpr (!QV4::WriteBarrier::isInsertionBarrier) + return; + auto val = Value::fromReturnedValue(value); + if (val.isManaged()) + val.heapObject()->mark(stack); + }); *val = value; } @@ -362,6 +337,11 @@ void PersistentValue::set(ExecutionEngine *engine, Heap::Base *obj) { if (!val) val = engine->memoryManager->m_persistentValues->allocate(); + QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack){ + if constexpr (QV4::WriteBarrier::isInsertionBarrier) + obj->mark(stack); + }); + *val = obj; } @@ -403,6 +383,56 @@ WeakValue::~WeakValue() free(); } +/* + WeakValue::set shold normally not mark objects, after all a weak value + is not supposed to keep an object alive. + However, if we are past GCState::HandleQObjectWrappers, nothing will + reset weak values referencing unmarked values, but those values will + still be swept. + That lead to stale pointers, and potentially to crashes. To avoid this, + we mark the objects here (they might still get collected in the next gc + run). + This is especially important due to the way we handle QObjectWrappers. + */ +void WeakValue::set(ExecutionEngine *engine, const Value &value) +{ + if (!val) + allocVal(engine); + QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *ms) { + if (engine->memoryManager->gcStateMachine->state <= GCState::HandleQObjectWrappers) + return; + if (auto *h = value.heapObject()) + h->mark(ms); + }); + *val = value; +} + +void WeakValue::set(ExecutionEngine *engine, ReturnedValue value) +{ + if (!val) + allocVal(engine); + QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *ms) { + if (engine->memoryManager->gcStateMachine->state <= GCState::HandleQObjectWrappers) + return; + if (auto *h = QV4::Value::fromReturnedValue(value).heapObject()) + h->mark(ms); + }); + + *val = value; +} + +void WeakValue::set(ExecutionEngine *engine, Heap::Base *obj) +{ + if (!val) + allocVal(engine); + QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *ms) { + if (engine->memoryManager->gcStateMachine->state <= GCState::HandleQObjectWrappers) + return; + obj->mark(ms); + }); + *val = obj; +} + void WeakValue::allocVal(ExecutionEngine *engine) { val = engine->memoryManager->m_weakValues->allocate(); diff --git a/src/qml/jsruntime/qv4persistent_p.h b/src/qml/jsruntime/qv4persistent_p.h index 55e8eefcb7..d0e29a166e 100644 --- a/src/qml/jsruntime/qv4persistent_p.h +++ b/src/qml/jsruntime/qv4persistent_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4PERSISTENT_H #define QV4PERSISTENT_H @@ -63,7 +27,11 @@ struct Q_QML_EXPORT PersistentValueStorage ~PersistentValueStorage(); Value *allocate(); - static void free(Value *e); + static void free(Value *v) + { + if (v) + freeUnchecked(v); + } void mark(MarkStack *markStack); @@ -83,23 +51,32 @@ struct Q_QML_EXPORT PersistentValueStorage Iterator begin() { return Iterator(firstPage, 0); } Iterator end() { return Iterator(nullptr, 0); } - static ExecutionEngine *getEngine(Value *v); + void clearFreePageHint(); + + static ExecutionEngine *getEngine(const Value *v); ExecutionEngine *engine; void *firstPage; + void *freePageHint = nullptr; private: + static void freeUnchecked(Value *v); static void freePage(void *page); }; class Q_QML_EXPORT PersistentValue { public: - PersistentValue() {} + constexpr PersistentValue() noexcept = default; PersistentValue(const PersistentValue &other); PersistentValue &operator=(const PersistentValue &other); + + PersistentValue(PersistentValue &&other) noexcept : val(std::exchange(other.val, nullptr)) {} + void swap(PersistentValue &other) noexcept { qt_ptr_swap(val, other.val); } + QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(PersistentValue) + ~PersistentValue() { PersistentValueStorage::free(val); } + PersistentValue &operator=(const WeakValue &other); PersistentValue &operator=(Object *object); - ~PersistentValue(); PersistentValue(ExecutionEngine *engine, const Value &value); PersistentValue(ExecutionEngine *engine, ReturnedValue value); @@ -154,26 +131,11 @@ public: WeakValue &operator=(const WeakValue &other); ~WeakValue(); - void set(ExecutionEngine *engine, const Value &value) - { - if (!val) - allocVal(engine); - *val = value; - } + void set(ExecutionEngine *engine, const Value &value); - void set(ExecutionEngine *engine, ReturnedValue value) - { - if (!val) - allocVal(engine); - *val = value; - } + void set(ExecutionEngine *engine, ReturnedValue value); - void set(ExecutionEngine *engine, Heap::Base *obj) - { - if (!val) - allocVal(engine); - *val = obj; - } + void set(ExecutionEngine *engine, Heap::Base *obj); ReturnedValue value() const { return (val ? val->asReturnedValue() : Encode::undefined()); diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp index 26e1074fe3..df63d4bdf7 100644 --- a/src/qml/jsruntime/qv4profiling.cpp +++ b/src/qml/jsruntime/qv4profiling.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4profiling_p.h" #include <private/qv4mm_p.h> @@ -50,8 +14,8 @@ FunctionLocation FunctionCall::resolveLocation() const { return FunctionLocation(m_function->name()->toQString(), m_function->executableCompilationUnit()->fileName(), - m_function->compiledFunction->location.line, - m_function->compiledFunction->location.column); + m_function->compiledFunction->location.line(), + m_function->compiledFunction->location.column()); } FunctionCallProperties FunctionCall::properties() const @@ -96,9 +60,10 @@ void Profiler::reportData() FunctionLocationHash locations; properties.reserve(m_data.size()); - for (const FunctionCall &call : qAsConst(m_data)) { + for (const FunctionCall &call : std::as_const(m_data)) { properties.append(call.properties()); Function *function = call.function(); + Q_ASSERT(function); SentMarker &marker = m_sentLocations[reinterpret_cast<quintptr>(function)]; if (!marker.isValid()) { FunctionLocation &location = locations[properties.constLast().id]; @@ -123,10 +88,10 @@ void Profiler::startProfiling(quint64 features) (qint64)m_engine->memoryManager->getLargeItemsMem(), HeapPage}; m_memory_data.append(heap); - MemoryAllocationProperties small = {timestamp, + MemoryAllocationProperties smallP = {timestamp, (qint64)m_engine->memoryManager->getUsedMem(), SmallItem}; - m_memory_data.append(small); + m_memory_data.append(smallP); MemoryAllocationProperties large = {timestamp, (qint64)m_engine->memoryManager->getLargeItemsMem(), LargeItem}; diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index ccf7c9210d..d1e8e34ba3 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4PROFILING_H #define QV4PROFILING_H @@ -51,7 +15,7 @@ // We mean it. // -#include "qv4global_p.h" +#include <QtQml/private/qv4global_p.h> #include "qv4engine_p.h" #include "qv4function_p.h" @@ -59,8 +23,8 @@ #if !QT_CONFIG(qml_debug) -#define Q_V4_PROFILE_ALLOC(engine, size, type) (!engine) -#define Q_V4_PROFILE_DEALLOC(engine, size, type) (!engine) +#define Q_V4_PROFILE_ALLOC(engine, size, type) Q_UNUSED(engine) +#define Q_V4_PROFILE_DEALLOC(engine, size, type) Q_UNUSED(engine) QT_BEGIN_NAMESPACE @@ -138,9 +102,7 @@ struct MemoryAllocationProperties { class FunctionCall { public: - - FunctionCall() : m_function(nullptr), m_start(0), m_end(0) - { Q_ASSERT_X(false, Q_FUNC_INFO, "Cannot construct a function call without function"); } + FunctionCall() : m_function(nullptr), m_start(0), m_end(0) {} FunctionCall(Function *function, qint64 start, qint64 end) : m_function(function), m_start(start), m_end(end) @@ -150,13 +112,24 @@ public: m_function(other.m_function), m_start(other.m_start), m_end(other.m_end) { m_function->executableCompilationUnit()->addref(); } + FunctionCall(FunctionCall &&other) noexcept + : m_function(std::exchange(other.m_function, nullptr)) + , m_start(std::exchange(other.m_start, 0)) + , m_end(std::exchange(other.m_end, 0)) + {} + ~FunctionCall() - { m_function->executableCompilationUnit()->release(); } + { + if (m_function) + m_function->executableCompilationUnit()->release(); + } FunctionCall &operator=(const FunctionCall &other) { if (&other != this) { - other.m_function->executableCompilationUnit()->addref(); - m_function->executableCompilationUnit()->release(); + if (other.m_function) + other.m_function->executableCompilationUnit()->addref(); + if (m_function) + m_function->executableCompilationUnit()->release(); m_function = other.m_function; m_start = other.m_start; m_end = other.m_end; @@ -164,6 +137,15 @@ public: return *this; } + QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(FunctionCall) + + void swap(FunctionCall &other) noexcept + { + qt_ptr_swap(m_function, other.m_function); + std::swap(m_start, other.m_start); + std::swap(m_end, other.m_end); + } + Function *function() const { return m_function; @@ -254,7 +236,7 @@ public: void reportData(); void setTimer(const QElapsedTimer &timer) { m_timer = timer; } -signals: +Q_SIGNALS: void dataReady(const QV4::Profiling::FunctionLocationHash &, const QVector<QV4::Profiling::FunctionCallProperties> &, const QVector<QV4::Profiling::MemoryAllocationProperties> &); @@ -276,7 +258,6 @@ public: // It's enough to ref() the function in the destructor as it will probably not disappear while // it's executing ... FunctionCallProfiler(ExecutionEngine *engine, Function *f) - : profiler(nullptr) { Profiler *p = engine->profiler(); if (Q_UNLIKELY(p) && (p->featuresEnabled & (1 << Profiling::FeatureFunctionCall))) { @@ -292,20 +273,20 @@ public: profiler->m_data.append(FunctionCall(function, startTime, profiler->m_timer.nsecsElapsed())); } - Profiler *profiler; - Function *function; - qint64 startTime; + Profiler *profiler = nullptr; + Function *function = nullptr; + qint64 startTime = 0; }; } // namespace Profiling } // namespace QV4 -Q_DECLARE_TYPEINFO(QV4::Profiling::MemoryAllocationProperties, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCallProperties, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCall, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionLocation, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(QV4::Profiling::Profiler::SentMarker, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QV4::Profiling::MemoryAllocationProperties, Q_RELOCATABLE_TYPE); +Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCallProperties, Q_RELOCATABLE_TYPE); +Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCall, Q_RELOCATABLE_TYPE); +Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionLocation, Q_RELOCATABLE_TYPE); +Q_DECLARE_TYPEINFO(QV4::Profiling::Profiler::SentMarker, Q_RELOCATABLE_TYPE); QT_END_NAMESPACE Q_DECLARE_METATYPE(QV4::Profiling::FunctionLocationHash) diff --git a/src/qml/jsruntime/qv4promiseobject.cpp b/src/qml/jsruntime/qv4promiseobject.cpp index 17d218a6eb..b8ede3e578 100644 --- a/src/qml/jsruntime/qv4promiseobject.cpp +++ b/src/qml/jsruntime/qv4promiseobject.cpp @@ -1,47 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QCoreApplication> #include <private/qv4promiseobject_p.h> #include <private/qv4symbol_p.h> #include "qv4jscall_p.h" +QT_BEGIN_NAMESPACE + using namespace QV4; using namespace QV4::Promise; @@ -81,7 +47,6 @@ void dropException(QV4::ExecutionEngine* e) } } -QT_BEGIN_NAMESPACE namespace QV4 { namespace Promise { @@ -114,7 +79,6 @@ struct ResolveThenableEvent : public QEvent } // namespace Promise } // namespace QV4 -QT_END_NAMESPACE ReactionHandler::ReactionHandler(QObject *parent) : QObject(parent) @@ -227,17 +191,17 @@ public: void ReactionHandler::executeResolveThenable(ResolveThenableEvent *event) { Scope scope(event->then.engine()); - JSCallData jsCallData(scope, 2); + JSCallArguments jsCallData(scope, 2); PromiseObject *promise = event->promise.as<PromiseObject>(); ScopedFunctionObject resolve {scope, FunctionBuilder::makeResolveFunction(scope.engine, promise->d())}; ScopedFunctionObject reject {scope, FunctionBuilder::makeRejectFunction(scope.engine, promise->d())}; - jsCallData->args[0] = resolve; + jsCallData.args[0] = resolve; jsCallData.args[1] = reject; - jsCallData->thisObject = event->thenable.as<QV4::Object>(); + jsCallData.thisObject = event->thenable.as<QV4::Object>(); event->then.as<const FunctionObject>()->call(jsCallData); - if (scope.engine->hasException) { - JSCallData rejectCallData(scope, 1); - rejectCallData->args[0] = scope.engine->catchException(); + if (scope.hasException()) { + JSCallArguments rejectCallData(scope, 1); + rejectCallData.args[0] = scope.engine->catchException(); Scoped<RejectWrapper> reject {scope, scope.engine->memoryManager->allocate<QV4::RejectWrapper>()}; reject->call(rejectCallData); } @@ -350,9 +314,9 @@ void Heap::PromiseReaction::triggerWithValue(ExecutionEngine *e, const Value *va handler->addReaction(e, reaction, value); } -void Heap::PromiseCtor::init(QV4::ExecutionContext *scope) +void Heap::PromiseCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("Promise")); + Heap::FunctionObject::init(engine, QStringLiteral("Promise")); } void Heap::PromiseObject::init(ExecutionEngine *e) @@ -428,7 +392,7 @@ ReturnedValue PromiseCtor::virtualCallAsConstructor(const FunctionObject *f, con THROW_TYPE_ERROR(); // throw a TypeError exception Scoped<PromiseObject> a(scope, scope.engine->newPromiseObject()); - if (scope.engine->hasException) + if (scope.hasException()) return Encode::undefined(); a->d()->state = Heap::PromiseObject::Pending; //4. Set promise.[[PromiseState]] to "pending" @@ -440,16 +404,16 @@ ReturnedValue PromiseCtor::virtualCallAsConstructor(const FunctionObject *f, con ScopedFunctionObject resolve(scope, FunctionBuilder::makeResolveFunction(scope.engine, a->d())); ScopedFunctionObject reject(scope, FunctionBuilder::makeRejectFunction(scope.engine, a->d())); - JSCallData jsCallData(scope, 2); - jsCallData->args[0] = resolve; - jsCallData->args[1] = reject; - //jsCallData->thisObject = a; VERIFY corretness, but this should be undefined (see below) + JSCallArguments jsCallData(scope, 2); + jsCallData.args[0] = resolve; + jsCallData.args[1] = reject; + //jsCallData.thisObject = a; VERIFY corretness, but this should be undefined (see below) executor->call(jsCallData); // 9. Let completion be Call(executor, undefined, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »). - if (scope.engine->hasException) { + if (scope.hasException()) { ScopedValue exception {scope, scope.engine->catchException()}; - JSCallData callData {scope, 1}; + JSCallArguments callData {scope, 1}; callData.args[0] = exception; reject->call(callData); } @@ -611,15 +575,17 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this } if (!doneValue->toBoolean()) - completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(e, iteratorObject); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); } ScopedObject nextPromise(scope, Value::fromReturnedValue(resolve->call(thisObject, nextValue, 1))); - if (!nextPromise || scope.hasException()) { - ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue)); + if (scope.hasException() || !nextPromise) { + ScopedValue completion(scope, doneValue->toBoolean() + ? Encode::undefined() + : Runtime::IteratorClose::call(e, iteratorObject)); if (scope.hasException()) { completion = e->exceptionValue->asReturnedValue(); dropException(e); @@ -641,7 +607,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this } if (!doneValue->toBoolean()) - completion = Runtime::IteratorClose::call(scope.engine, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(scope.engine, iteratorObject); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); @@ -649,10 +615,10 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this ScopedFunctionObject resolveElement(scope, FunctionBuilder::makeResolveElementFunction(e, index, executionState->d())); - JSCallData jsCallData(scope, 2); - jsCallData->args[0] = resolveElement; - jsCallData->args[1] = reject; - jsCallData->thisObject = nextPromise; + JSCallArguments jsCallData(scope, 2); + jsCallData.args[0] = resolveElement; + jsCallData.args[1] = reject; + jsCallData.thisObject = nextPromise; then->call(jsCallData); if (scope.hasException()) { @@ -660,7 +626,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this dropException(e); if (!doneValue->toBoolean()) - completion = Runtime::IteratorClose::call(scope.engine, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(scope.engine, iteratorObject); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); @@ -722,7 +688,9 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi doneValue = Value::fromReturnedValue(Runtime::IteratorNext::call(e, iteratorObject, nextValue)); if (scope.hasException()) { - ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue)); + ScopedValue completion(scope, doneValue->toBoolean() + ? Encode::undefined() + : Runtime::IteratorClose::call(e, iteratorObject)); if (scope.hasException()) { completion = e->exceptionValue->asReturnedValue(); dropException(e); @@ -757,15 +725,17 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi } if (!doneValue->toBoolean()) - completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(e, iteratorObject); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); } ScopedObject nextPromise(scope, Value::fromReturnedValue(resolve->call(thisObject, nextValue, 1))); - if (!nextPromise || scope.hasException()) { - ScopedValue completion(scope, Runtime::IteratorClose::call(e, iteratorObject, doneValue)); + if (scope.hasException() || !nextPromise) { + ScopedValue completion(scope, doneValue->toBoolean() + ? Encode::undefined() + : Runtime::IteratorClose::call(e, iteratorObject)); if (scope.hasException()) { completion = e->exceptionValue->asReturnedValue(); dropException(e); @@ -785,7 +755,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi } if (!doneValue->toBoolean()) - completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(e, iteratorObject); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); @@ -793,10 +763,10 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi ScopedFunctionObject resolveOriginalPromise(scope, capability->d()->resolve); - JSCallData jsCallData(scope, 2); - jsCallData->args[0] = resolveOriginalPromise; - jsCallData->args[1] = reject; - jsCallData->thisObject = nextPromise; + JSCallArguments jsCallData(scope, 2); + jsCallData.args[0] = resolveOriginalPromise; + jsCallData.args[1] = reject; + jsCallData.thisObject = nextPromise; then->call(jsCallData); if (scope.hasException()) { @@ -804,7 +774,7 @@ ReturnedValue PromiseCtor::method_race(const FunctionObject *f, const Value *thi dropException(e); if (!doneValue->toBoolean()) - completion = Runtime::IteratorClose::call(e, iteratorObject, doneValue); + completion = Runtime::IteratorClose::call(e, iteratorObject); reject->call(newPromise, completion, 1); return newPromise.asReturnedValue(); @@ -927,10 +897,10 @@ ReturnedValue PromisePrototype::method_catch(const FunctionObject *f, const Valu onRejected = argv[0]; } - JSCallData jsCallData(scope, 2); - jsCallData->args[0] = Encode::undefined(); - jsCallData->args[1] = onRejected; - jsCallData->thisObject = promise; + JSCallArguments jsCallData(scope, 2); + jsCallData.args[0] = Encode::undefined(); + jsCallData.args[1] = onRejected; + jsCallData.thisObject = promise; ScopedString thenName(scope, scope.engine->newIdentifier(QStringLiteral("then"))); ScopedFunctionObject then(scope, promise->get(thenName)); @@ -1032,7 +1002,7 @@ ReturnedValue ResolveWrapper::virtualCall(const FunctionObject *f, const Value * // 8. Let then be Get(resolution, then) ScopedFunctionObject thenAction { scope, resolutionObject->get(thenName)}; // 9. If then is an abrupt completion, then - if (scope.engine->hasException) { + if (scope.hasException()) { // Return RecjectPromise(promise, then.[[Value]] ScopedValue thenValue {scope, scope.engine->catchException()}; promise->d()->setState(Heap::PromiseObject::Rejected); @@ -1084,13 +1054,17 @@ ReturnedValue RejectWrapper::virtualCall(const FunctionObject *f, const Value *t ScopedString thenName(scope, scope.engine->newIdentifier(QStringLiteral("catch"))); ScopedFunctionObject then(scope, promise->get(thenName)); - JSCallData jsCallData(scope, 2); - jsCallData->args[0] = *f; - jsCallData->args[1] = Encode::undefined(); - jsCallData->thisObject = value; + JSCallArguments jsCallData(scope, 2); + jsCallData.args[0] = *f; + jsCallData.args[1] = Encode::undefined(); + jsCallData.thisObject = value; then->call(jsCallData); } return Encode::undefined(); } + +QT_END_NAMESPACE + +#include "moc_qv4promiseobject_p.cpp" diff --git a/src/qml/jsruntime/qv4promiseobject_p.h b/src/qml/jsruntime/qv4promiseobject_p.h index 8a3724e07d..fbde9f95e0 100644 --- a/src/qml/jsruntime/qv4promiseobject_p.h +++ b/src/qml/jsruntime/qv4promiseobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4PROMISEOBJECT_H #define QV4PROMISEOBJECT_H @@ -70,7 +34,7 @@ class ReactionHandler : public QObject public: ReactionHandler(QObject *parent = nullptr); - virtual ~ReactionHandler() override; + ~ReactionHandler() override; void addReaction(ExecutionEngine *e, const Value *reaction, const Value *value); void addResolveThenable(ExecutionEngine *e, const PromiseObject *promise, const Object *thenable, const FunctionObject *then); @@ -86,7 +50,7 @@ protected: namespace Heap { struct PromiseCtor : FunctionObject { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; #define PromiseObjectMembers(class, Member) \ @@ -217,7 +181,7 @@ struct PromiseExecutionState : Object V4_OBJECT2(PromiseExecutionState, Object) }; -struct Q_QML_PRIVATE_EXPORT PromiseObject : Object +struct Q_QML_EXPORT PromiseObject : Object { V4_OBJECT2(PromiseObject, Object) V4_NEEDS_DESTROY diff --git a/src/qml/jsruntime/qv4property_p.h b/src/qml/jsruntime/qv4property_p.h index 555f323737..e7ad6b58d6 100644 --- a/src/qml/jsruntime/qv4property_p.h +++ b/src/qml/jsruntime/qv4property_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4PROPERTYDESCRIPTOR_H #define QV4PROPERTYDESCRIPTOR_H @@ -209,8 +173,6 @@ struct PropertyIndex { } -Q_DECLARE_TYPEINFO(QV4::Property, Q_MOVABLE_TYPE); - QT_END_NAMESPACE #endif diff --git a/src/qml/jsruntime/qv4propertykey.cpp b/src/qml/jsruntime/qv4propertykey.cpp index 064d030b83..65dd7e7fc1 100644 --- a/src/qml/jsruntime/qv4propertykey.cpp +++ b/src/qml/jsruntime/qv4propertykey.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4propertykey_p.h" @@ -43,12 +7,15 @@ #include <qv4string_p.h> #include <qv4engine_p.h> #include <qv4scopedvalue_p.h> +#include <private/qv4mm_p.h> + +using namespace Qt::Literals::StringLiterals; QV4::Heap::StringOrSymbol *QV4::PropertyKey::toStringOrSymbol(QV4::ExecutionEngine *e) { if (isArrayIndex()) return Value::fromUInt32(asArrayIndex()).toString(e); - return static_cast<Heap::StringOrSymbol *>(asStringOrSymbol()); + return asStringOrSymbol(); } bool QV4::PropertyKey::isString() const { @@ -57,7 +24,7 @@ bool QV4::PropertyKey::isString() const { } bool QV4::PropertyKey::isSymbol() const { - Heap::Base *s = asStringOrSymbol(); + Heap::StringOrSymbol *s = asStringOrSymbol(); return s && !s->internalClass->vtable->isString && s->internalClass->vtable->isStringOrSymbol; } @@ -92,9 +59,9 @@ QV4::Heap::String *QV4::PropertyKey::asFunctionName(ExecutionEngine *engine, Fun { QString n; if (prefix == Getter) - n = QStringLiteral("get "); + n += "get "_L1; else if (prefix == Setter) - n = QStringLiteral("set "); + n += "set "_L1; if (isArrayIndex()) n += QString::number(asArrayIndex()); else { @@ -102,8 +69,8 @@ QV4::Heap::String *QV4::PropertyKey::asFunctionName(ExecutionEngine *engine, Fun QString str = s->toQString(); if (s->internalClass->vtable->isString) n += s->toQString(); - else if (str.length() > 1) - n += QChar::fromLatin1('[') + str.midRef(1) + QChar::fromLatin1(']'); + else if (str.size() > 1) + n += QChar::fromLatin1('[') + QStringView{str}.mid(1) + QChar::fromLatin1(']'); } return engine->newString(n); } diff --git a/src/qml/jsruntime/qv4propertykey_p.h b/src/qml/jsruntime/qv4propertykey_p.h index b2a2ec3dea..f3b05ee0d8 100644 --- a/src/qml/jsruntime/qv4propertykey_p.h +++ b/src/qml/jsruntime/qv4propertykey_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4PROPERTYKEY_H #define QV4PROPERTYKEY_H @@ -50,7 +14,10 @@ // We mean it. // +#include <private/qv4writebarrier_p.h> #include <private/qv4global_p.h> +#include <private/qv4staticvalue_p.h> +#include <QtCore/qhashfunctions.h> QT_BEGIN_NAMESPACE @@ -71,68 +38,74 @@ private: // * If the key is a Symbol it simply points to the referenced symbol object // * if the key is an array index (a uint < UINT_MAX), it's encoded as an // integer value - quint64 val; + QV4::StaticValue val; - // Important: Always keep this in sync with the definitions for Integers and heap objects in Value - static const quint64 ArrayIndexMask = 0x3800000000000ull; - enum { - IsManagedOrUndefined_Shift = 64-15, - }; - inline bool isManaged() const { return (val >> IsManagedOrUndefined_Shift) == 0; } - inline quint32 value() const { return val & quint64(~quint32(0)); } + inline bool isManaged() const { return val.isManaged(); } + inline quint32 value() const { return val.value(); } -#if QT_POINTER_SIZE == 8 - QML_NEARLY_ALWAYS_INLINE Heap::StringOrSymbol *m() const +public: + static PropertyKey invalid() { - Heap::StringOrSymbol *b; - memcpy(&b, &val, 8); - return b; + PropertyKey key; + key.val = StaticValue::undefinedValue(); + return key; } - QML_NEARLY_ALWAYS_INLINE void setM(Heap::StringOrSymbol *b) + + static PropertyKey fromArrayIndex(uint idx) { - memcpy(&val, &b, 8); + PropertyKey key; + key.val.setInt_32(idx); + return key; } -#elif QT_POINTER_SIZE == 4 - QML_NEARLY_ALWAYS_INLINE Heap::StringOrSymbol *m() const + + bool isStringOrSymbol() const { return isManaged(); } + uint asArrayIndex() const { - Q_STATIC_ASSERT(sizeof(Heap::StringOrSymbol*) == sizeof(quint32)); - Heap::StringOrSymbol *b; - quint32 v = value(); - memcpy(&b, &v, 4); - return b; + Q_ASSERT(isArrayIndex()); + return value(); } - QML_NEARLY_ALWAYS_INLINE void setM(Heap::StringOrSymbol *b) + + bool isArrayIndex() const { return val.isInteger(); } + bool isValid() const { return !val.isUndefined(); } + + // We cannot #include the declaration of Heap::StringOrSymbol here. + // Therefore we do some gymnastics to enforce the type safety. + + template<typename StringOrSymbol = Heap::StringOrSymbol, typename Engine = QV4::EngineBase> + static PropertyKey fromStringOrSymbol(Engine *engine, StringOrSymbol *b) { - quint32 v; - memcpy(&v, &b, 4); - val = v; + static_assert(std::is_base_of_v<Heap::StringOrSymbol, StringOrSymbol>); + PropertyKey key; + QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *stack) { + if constexpr (QV4::WriteBarrier::isInsertionBarrier) { + // treat this as an insertion - the StringOrSymbol becomes reachable + // via the propertykey, so we consequently need to mark it durnig gc + b->mark(stack); + } + }); + key.val.setM(b); + Q_ASSERT(key.isManaged()); + return key; } -#endif -public: - static PropertyKey invalid() { PropertyKey key; key.val = 0; return key; } - static PropertyKey fromArrayIndex(uint idx) { PropertyKey key; key.val = ArrayIndexMask | static_cast<quint64>(idx); return key; } - bool isStringOrSymbol() const { return isManaged() && val != 0; } - uint asArrayIndex() const { Q_ASSERT(isArrayIndex()); return static_cast<uint>(val & 0xffffffff); } - uint isArrayIndex() const { return !isManaged() && val != 0; } - bool isValid() const { return val != 0; } - static PropertyKey fromStringOrSymbol(Heap::StringOrSymbol *b) - { PropertyKey key; key.setM(b); return key; } - Heap::StringOrSymbol *asStringOrSymbol() const { + template<typename StringOrSymbol = Heap::StringOrSymbol> + StringOrSymbol *asStringOrSymbol() const + { + static_assert(std::is_base_of_v<Heap::StringOrSymbol, StringOrSymbol>); if (!isManaged()) return nullptr; - return m(); + return static_cast<StringOrSymbol *>(val.m()); } Q_QML_EXPORT bool isString() const; - bool isSymbol() const; + Q_QML_EXPORT bool isSymbol() const; bool isCanonicalNumericIndexString() const; Q_QML_EXPORT QString toQString() const; Heap::StringOrSymbol *toStringOrSymbol(ExecutionEngine *e); - quint64 id() const { return val; } + quint64 id() const { return val._val; } static PropertyKey fromId(quint64 id) { - PropertyKey key; key.val = id; return key; + PropertyKey key; key.val._val = id; return key; } enum FunctionNamePrefix { @@ -142,9 +115,10 @@ public: }; Heap::String *asFunctionName(ExecutionEngine *e, FunctionNamePrefix prefix) const; - bool operator ==(const PropertyKey &other) const { return val == other.val; } - bool operator !=(const PropertyKey &other) const { return val != other.val; } - bool operator <(const PropertyKey &other) const { return val < other.val; } + bool operator ==(const PropertyKey &other) const { return val._val == other.val._val; } + bool operator !=(const PropertyKey &other) const { return val._val != other.val._val; } + bool operator <(const PropertyKey &other) const { return val._val < other.val._val; } + friend size_t qHash(const PropertyKey &key, size_t seed = 0) { return qHash(key.val._val, seed); } }; } diff --git a/src/qml/jsruntime/qv4proxy.cpp b/src/qml/jsruntime/qv4proxy.cpp index 9325e2e53b..974788d420 100644 --- a/src/qml/jsruntime/qv4proxy.cpp +++ b/src/qml/jsruntime/qv4proxy.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4proxy_p.h" @@ -49,6 +13,7 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(ProxyObject); DEFINE_OBJECT_VTABLE(ProxyFunctionObject); +DEFINE_OBJECT_VTABLE(ProxyConstructorObject); void Heap::ProxyObject::init(const QV4::Object *target, const QV4::Object *handler) { @@ -61,12 +26,9 @@ void Heap::ProxyObject::init(const QV4::Object *target, const QV4::Object *handl void Heap::ProxyFunctionObject::init(const QV4::FunctionObject *target, const QV4::Object *handler) { ExecutionEngine *e = internalClass->engine; - FunctionObject::init(e->rootContext()); + FunctionObject::init(e); this->target.set(e, target->d()); this->handler.set(e, handler->d()); - - if (!target->isConstructor()) - jsConstruct = nullptr; } @@ -90,12 +52,15 @@ ReturnedValue ProxyObject::virtualGet(const Managed *m, PropertyKey id, const Va if (hasProperty) *hasProperty = true; - JSCallData cdata(scope, 3, nullptr, handler); - cdata.args[0] = target; - cdata.args[1] = id.toStringOrSymbol(scope.engine); - cdata.args[2] = *receiver; + Value *args = scope.alloc(3); + args[0] = target; + args[1] = id.toStringOrSymbol(scope.engine); + args[2] = *receiver; + JSCallData cdata(handler, args, 3); ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (scope.hasException()) + return Encode::undefined(); ScopedProperty targetDesc(scope); PropertyAttributes attributes = target->getOwnProperty(id, targetDesc); if (attributes != Attr_Invalid && !attributes.isConfigurable()) { @@ -129,14 +94,15 @@ bool ProxyObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Val if (!trap->isFunctionObject()) return scope.engine->throwTypeError(); - JSCallData cdata(scope, 4, nullptr, handler); - cdata.args[0] = target; - cdata.args[1] = id.toStringOrSymbol(scope.engine); - cdata.args[2] = value; - cdata.args[3] = *receiver; + Value *args = scope.alloc(4); + args[0] = target; + args[1] = id.toStringOrSymbol(scope.engine); + args[2] = value; + args[3] = *receiver; + JSCallData cdata(handler, args, 4); ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); - if (!trapResult->toBoolean()) + if (scope.hasException() || !trapResult->toBoolean()) return false; ScopedProperty targetDesc(scope); PropertyAttributes attributes = target->getOwnProperty(id, targetDesc); @@ -170,13 +136,14 @@ bool ProxyObject::virtualDeleteProperty(Managed *m, PropertyKey id) if (!trap->isFunctionObject()) return scope.engine->throwTypeError(); - JSCallData cdata(scope, 3, nullptr, handler); - cdata.args[0] = target; - cdata.args[1] = id.toStringOrSymbol(scope.engine); - cdata.args[2] = o->d(); // ### fix receiver handling + Value *args = scope.alloc(3); + args[0] = target; + args[1] = id.toStringOrSymbol(scope.engine); + args[2] = o->d(); // ### fix receiver handling + JSCallData cdata(handler, args, 3); ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); - if (!trapResult->toBoolean()) + if (scope.hasException() || !trapResult->toBoolean()) return false; ScopedProperty targetDesc(scope); PropertyAttributes attributes = target->getOwnProperty(id, targetDesc); @@ -206,11 +173,14 @@ bool ProxyObject::virtualHasProperty(const Managed *m, PropertyKey id) if (!trap->isFunctionObject()) return scope.engine->throwTypeError(); - JSCallData cdata(scope, 2, nullptr, handler); - cdata.args[0] = target; - cdata.args[1] = id.isArrayIndex() ? Value::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol(); + Value *args = scope.alloc(2); + args[0] = target; + args[1] = id.isArrayIndex() ? Value::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol(); + JSCallData cdata(handler, args, 2); ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (scope.hasException()) + return false; bool result = trapResult->toBoolean(); if (!result) { ScopedProperty targetDesc(scope); @@ -246,11 +216,14 @@ PropertyAttributes ProxyObject::virtualGetOwnProperty(const Managed *m, Property return Attr_Invalid; } - JSCallData cdata(scope, 2, nullptr, handler); - cdata.args[0] = target; - cdata.args[1] = id.isArrayIndex() ? Value::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol(); + Value *args = scope.alloc(2); + args[0] = target; + args[1] = id.isArrayIndex() ? Value::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol(); + JSCallData cdata(handler, args, 2); ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (scope.hasException()) + return Attr_Invalid; if (!trapResult->isObject() && !trapResult->isUndefined()) { scope.engine->throwTypeError(); return Attr_Invalid; @@ -259,9 +232,9 @@ PropertyAttributes ProxyObject::virtualGetOwnProperty(const Managed *m, Property ScopedProperty targetDesc(scope); PropertyAttributes targetAttributes = target->getOwnProperty(id, targetDesc); if (trapResult->isUndefined()) { - p->value = Encode::undefined(); - if (targetAttributes == Attr_Invalid) { + if (p) p->value = Encode::undefined(); + if (targetAttributes == Attr_Invalid) { return Attr_Invalid; } if (!targetAttributes.isConfigurable() || !target->isExtensible()) { @@ -289,8 +262,10 @@ PropertyAttributes ProxyObject::virtualGetOwnProperty(const Managed *m, Property } } - p->value = resultDesc->value; - p->set = resultDesc->set; + if (p) { + p->value = resultDesc->value; + p->set = resultDesc->set; + } return resultAttributes; } @@ -317,13 +292,14 @@ bool ProxyObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Pro return false; } - JSCallData cdata(scope, 3, nullptr, handler); - cdata.args[0] = target; - cdata.args[1] = id.isArrayIndex() ? Value::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol(); - cdata.args[2] = ObjectPrototype::fromPropertyDescriptor(scope.engine, p, attrs); + Value *args = scope.alloc(3); + args[0] = target; + args[1] = id.isArrayIndex() ? Value::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol(); + args[2] = ObjectPrototype::fromPropertyDescriptor(scope.engine, p, attrs); + JSCallData cdata(handler, args, 3); ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); - bool result = trapResult->toBoolean(); + bool result = !scope.hasException() && trapResult->toBoolean(); if (!result) return false; @@ -369,10 +345,13 @@ bool ProxyObject::virtualIsExtensible(const Managed *m) if (!trap->isFunctionObject()) return scope.engine->throwTypeError(); - JSCallData cdata(scope, 1, nullptr, handler); - cdata.args[0] = target; + Value *args = scope.alloc(1); + args[0] = target; + JSCallData cdata(handler, args, 1); ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (scope.hasException()) + return false; bool result = trapResult->toBoolean(); if (result != target->isExtensible()) { scope.engine->throwTypeError(); @@ -400,10 +379,13 @@ bool ProxyObject::virtualPreventExtensions(Managed *m) if (!trap->isFunctionObject()) return scope.engine->throwTypeError(); - JSCallData cdata(scope, 1, nullptr, handler); - cdata.args[0] = target; + Value *args = scope.alloc(1); + args[0] = target; + JSCallData cdata(handler, args, 1); ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (scope.hasException()) + return false; bool result = trapResult->toBoolean(); if (result && target->isExtensible()) { scope.engine->throwTypeError(); @@ -435,10 +417,13 @@ Heap::Object *ProxyObject::virtualGetPrototypeOf(const Managed *m) return nullptr; } - JSCallData cdata(scope, 1, nullptr, handler); - cdata.args[0] = target; + Value *args = scope.alloc(1); + args[0] = target; + JSCallData cdata(handler, args, 1); ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (scope.hasException()) + return nullptr; if (!trapResult->isNull() && !trapResult->isObject()) { scope.engine->throwTypeError(); return nullptr; @@ -477,12 +462,13 @@ bool ProxyObject::virtualSetPrototypeOf(Managed *m, const Object *p) return false; } - JSCallData cdata(scope, 2, nullptr, handler); - cdata.args[0] = target; - cdata.args[1] = p ? p->asReturnedValue() : Encode::null(); + Value *args = scope.alloc(2); + args[0] = target; + args[1] = p ? p->asReturnedValue() : Encode::null(); + JSCallData cdata(handler, args, 2); ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); - bool result = trapResult->toBoolean(); + bool result = !scope.hasException() && trapResult->toBoolean(); if (!result) return false; if (!target->isExtensible()) { @@ -515,7 +501,7 @@ ProxyObjectOwnPropertyKeyIterator::ProxyObjectOwnPropertyKeyIterator(ArrayObject PropertyKey ProxyObjectOwnPropertyKeyIterator::next(const Object *m, Property *pd, PropertyAttributes *attrs) { - if (index >= len) + if (index >= len || m == nullptr) return PropertyKey::invalid(); Scope scope(m); @@ -570,9 +556,12 @@ OwnPropertyKeyIterator *ProxyObject::virtualOwnPropertyKeys(const Object *m, Val return nullptr; } - JSCallData cdata(scope, 1, nullptr, handler); - cdata.args[0] = target; + Value *args = scope.alloc(1); + args[0] = target; + JSCallData cdata(handler, args, 1); ScopedObject trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (scope.hasException()) + return nullptr; if (!trapResult) { scope.engine->throwTypeError(); return nullptr; @@ -583,7 +572,7 @@ OwnPropertyKeyIterator *ProxyObject::virtualOwnPropertyKeys(const Object *m, Val ScopedStringOrSymbol key(scope); for (uint i = 0; i < len; ++i) { key = trapResult->get(i); - if (scope.engine->hasException) + if (scope.hasException()) return nullptr; if (!key) { scope.engine->throwTypeError(); @@ -608,8 +597,10 @@ OwnPropertyKeyIterator *ProxyObject::virtualOwnPropertyKeys(const Object *m, Val else targetNonConfigurableKeys->push_back(keyAsValue); } - if (target->isExtensible() && targetNonConfigurableKeys->getLength() == 0) + if (target->isExtensible() && targetNonConfigurableKeys->getLength() == 0) { + *iteratorTarget = *m; return new ProxyObjectOwnPropertyKeyIterator(trapKeys); + } ScopedArrayObject uncheckedResultKeys(scope, scope.engine->newArrayObject()); uncheckedResultKeys->copyArrayData(trapKeys); @@ -623,8 +614,10 @@ OwnPropertyKeyIterator *ProxyObject::virtualOwnPropertyKeys(const Object *m, Val } } - if (target->isExtensible()) + if (target->isExtensible()) { + *iteratorTarget = *m; return new ProxyObjectOwnPropertyKeyIterator(trapKeys); + } len = targetConfigurableKeys->getLength(); for (uint i = 0; i < len; ++i) { @@ -648,7 +641,8 @@ OwnPropertyKeyIterator *ProxyObject::virtualOwnPropertyKeys(const Object *m, Val } -ReturnedValue ProxyFunctionObject::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) +ReturnedValue ProxyConstructorObject::virtualCallAsConstructor( + const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { Scope scope(f); const ProxyObject *o = static_cast<const ProxyObject *>(f); @@ -663,10 +657,8 @@ ReturnedValue ProxyFunctionObject::virtualCallAsConstructor(const FunctionObject if (scope.hasException()) return Encode::undefined(); - if (trap->isNullOrUndefined()) { - Q_ASSERT(target->isConstructor()); + if (trap->isNullOrUndefined()) return target->callAsConstructor(argv, argc, newTarget); - } if (!trap->isFunctionObject()) return scope.engine->throwTypeError(); @@ -699,7 +691,7 @@ ReturnedValue ProxyFunctionObject::virtualCall(const FunctionObject *f, const Va if (scope.hasException()) return Encode::undefined(); if (trap->isNullOrUndefined()) - return target->call(thisObject, argv, argc); + return checkedResult(scope.engine, target->call(thisObject, argv, argc)); if (!trap->isFunctionObject()) return scope.engine->throwTypeError(); @@ -713,11 +705,11 @@ ReturnedValue ProxyFunctionObject::virtualCall(const FunctionObject *f, const Va DEFINE_OBJECT_VTABLE(Proxy); -void Heap::Proxy::init(QV4::ExecutionContext *ctx) +void Heap::Proxy::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(ctx, QStringLiteral("Proxy")); + Heap::FunctionObject::init(engine, QStringLiteral("Proxy")); - Scope scope(ctx); + Scope scope(engine); Scoped<QV4::Proxy> ctor(scope, this); ctor->defineDefaultProperty(QStringLiteral("revocable"), QV4::Proxy::method_revocable, 2); ctor->defineReadonlyConfigurableProperty(scope.engine->id_length(), Value::fromInt32(2)); @@ -739,9 +731,18 @@ ReturnedValue Proxy::virtualCallAsConstructor(const FunctionObject *f, const Val return scope.engine->throwTypeError(); const FunctionObject *targetFunction = target->as<FunctionObject>(); - if (targetFunction) - return scope.engine->memoryManager->allocate<ProxyFunctionObject>(targetFunction, handler)->asReturnedValue(); - return scope.engine->memoryManager->allocate<ProxyObject>(target, handler)->asReturnedValue(); + if (!targetFunction) { + return scope.engine->memoryManager->allocate<ProxyObject>(target, handler) + ->asReturnedValue(); + } + + if (targetFunction->isConstructor()) { + return scope.engine->memoryManager->allocate<ProxyConstructorObject>( + targetFunction, handler)->asReturnedValue(); + } + + return scope.engine->memoryManager->allocate<ProxyFunctionObject>(targetFunction, handler) + ->asReturnedValue(); } ReturnedValue Proxy::virtualCall(const FunctionObject *f, const Value *, const Value *, int) @@ -758,7 +759,10 @@ ReturnedValue Proxy::method_revocable(const FunctionObject *f, const Value *, co Q_ASSERT(proxy); ScopedString revoke(scope, scope.engine->newString(QStringLiteral("revoke"))); - ScopedFunctionObject revoker(scope, scope.engine->memoryManager->allocate<FunctionObject>(scope.engine->rootContext(), nullptr, method_revoke)); + ScopedFunctionObject revoker( + scope, + scope.engine->memoryManager->allocate<DynamicFunctionObject>( + scope.engine, nullptr, method_revoke)); revoker->defineReadonlyConfigurableProperty(scope.engine->id_length(), Value::fromInt32(0)); revoker->defineDefaultProperty(scope.engine->symbol_revokableProxy(), proxy); diff --git a/src/qml/jsruntime/qv4proxy_p.h b/src/qml/jsruntime/qv4proxy_p.h index 2ccb50ece6..b3ce9c7a96 100644 --- a/src/qml/jsruntime/qv4proxy_p.h +++ b/src/qml/jsruntime/qv4proxy_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4PROXY_P_H #define QV4PROXY_P_H @@ -73,13 +37,15 @@ struct ProxyFunctionObject : ProxyObject { void init(const QV4::FunctionObject *target, const QV4::Object *handler); }; +struct ProxyConstructorObject : ProxyFunctionObject {}; + #define ProxyMembers(class, Member) \ Member(class, Pointer, Symbol *, revokableProxySymbol) \ DECLARE_HEAP_OBJECT(Proxy, FunctionObject) { DECLARE_MARKOBJECTS(Proxy) - void init(QV4::ExecutionContext *ctx); + void init(ExecutionEngine *engine); }; } @@ -96,9 +62,6 @@ struct ProxyObject : FunctionObject { V4_OBJECT2(ProxyObject, Object) Q_MANAGED_TYPE(ProxyObject) V4_INTERNALCLASS(ProxyObject) - enum { - IsFunctionObject = false - }; static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); @@ -117,14 +80,16 @@ struct ProxyFunctionObject : ProxyObject { V4_OBJECT2(ProxyFunctionObject, FunctionObject) Q_MANAGED_TYPE(ProxyObject) V4_INTERNALCLASS(ProxyFunctionObject) - enum { - IsFunctionObject = true - }; - static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; +struct ProxyConstructorObject : ProxyFunctionObject { + V4_OBJECT2(ProxyConstructorObject, ProxyFunctionObject) + + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); +}; + struct Proxy : FunctionObject { V4_OBJECT2(Proxy, FunctionObject) diff --git a/src/qml/jsruntime/qv4qmetaobjectwrapper.cpp b/src/qml/jsruntime/qv4qmetaobjectwrapper.cpp new file mode 100644 index 0000000000..6521c98dbf --- /dev/null +++ b/src/qml/jsruntime/qv4qmetaobjectwrapper.cpp @@ -0,0 +1,111 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qv4qmetaobjectwrapper_p.h" + +#include <private/qqmlobjectorgadget_p.h> +#include <private/qv4jscall_p.h> +#include <private/qv4qobjectwrapper_p.h> + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +namespace QV4 { + +void Heap::QMetaObjectWrapper::init(const QMetaObject *metaObject) +{ + FunctionObject::init(); + m_metaObject = metaObject; +} + +void Heap::QMetaObjectWrapper::destroy() +{ + delete[] m_constructors; + FunctionObject::destroy(); +} + +ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) { + + Scope scope(engine); + Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocate<QMetaObjectWrapper>(metaObject)->asReturnedValue()); + mo->init(engine); + return mo->asReturnedValue(); +} + +void QMetaObjectWrapper::init(ExecutionEngine *) { + const QMetaObject &mo = *d()->metaObject(); + + for (int i = 0; i < mo.enumeratorCount(); i++) { + QMetaEnum Enum = mo.enumerator(i); + for (int k = 0; k < Enum.keyCount(); k++) { + const char* key = Enum.key(k); + const int value = Enum.value(k); + defineReadonlyProperty(QLatin1String(key), Value::fromInt32(value)); + } + } +} + +ReturnedValue QMetaObjectWrapper::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +{ + Q_ASSERT(f->as<QMetaObjectWrapper>()); + return construct(static_cast<const QMetaObjectWrapper*>(f)->d(), argv, argc); +} + +ReturnedValue QMetaObjectWrapper::constructInternal( + const QMetaObject *mo, const QQmlPropertyData *constructors, Heap::FunctionObject *d, + const Value *argv, int argc) +{ + ExecutionEngine *v4 = d->internalClass->engine; + + if (!constructors) { + return v4->throwTypeError(QLatin1String(mo->className()) + + QLatin1String(" has no invokable constructor")); + } + + Scope scope(v4); + ScopedObject object(scope); + JSCallData cData(nullptr, argv, argc); + CallData *callData = cData.callData(scope); + + const QQmlObjectOrGadget objectOrGadget(mo); + + const auto callType = [](QMetaType metaType) { + return metaType.flags() & QMetaType::PointerToQObject + ? QMetaObject::CreateInstance + : QMetaObject::ConstructInPlace; + }; + + const int constructorCount = mo->constructorCount(); + if (constructorCount == 1) { + object = QObjectMethod::callPrecise( + objectOrGadget, constructors[0], v4, callData, + callType(constructors[0].propType())); + } else if (const QQmlPropertyData *ctor = QObjectMethod::resolveOverloaded( + objectOrGadget, constructors, constructorCount, v4, callData)) { + object = QObjectMethod::callPrecise( + objectOrGadget, *ctor, v4, callData, callType(ctor->propType())); + } + + if (object) { + Scoped<FunctionObject> functionObject(scope, d); + object->defineDefaultProperty(v4->id_constructor(), functionObject); + object->setPrototypeOf(functionObject); + } + + return object.asReturnedValue(); +} + +bool QMetaObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b) +{ + const QMetaObjectWrapper *aMetaObject = a->as<QMetaObjectWrapper>(); + Q_ASSERT(aMetaObject); + const QMetaObjectWrapper *bMetaObject = b->as<QMetaObjectWrapper>(); + return bMetaObject && aMetaObject->metaObject() == bMetaObject->metaObject(); +} + +DEFINE_OBJECT_VTABLE(QMetaObjectWrapper); + +} // namespace QV4 + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4qmetaobjectwrapper_p.h b/src/qml/jsruntime/qv4qmetaobjectwrapper_p.h new file mode 100644 index 0000000000..c44b18f291 --- /dev/null +++ b/src/qml/jsruntime/qv4qmetaobjectwrapper_p.h @@ -0,0 +1,120 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QV4QMETAOBJECTWRAPPER_P_H +#define QV4QMETAOBJECTWRAPPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qv4functionobject_p.h> +#include <private/qv4value_p.h> + +#include <QtCore/qmetaobject.h> + +QT_BEGIN_NAMESPACE + +class QQmlPropertyData; + +namespace QV4 { +namespace Heap { + +struct QMetaObjectWrapper : FunctionObject +{ + void init(const QMetaObject *metaObject); + void destroy(); + + const QMetaObject *metaObject() const { return m_metaObject; } + QMetaType metaType() const + { + const QMetaType type = m_metaObject->metaType(); + if (type.flags() & QMetaType::IsGadget) + return type; + + // QObject* is our best guess because we can't get from a metatype to + // the metatype of its pointer. + return QMetaType::fromType<QObject *>(); + } + + const QQmlPropertyData *ensureConstructorsCache( + const QMetaObject *metaObject, QMetaType metaType) + { + Q_ASSERT(metaObject); + if (!m_constructors) + m_constructors = createConstructors(metaObject, metaType); + return m_constructors; + } + + + static const QQmlPropertyData *createConstructors( + const QMetaObject *metaObject, QMetaType metaType) + { + Q_ASSERT(metaObject); + const int count = metaObject->constructorCount(); + if (count == 0) + return nullptr; + + QQmlPropertyData *constructors = new QQmlPropertyData[count]; + + for (int i = 0; i < count; ++i) { + QMetaMethod method = metaObject->constructor(i); + QQmlPropertyData &d = constructors[i]; + d.load(method); + d.setPropType(metaType); + d.setCoreIndex(i); + } + + return constructors; + } + +private: + const QMetaObject *m_metaObject; + const QQmlPropertyData *m_constructors; +}; + +} // namespace Heap + +struct Q_QML_EXPORT QMetaObjectWrapper : public FunctionObject +{ + V4_OBJECT2(QMetaObjectWrapper, FunctionObject) + V4_NEEDS_DESTROY + + static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject); + const QMetaObject *metaObject() const { return d()->metaObject(); } + + template<typename HeapObject> + ReturnedValue static construct(HeapObject *d, const Value *argv, int argc) + { + const QMetaObject *mo = d->metaObject(); + return constructInternal( + mo, d->ensureConstructorsCache(mo, d->metaType()), d, argv, argc); + } + +protected: + static ReturnedValue virtualCallAsConstructor( + const FunctionObject *, const Value *argv, int argc, const Value *); + static bool virtualIsEqualTo(Managed *a, Managed *b); + +private: + void init(ExecutionEngine *engine); + + static ReturnedValue constructInternal( + const QMetaObject *mo, const QQmlPropertyData *constructors, Heap::FunctionObject *d, + const Value *argv, int argc); +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4QMETAOBJECTWRAPPER_P_H + + diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index e2d3b98ff6..e1e882cb06 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -1,97 +1,64 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4qmlcontext_p.h" -#include <private/qqmlengine_p.h> +#include <private/qjsvalue_p.h> #include <private/qqmlcontext_p.h> - +#include <private/qqmlengine_p.h> +#include <private/qqmlglobal_p.h> +#include <private/qqmljavascriptexpression_p.h> +#include <private/qqmllistwrapper_p.h> +#include <private/qqmltypewrapper_p.h> +#include <private/qv4compileddata_p.h> #include <private/qv4engine_p.h> -#include <private/qv4value_p.h> -#include <private/qv4objectproto_p.h> -#include <private/qv4mm_p.h> #include <private/qv4function_p.h> -#include <private/qv4compileddata_p.h> -#include <private/qqmltypewrapper_p.h> -#include <private/qqmllistwrapper_p.h> -#include <private/qqmljavascriptexpression_p.h> -#include <private/qjsvalue_p.h> -#include <private/qv4qobjectwrapper_p.h> -#include <private/qv4module_p.h> -#include <private/qv4lookup_p.h> #include <private/qv4identifiertable_p.h> +#include <private/qv4lookup_p.h> +#include <private/qv4mm_p.h> +#include <private/qv4module_p.h> +#include <private/qv4objectproto_p.h> +#include <private/qv4qobjectwrapper_p.h> +#include <private/qv4stackframe_p.h> +#include <private/qv4value_p.h> + +#include <QtCore/qloggingcategory.h> QT_BEGIN_NAMESPACE +Q_STATIC_LOGGING_CATEGORY(lcQmlContext, "qt.qml.context"); + using namespace QV4; DEFINE_OBJECT_VTABLE(QQmlContextWrapper); DEFINE_MANAGED_VTABLE(QmlContext); -void Heap::QQmlContextWrapper::init(QQmlContextData *context, QObject *scopeObject) +void Heap::QQmlContextWrapper::init(QQmlRefPointer<QQmlContextData> context, QObject *scopeObject) { Object::init(); - this->context = new QQmlContextDataRef(context); + this->context = context.take(); this->scopeObject.init(scopeObject); } void Heap::QQmlContextWrapper::destroy() { - delete context; + context->release(); + context = nullptr; scopeObject.destroy(); Object::destroy(); } -static OptionalReturnedValue searchContextProperties(QV4::ExecutionEngine *v4, QQmlContextData *context, String *name, - bool *hasProperty, Value *base, QV4::Lookup *lookup, - QV4::Lookup *originalLookup, QQmlEnginePrivate *ep) +static OptionalReturnedValue searchContextProperties( + QV4::ExecutionEngine *v4, const QQmlRefPointer<QQmlContextData> &context, String *name, + bool *hasProperty, Value *base, QV4::Lookup *lookup, QV4::Lookup *originalLookup, + QQmlEnginePrivate *ep) { - const QV4::IdentifierHash &properties = context->propertyNames(); - if (properties.count() == 0) - return OptionalReturnedValue(); - - const int propertyIdx = properties.value(name); + const int propertyIdx = context->propertyIndex(name); if (propertyIdx == -1) return OptionalReturnedValue(); - if (propertyIdx < context->idValueCount) { + if (propertyIdx < context->numIdValues()) { if (hasProperty) *hasProperty = true; @@ -104,25 +71,44 @@ static OptionalReturnedValue searchContextProperties(QV4::ExecutionEngine *v4, Q } if (ep->propertyCapture) - ep->propertyCapture->captureProperty(&context->idValues[propertyIdx].bindings); - return OptionalReturnedValue(QV4::QObjectWrapper::wrap(v4, context->idValues[propertyIdx])); + ep->propertyCapture->captureProperty(context->idValueBindings(propertyIdx)); + return OptionalReturnedValue(QV4::QObjectWrapper::wrap(v4, context->idValue(propertyIdx))); } QQmlContextPrivate *cp = context->asQQmlContextPrivate(); if (ep->propertyCapture) - ep->propertyCapture->captureProperty(context->asQQmlContext(), -1, propertyIdx + cp->notifyIndex); + ep->propertyCapture->captureProperty(context->asQQmlContext(), -1, propertyIdx + cp->notifyIndex()); - const QVariant &value = cp->propertyValues.at(propertyIdx); + const QVariant &value = cp->propertyValue(propertyIdx); if (hasProperty) *hasProperty = true; if (value.userType() == qMetaTypeId<QList<QObject*> >()) { QQmlListProperty<QObject> prop(context->asQQmlContext(), (void*) qintptr(propertyIdx), QQmlContextPrivate::context_count, QQmlContextPrivate::context_at); - return OptionalReturnedValue(QmlListWrapper::create(v4, prop, qMetaTypeId<QQmlListProperty<QObject> >())); + return OptionalReturnedValue(QmlListWrapper::create(v4, prop, QMetaType::fromType<QQmlListProperty<QObject> >())); + } + return OptionalReturnedValue(v4->fromVariant(cp->propertyValue(propertyIdx))); +} + +template<typename Lookup> +bool performLookup(ScopedValue *result, bool *hasProperty, const Lookup &lookup) { + bool hasProp = false; + *result = lookup(&hasProp); + if (hasProp) { + if (hasProperty) + *hasProperty = hasProp; + return true; } - return OptionalReturnedValue(v4->fromVariant(cp->propertyValues.at(propertyIdx))); + return false; +} + +static QV4::QObjectWrapper::Flags getQmlPropertyFlags(const Lookup *l) +{ + return (l && l->forCall) + ? QV4::QObjectWrapper::CheckRevision + : (QV4::QObjectWrapper::CheckRevision | QV4::QObjectWrapper::AttachMethods); } ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, bool *hasProperty, Value *base, Lookup *lookup) @@ -133,7 +119,7 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r QV4::ExecutionEngine *v4 = resource->engine(); QV4::Scope scope(v4); - if (v4->callingQmlContext() != *resource->d()->context) { + if (v4->callingQmlContext().data() != resource->d()->context) { if (resource->d()->module) { Scoped<Module> module(scope, resource->d()->module); bool hasProp = false; @@ -148,18 +134,12 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r return Object::virtualGet(resource, id, receiver, hasProperty); } - bool hasProp = false; - ScopedValue result(scope, Object::virtualGet(resource, id, receiver, &hasProp)); - if (hasProp) { - if (hasProperty) - *hasProperty = hasProp; - return result->asReturnedValue(); - } + ScopedValue result(scope); // It's possible we could delay the calculation of the "actual" context (in the case // of sub contexts) until it is definitely needed. - QQmlContextData *context = resource->getContext(); - QQmlContextData *expressionContext = context; + QQmlRefPointer<QQmlContextData> context = resource->getContext(); + QQmlRefPointer<QQmlContextData> expressionContext = context; if (!context) { if (hasProperty) @@ -179,17 +159,20 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r ScopedString name(scope, id.asStringOrSymbol()); - const auto performGobalLookUp = [&result, v4, &name, hasProperty]() { - bool hasProp = false; - result = v4->globalObject->get(name, &hasProp); - if (hasProp) { - if (hasProperty) - *hasProperty = hasProp; - return true; - } - return false; + const auto globalLookup = [v4, &name](bool *hasProp) { + return v4->globalObject->get(name, hasProp); + }; + + const auto jsLookup = [resource, &id, receiver](bool *hasProp) { + return Object::virtualGet(resource, id, receiver, hasProp); }; + const bool isJSContext = context->isJSContext(); + + // Do the generic JS lookup early in case of a JavaScript context. + if (isJSContext && performLookup(&result, hasProperty, jsLookup)) + return result->asReturnedValue(); + // If the scope object is a QAbstractDynamicMetaObject, then QMetaObject::indexOfProperty // will call createProperty() on the QADMO and implicitly create the property. While that // is questionable behavior, there are two use-cases that we support in the light of this: @@ -204,16 +187,21 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r // // Note: The scope object is only a QADMO for example when somebody registers a QQmlPropertyMap // sub-class as QML type and then instantiates it in .qml. - if (scopeObject && QQmlPropertyCache::isDynamicMetaObject(scopeObject->metaObject())) { + const QMetaObjectPrivate *metaObjectPrivate = scopeObject + ? reinterpret_cast<const QMetaObjectPrivate *>(scopeObject->metaObject()->d.data) + : nullptr; + if (metaObjectPrivate && metaObjectPrivate->flags & DynamicMetaObject) { // all bets are off, so don't try to optimize any lookups lookup = nullptr; - if (performGobalLookUp()) + if (performLookup(&result, hasProperty, globalLookup)) return result->asReturnedValue(); } - if (context->imports && name->startsWithUpper()) { + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(v4->qmlEngine()); + if (context->imports() && (name->startsWithUpper() || context->valueTypesAreAddressable())) { // Search for attached properties, enums and imported scripts - QQmlTypeNameCache::Result r = context->imports->query(name, QQmlImport::AllowRecursion); + QQmlTypeNameCache::Result r = context->imports()->query<QQmlImport::AllowRecursion>( + name, QQmlTypeLoader::get(ep)); if (r.isValid()) { if (hasProperty) @@ -224,36 +212,46 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScript; return lookup->qmlContextPropertyGetter(lookup, v4, base); } - QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); + QV4::ScopedObject scripts(scope, context->importedScripts().valueRef()); if (scripts) return scripts->get(r.scriptIndex); return QV4::Encode::null(); } else if (r.type.isValid()) { if (lookup) { + bool isValueSingleton = false; if (r.type.isSingleton()) { QQmlEnginePrivate *e = QQmlEnginePrivate::get(v4->qmlEngine()); if (r.type.isQObjectSingleton() || r.type.isCompositeSingleton()) { e->singletonInstance<QObject*>(r.type); - lookup->qmlContextSingletonLookup.singleton = - static_cast<Heap::Object*>( + lookup->qmlContextSingletonLookup.singletonObject.set(v4, Value::fromReturnedValue( QQmlTypeWrapper::create(v4, nullptr, r.type) ).heapObject()); } else { QJSValue singleton = e->singletonInstance<QJSValue>(r.type); - QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, singleton)); - lookup->qmlContextSingletonLookup.singleton = o->d(); + + // QSrting values should already have been put on the engine heap at this point + // to manage their memory. We later assume this has already happened. + Q_ASSERT(!QJSValuePrivate::asQString(&singleton)); + + if (QV4::Value *val = QJSValuePrivate::takeManagedValue(&singleton)) { + lookup->qmlContextSingletonLookup.singletonObject.set(v4, val->heapObject()); + } else { + lookup->qmlContextSingletonLookup.singletonValue = QJSValuePrivate::asReturnedValue(&singleton); + isValueSingleton = true; + } } - lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupSingleton; + lookup->qmlContextPropertyGetter = isValueSingleton ? QQmlContextWrapper::lookupValueSingleton + : QQmlContextWrapper::lookupSingleton; return lookup->qmlContextPropertyGetter(lookup, v4, base); } } result = QQmlTypeWrapper::create(v4, scopeObject, r.type); } else if (r.importNamespace) { - result = QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace); + result = QQmlTypeWrapper::create(v4, scopeObject, context->imports(), r.importNamespace); } if (lookup) { - lookup->qmlTypeLookup.qmlTypeWrapper = static_cast<Heap::Object*>(result->heapObject()); + lookup->qmlTypeLookup.qmlTypeWrapper.set(v4, result->heapObject()); lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupType; } return result->asReturnedValue(); @@ -262,44 +260,79 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r // Fall through } - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(v4->qmlEngine()); Lookup * const originalLookup = lookup; decltype(lookup->qmlContextPropertyGetter) contextGetterFunction = QQmlContextWrapper::lookupContextObjectProperty; // minor optimization so we don't potentially try two property lookups on the same object - if (scopeObject == context->contextObject) { + if (scopeObject == context->contextObject()) { scopeObject = nullptr; contextGetterFunction = QQmlContextWrapper::lookupScopeObjectProperty; } + QQmlRefPointer<QQmlContextData> outer = context; while (context) { - if (auto property = searchContextProperties(v4, context, name, hasProperty, base, lookup, originalLookup, ep)) - return *property; + if (outer == context) { + if (auto property = searchContextProperties( + v4, context, name, hasProperty, base, lookup, originalLookup, ep)) { + return *property; + } + + outer = outer->parent(); + + if (const auto cu = context->typeCompilationUnit(); cu && cu->componentsAreBound()) { + // If components are bound in this CU, we can search the whole context hierarchy + // of the file. Bound components' contexts override their local properties. + // You also can't instantiate bound components outside of their creation + // context. Therefore this is safe. + + for (; + outer && outer->typeCompilationUnit() == cu; + outer = outer->parent()) { + if (auto property = searchContextProperties( + v4, outer, name, hasProperty, base, + nullptr, originalLookup, ep)) { + return *property; + } + } + } + } // Search scope object if (scopeObject) { bool hasProp = false; - QQmlPropertyData *propertyData = nullptr; - QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty(v4, context, scopeObject, - name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData)); + const QQmlPropertyData *propertyData = nullptr; + + QV4::ScopedObject wrapper(scope, QV4::QObjectWrapper::wrap(v4, scopeObject)); + QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty( + v4, context, wrapper->d(), scopeObject, name, + getQmlPropertyFlags(lookup), &hasProp, &propertyData)); if (hasProp) { if (hasProperty) *hasProperty = true; if (base) - *base = QV4::QObjectWrapper::wrap(v4, scopeObject); + *base = wrapper; if (lookup && propertyData) { QQmlData *ddata = QQmlData::get(scopeObject, false); if (ddata && ddata->propertyCache) { - ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, scopeObject))); - const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue()); - lookup->qobjectLookup.ic = That->internalClass(); - lookup->qobjectLookup.propertyCache = ddata->propertyCache; - lookup->qobjectLookup.propertyCache->addref(); - lookup->qobjectLookup.propertyData = propertyData; - lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScopeObjectProperty; + ScopedValue val( + scope, + base ? *base : Value::fromReturnedValue( + QV4::QObjectWrapper::wrap(v4, scopeObject))); + if (QObjectMethod *method = result->as<QObjectMethod>()) { + QV4::setupQObjectMethodLookup( + lookup, ddata, propertyData, val->objectValue(), + method->d()); + lookup->qmlContextPropertyGetter + = QQmlContextWrapper::lookupScopeObjectMethod; + } else { + QV4::setupQObjectLookup( + lookup, ddata, propertyData, val->objectValue()); + lookup->qmlContextPropertyGetter + = QQmlContextWrapper::lookupScopeObjectProperty; + } } } @@ -310,28 +343,41 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r // Search context object - if (context->contextObject) { + if (QObject *contextObject = context->contextObject()) { bool hasProp = false; - QQmlPropertyData *propertyData = nullptr; - result = QV4::QObjectWrapper::getQmlProperty(v4, context, context->contextObject, - name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData); + const QQmlPropertyData *propertyData = nullptr; + QV4::ScopedObject wrapper(scope, QV4::QObjectWrapper::wrap(v4, contextObject)); + result = QV4::QObjectWrapper::getQmlProperty( + v4, context, wrapper->d(), contextObject, name, getQmlPropertyFlags(lookup), + &hasProp, &propertyData); if (hasProp) { if (hasProperty) *hasProperty = true; if (base) - *base = QV4::QObjectWrapper::wrap(v4, context->contextObject); + *base = wrapper; if (propertyData) { if (lookup) { - QQmlData *ddata = QQmlData::get(context->contextObject, false); - if (ddata && ddata->propertyCache) { - ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, context->contextObject))); - const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue()); - lookup->qobjectLookup.ic = That->internalClass(); - lookup->qobjectLookup.propertyCache = ddata->propertyCache; - lookup->qobjectLookup.propertyCache->addref(); - lookup->qobjectLookup.propertyData = propertyData; - lookup->qmlContextPropertyGetter = contextGetterFunction; + QQmlData *ddata = QQmlData::get(contextObject, false); + if (ddata && ddata->propertyCache + && lookup->qmlContextPropertyGetter != contextGetterFunction) { + ScopedValue val( + scope, + base ? *base : Value::fromReturnedValue( + QV4::QObjectWrapper::wrap(v4, contextObject))); + if (QObjectMethod *method = result->as<QObjectMethod>()) { + setupQObjectMethodLookup( + lookup, ddata, propertyData, val->objectValue(), + method->d()); + if (contextGetterFunction == lookupScopeObjectProperty) + lookup->qmlContextPropertyGetter = lookupScopeObjectMethod; + else + lookup->qmlContextPropertyGetter = lookupContextObjectMethod; + } else { + setupQObjectLookup( + lookup, ddata, propertyData, val->objectValue()); + lookup->qmlContextPropertyGetter = contextGetterFunction; + } } } else if (originalLookup) { originalLookup->qmlContextPropertyGetter = lookupInParentContextHierarchy; @@ -342,13 +388,18 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r } } - context = context->parent; + context = context->parent(); // As the hierarchy of contexts is not stable, we can't do accelerated lookups beyond // the immediate QML context (of the .qml file). lookup = nullptr; } + // Do the generic JS lookup late in case of a non-JavaScript context. + // The scope, context, types etc should be able to override it. + if (!isJSContext && performLookup(&result, hasProperty, jsLookup)) + return result->asReturnedValue(); + // Do a lookup in the global object here to avoid expressionContext->unresolvedNames becoming // true if we access properties of the global object. if (originalLookup) { @@ -366,11 +417,11 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r } lookup->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; } else { - if (performGobalLookUp()) + if (performLookup(&result, hasProperty, globalLookup)) return result->asReturnedValue(); } - expressionContext->unresolvedNames = true; + expressionContext->setUnresolvedNames(true); return Encode::undefined(); } @@ -402,8 +453,8 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val // It's possible we could delay the calculation of the "actual" context (in the case // of sub contexts) until it is definitely needed. - QQmlContextData *context = wrapper->getContext(); - QQmlContextData *expressionContext = context; + QQmlRefPointer<QQmlContextData> context = wrapper->getContext(); + QQmlRefPointer<QQmlContextData> expressionContext = context; if (!context) return false; @@ -414,17 +465,13 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val ScopedString name(scope, id.asStringOrSymbol()); while (context) { - const QV4::IdentifierHash &properties = context->propertyNames(); // Search context properties - if (properties.count()) { - const int propertyIndex = properties.value(name); - if (propertyIndex != -1) { - if (propertyIndex < context->idValueCount) { - v4->throwError(QLatin1String("left-hand side of assignment operator is not an lvalue")); - return false; - } + if (const int propertyIndex = context->propertyIndex(name); propertyIndex != -1) { + if (propertyIndex < context->numIdValues()) { + v4->throwError(QLatin1String("left-hand side of assignment operator is not an lvalue")); return false; } + return false; } // Search scope object @@ -434,14 +481,15 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val scopeObject = nullptr; // Search context object - if (context->contextObject && - QV4::QObjectWrapper::setQmlProperty(v4, context, context->contextObject, name, QV4::QObjectWrapper::CheckRevision, value)) + if (context->contextObject() && + QV4::QObjectWrapper::setQmlProperty(v4, context, context->contextObject(), name, + QV4::QObjectWrapper::CheckRevision, value)) return true; - context = context->parent; + context = context->parent(); } - expressionContext->unresolvedNames = true; + expressionContext->setUnresolvedNames(true); QString error = QLatin1String("Invalid write to global property \"") + name->toQString() + QLatin1Char('"'); @@ -452,8 +500,9 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base) { Scope scope(engine); - PropertyKey name =engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit-> - runtimeStrings[l->nameIndex]); + auto *func = engine->currentStackFrame->v4Function; + ScopedPropertyKey name(scope, engine->identifierTable->asPropertyKey( + func->compilationUnit->runtimeStrings[l->nameIndex])); // Special hack for bounded signal expressions, where the parameters of signals are injected // into the handler expression through the locals of the call context. So for onClicked: { ... } @@ -462,13 +511,26 @@ ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup * for (Heap::ExecutionContext *ctx = engine->currentContext()->d(); ctx; ctx = ctx->outer) { if (ctx->type == Heap::ExecutionContext::Type_CallContext) { const uint index = ctx->internalClass->indexOfValueOrGetter(name); - if (index < std::numeric_limits<uint>::max()) + if (index < std::numeric_limits<uint>::max()) { + if (!func->detectedInjectedParameters) { + const auto location = func->sourceLocation(); + qCWarning(lcQmlContext).nospace().noquote() + << location.sourceFile << ":" << location.line << ":" << location.column + << " Parameter \"" << name->toQString() << "\" is not declared." + << " Injection of parameters into signal handlers is deprecated." + << " Use JavaScript functions with formal parameters instead."; + + // Don't warn over and over for the same function + func->detectedInjectedParameters = true; + } + return static_cast<Heap::CallContext *>(ctx)->locals[index].asReturnedValue(); + } } - // Skip only block contexts within the current call context. + // Skip only block and call contexts. // Other contexts need a regular QML property lookup. See below. - if (ctx->type != Heap::ExecutionContext::Type_BlockContext) + if (ctx->type != Heap::ExecutionContext::Type_BlockContext && ctx->type != Heap::ExecutionContext::Type_CallContext) break; } @@ -478,8 +540,8 @@ ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup * Scoped<QmlContext> callingQmlContext(scope, engine->qmlContext()); if (callingQmlContext) { Scoped<QQmlContextWrapper> qmlContextWrapper(scope, callingQmlContext->d()->qml()); - result = QQmlContextWrapper::getPropertyAndBase(qmlContextWrapper, name, /*receiver*/nullptr, &hasProperty, - base, l); + result = QQmlContextWrapper::getPropertyAndBase( + qmlContextWrapper, name, /*receiver*/nullptr, &hasProperty, base, l); } else { // Code path typical to worker scripts, compiled with lookups but no qml context. result = l->resolveGlobalGetter(engine); @@ -490,23 +552,23 @@ ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup * } } if (!hasProperty) - return engine->throwReferenceError(name.toQString()); + return engine->throwReferenceError(name->toQString()); return result->asReturnedValue(); } ReturnedValue QQmlContextWrapper::lookupScript(Lookup *l, ExecutionEngine *engine, Value *base) { - Q_UNUSED(base) + Q_UNUSED(base); Scope scope(engine); Scoped<QmlContext> qmlContext(scope, engine->qmlContext()); if (!qmlContext) return QV4::Encode::null(); - QQmlContextData *context = qmlContext->qmlContext(); + QQmlRefPointer<QQmlContextData> context = qmlContext->qmlContext(); if (!context) return QV4::Encode::null(); - QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); + QV4::ScopedObject scripts(scope, context->importedScripts().valueRef()); if (!scripts) return QV4::Encode::null(); return scripts->get(l->qmlContextScriptLookup.scriptIndex); @@ -514,20 +576,30 @@ ReturnedValue QQmlContextWrapper::lookupScript(Lookup *l, ExecutionEngine *engin ReturnedValue QQmlContextWrapper::lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base) { - Q_UNUSED(engine) - Q_UNUSED(base) - return Value::fromHeapObject(l->qmlContextSingletonLookup.singleton).asReturnedValue(); + Q_UNUSED(engine); + Q_UNUSED(base); + + return l->qmlContextSingletonLookup.singletonObject->asReturnedValue(); +} + +ReturnedValue QQmlContextWrapper::lookupValueSingleton(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Q_UNUSED(engine); + Q_UNUSED(base); + + Q_ASSERT(l->qmlContextSingletonLookup.singletonObject == nullptr); + return l->qmlContextSingletonLookup.singletonValue; } ReturnedValue QQmlContextWrapper::lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base) { - Q_UNUSED(base) + Q_UNUSED(base); Scope scope(engine); Scoped<QmlContext> qmlContext(scope, engine->qmlContext()); if (!qmlContext) return QV4::Encode::null(); - QQmlContextData *context = qmlContext->qmlContext(); + QQmlRefPointer<QQmlContextData> context = qmlContext->qmlContext(); if (!context) return QV4::Encode::null(); @@ -535,12 +607,35 @@ ReturnedValue QQmlContextWrapper::lookupIdObject(Lookup *l, ExecutionEngine *eng const int objectId = l->qmlContextIdObjectLookup.objectId; if (qmlEngine->propertyCapture) - qmlEngine->propertyCapture->captureProperty(&context->idValues[objectId].bindings); + qmlEngine->propertyCapture->captureProperty(context->idValueBindings(objectId)); - return QV4::QObjectWrapper::wrap(engine, context->idValues[objectId]); + return QV4::QObjectWrapper::wrap(engine, context->idValue(objectId)); } -ReturnedValue QQmlContextWrapper::lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base) +ReturnedValue QQmlContextWrapper::lookupIdObjectInParentContext( + Lookup *l, ExecutionEngine *engine, Value *base) +{ + return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base); +} + +static ReturnedValue revertObjectPropertyLookup(Lookup *l, ExecutionEngine *engine, Value *base) +{ + l->qobjectLookup.propertyCache->release(); + l->qobjectLookup.propertyCache = nullptr; + l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; + return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base); +} + +static ReturnedValue revertObjectMethodLookup(Lookup *l, ExecutionEngine *engine, Value *base) +{ + l->qobjectMethodLookup.propertyCache->release(); + l->qobjectMethodLookup.propertyCache = nullptr; + l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; + return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base); +} + +template<typename Call> +ReturnedValue callWithScopeObject(ExecutionEngine *engine, Value *base, Call c) { Scope scope(engine); Scoped<QmlContext> qmlContext(scope, engine->qmlContext()); @@ -554,52 +649,94 @@ ReturnedValue QQmlContextWrapper::lookupScopeObjectProperty(Lookup *l, Execution if (QQmlData::wasDeleted(scopeObject)) return QV4::Encode::undefined(); - const auto revertLookup = [l, engine, base]() { - l->qobjectLookup.propertyCache->release(); - l->qobjectLookup.propertyCache = nullptr; - l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; - return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base); - }; - ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, scopeObject)); if (base) *base = obj; - return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup); + return c(obj); } -ReturnedValue QQmlContextWrapper::lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base) +ReturnedValue QQmlContextWrapper::lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base) +{ + return callWithScopeObject(engine, base, [l, engine, base](const Value &obj) { + const QObjectWrapper::Flags flags = l->forCall + ? QObjectWrapper::NoFlag + : QObjectWrapper::AttachMethods; + return QObjectWrapper::lookupPropertyGetterImpl(l, engine, obj, flags, [l, engine, base]() { + return revertObjectPropertyLookup(l, engine, base); + }); + }); +} + +ReturnedValue QQmlContextWrapper::lookupScopeObjectMethod(Lookup *l, ExecutionEngine *engine, Value *base) +{ + return callWithScopeObject(engine, base, [l, engine, base](const Value &obj) { + const QObjectWrapper::Flags flags = l->forCall + ? QObjectWrapper::NoFlag + : QObjectWrapper::AttachMethods; + return QObjectWrapper::lookupMethodGetterImpl(l, engine, obj, flags, [l, engine, base]() { + return revertObjectMethodLookup(l, engine, base); + }); + }); +} + +template<typename Call> +ReturnedValue callWithContextObject(ExecutionEngine *engine, Value *base, Call c) { Scope scope(engine); Scoped<QmlContext> qmlContext(scope, engine->qmlContext()); if (!qmlContext) return QV4::Encode::undefined(); - QQmlContextData *context = qmlContext->qmlContext(); + QQmlRefPointer<QQmlContextData> context = qmlContext->qmlContext(); if (!context) return QV4::Encode::undefined(); - QObject *contextObject = context->contextObject; + QObject *contextObject = context->contextObject(); if (!contextObject) return QV4::Encode::undefined(); if (QQmlData::wasDeleted(contextObject)) return QV4::Encode::undefined(); - const auto revertLookup = [l, engine, base]() { - l->qobjectLookup.propertyCache->release(); - l->qobjectLookup.propertyCache = nullptr; - l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; - return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base); - }; - ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, contextObject)); if (base) *base = obj; - return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup); + return c(obj); +} + +ReturnedValue QQmlContextWrapper::lookupContextObjectProperty( + Lookup *l, ExecutionEngine *engine, Value *base) +{ + return callWithContextObject(engine, base, [l, engine, base](const Value &obj) { + const QObjectWrapper::Flags flags = l->forCall + ? QObjectWrapper::NoFlag + : QObjectWrapper::AttachMethods; + return QObjectWrapper::lookupPropertyGetterImpl(l, engine, obj, flags, [l, engine, base]() { + return revertObjectPropertyLookup(l, engine, base); + }); + }); +} + +ReturnedValue QQmlContextWrapper::lookupContextObjectMethod( + Lookup *l, ExecutionEngine *engine, Value *base) +{ + return callWithContextObject(engine, base, [l, engine, base](const Value &obj) { + const QObjectWrapper::Flags flags = l->forCall + ? QObjectWrapper::NoFlag + : QObjectWrapper::AttachMethods; + return QObjectWrapper::lookupMethodGetterImpl(l, engine, obj, flags, [l, engine, base]() { + return revertObjectMethodLookup(l, engine, base); + }); + }); +} + +ReturnedValue QQmlContextWrapper::lookupScopeFallbackProperty(Lookup *l, ExecutionEngine *engine, Value *base) +{ + return resolveQmlContextPropertyLookupGetter(l, engine, base); } ReturnedValue QQmlContextWrapper::lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base) @@ -621,11 +758,11 @@ ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, Exec if (!qmlContext) return QV4::Encode::undefined(); - QQmlContextData *context = qmlContext->qmlContext(); + QQmlRefPointer<QQmlContextData> context = qmlContext->qmlContext(); if (!context) return QV4::Encode::undefined(); - QQmlContextData *expressionContext = context; + QQmlRefPointer<QQmlContextData> expressionContext = context; QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->qmlEngine()); @@ -635,18 +772,20 @@ ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, Exec ScopedValue result(scope); - for (context = context->parent; context; context = context->parent) { + for (context = context->parent(); context; context = context->parent()) { if (auto property = searchContextProperties(engine, context, name, nullptr, base, nullptr, nullptr, ep)) return *property; // Search context object - if (context->contextObject) { + if (QObject *contextObject = context->contextObject()) { bool hasProp = false; - result = QV4::QObjectWrapper::getQmlProperty(engine, context, context->contextObject, - name, QV4::QObjectWrapper::CheckRevision, &hasProp); + QV4::ScopedObject wrapper(scope, QV4::QObjectWrapper::wrap(engine, contextObject)); + result = QV4::QObjectWrapper::getQmlProperty( + engine, context, wrapper->d(), contextObject, name, getQmlPropertyFlags(l), + &hasProp); if (hasProp) { if (base) - *base = QV4::QObjectWrapper::wrap(engine, context->contextObject); + *base = wrapper; return result->asReturnedValue(); } @@ -658,7 +797,7 @@ ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, Exec if (hasProp) return result->asReturnedValue(); - expressionContext->unresolvedNames = true; + expressionContext->setUnresolvedNames(true); return Encode::undefined(); } @@ -674,9 +813,9 @@ ReturnedValue QQmlContextWrapper::lookupType(Lookup *l, ExecutionEngine *engine, if (scopeObject && QQmlData::wasDeleted(scopeObject)) return QV4::Encode::undefined(); - Heap::Object *heapObject = l->qmlTypeLookup.qmlTypeWrapper; + Heap::Base *heapObject = l->qmlTypeLookup.qmlTypeWrapper; if (static_cast<Heap::QQmlTypeWrapper *>(heapObject)->object != scopeObject) { - l->qmlTypeLookup.qmlTypeWrapper = nullptr; + l->qmlTypeLookup.qmlTypeWrapper.clear(); l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base); } @@ -692,11 +831,15 @@ void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContex this->activation.set(internalClass->engine, qml->d()); } -Heap::QmlContext *QmlContext::create(ExecutionContext *parent, QQmlContextData *context, QObject *scopeObject) +Heap::QmlContext *QmlContext::create( + ExecutionContext *parent, QQmlRefPointer<QQmlContextData> context, + QObject *scopeObject) { Scope scope(parent); - Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocate<QQmlContextWrapper>(context, scopeObject)); + Scoped<QQmlContextWrapper> qml( + scope, scope.engine->memoryManager->allocate<QQmlContextWrapper>( + std::move(context), scopeObject)); Heap::QmlContext *c = scope.engine->memoryManager->alloc<QmlContext>(parent, qml); Q_ASSERT(c->vtable() == staticVTable()); return c; diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index e3e7239fe5..1b337d4c0e 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4QMLCONTEXT_P_H #define QV4QMLCONTEXT_P_H @@ -51,12 +15,12 @@ // We mean it. // -#include <QtCore/qglobal.h> +#include <private/qqmlcontextdata_p.h> #include <private/qtqmlglobal_p.h> - -#include <private/qv4object_p.h> #include <private/qv4context_p.h> -#include <private/qqmlcontext_p.h> +#include <private/qv4object_p.h> + +#include <QtCore/qglobal.h> QT_BEGIN_NAMESPACE @@ -70,19 +34,20 @@ namespace Heap { Member(class, Pointer, Module *, module) DECLARE_HEAP_OBJECT(QQmlContextWrapper, Object) { - DECLARE_MARKOBJECTS(QQmlContextWrapper); + DECLARE_MARKOBJECTS(QQmlContextWrapper) - void init(QQmlContextData *context, QObject *scopeObject); + void init(QQmlRefPointer<QQmlContextData> context, QObject *scopeObject); void destroy(); - QQmlContextDataRef *context; - QQmlQPointer<QObject> scopeObject; + // This has to be a plain pointer because object needs to be a POD type. + QQmlContextData *context; + QV4QPointer<QObject> scopeObject; }; #define QmlContextMembers(class, Member) DECLARE_HEAP_OBJECT(QmlContext, ExecutionContext) { - DECLARE_MARKOBJECTS(QmlContext); + DECLARE_MARKOBJECTS(QmlContext) QQmlContextWrapper *qml() { return static_cast<QQmlContextWrapper *>(activation.get()); } void init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml); @@ -97,7 +62,7 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object V4_INTERNALCLASS(QmlContextWrapper) inline QObject *getScopeObject() const { return d()->scopeObject; } - inline QQmlContextData *getContext() const { return *d()->context; } + inline QQmlRefPointer<QQmlContextData> getContext() const { return d()->context; } static ReturnedValue getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, bool *hasProperty, Value *base, Lookup *lookup = nullptr); @@ -107,9 +72,14 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object static ReturnedValue resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupScript(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupValueSingleton(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupIdObjectInParentContext(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupScopeObjectMethod(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupScopeFallbackProperty(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupContextObjectMethod(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupType(Lookup *l, ExecutionEngine *engine, Value *base); @@ -120,13 +90,16 @@ struct Q_QML_EXPORT QmlContext : public ExecutionContext V4_MANAGED(QmlContext, ExecutionContext) V4_INTERNALCLASS(QmlContext) - static Heap::QmlContext *create(QV4::ExecutionContext *parent, QQmlContextData *context, QObject *scopeObject); + static Heap::QmlContext *create( + QV4::ExecutionContext *parent, QQmlRefPointer<QQmlContextData> context, + QObject *scopeObject); QObject *qmlScope() const { return d()->qml()->scopeObject; } - QQmlContextData *qmlContext() const { - return *d()->qml()->context; + + QQmlRefPointer<QQmlContextData> qmlContext() const { + return d()->qml()->context; } }; diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 2b5e8bd2b9..b1d2b77a33 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -1,108 +1,74 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4qobjectwrapper_p.h" -#include <private/qqmlstaticmetaobject_p.h> -#include <private/qqmlengine_p.h> -#include <private/qqmlvmemetaobject_p.h> -#include <private/qqmlbinding_p.h> #include <private/qjsvalue_p.h> -#include <private/qqmlexpression_p.h> -#include <private/qqmlglobal_p.h> + +#include <private/qqmlbinding_p.h> +#include <private/qqmlbuiltinfunctions_p.h> +#include <private/qqmlengine_p.h> +#include <private/qqmlobjectorgadget_p.h> +#include <private/qqmlpropertybinding_p.h> +#include <private/qqmlscriptstring_p.h> +#include <private/qqmlsignalnames_p.h> #include <private/qqmltypewrapper_p.h> #include <private/qqmlvaluetypewrapper_p.h> -#include <private/qqmllistwrapper_p.h> -#include <private/qqmlbuiltinfunctions_p.h> +#include <private/qqmlvmemetaobject_p.h> #include <private/qv4arraybuffer_p.h> +#include <private/qv4compileddata_p.h> +#include <private/qv4dateobject_p.h> #include <private/qv4functionobject_p.h> -#include <private/qv4runtime_p.h> -#include <private/qv4variantobject_p.h> #include <private/qv4identifiertable_p.h> -#include <private/qv4lookup_p.h> -#include <private/qv4qmlcontext_p.h> - -#if QT_CONFIG(qml_sequence_object) -#include <private/qv4sequenceobject_p.h> -#endif - -#include <private/qv4objectproto_p.h> +#include <private/qv4jscall_p.h> #include <private/qv4jsonobject_p.h> +#include <private/qv4lookup_p.h> +#include <private/qv4mm_p.h> #include <private/qv4regexpobject_p.h> -#include <private/qv4dateobject_p.h> +#include <private/qv4runtime_p.h> #include <private/qv4scopedvalue_p.h> -#include <private/qv4jscall_p.h> -#include <private/qv4mm_p.h> -#include <private/qqmlscriptstring_p.h> -#include <private/qv4compileddata_p.h> +#include <private/qv4sequenceobject_p.h> +#include <private/qv4variantobject_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> +#include <QtCore/qloggingcategory.h> #include <QtCore/qmetaobject.h> +#include <QtCore/qqueue.h> +#include <QtCore/qtimer.h> +#include <QtCore/qtypes.h> +#include <QtCore/qvarlengtharray.h> + +#include <vector> + #if QT_CONFIG(qml_itemmodel) #include <QtCore/qabstractitemmodel.h> #endif -#include <QtCore/qloggingcategory.h> -#include <vector> QT_BEGIN_NAMESPACE -Q_LOGGING_CATEGORY(lcBindingRemoval, "qt.qml.binding.removal", QtWarningMsg) +Q_LOGGING_CATEGORY(lcBuiltinsBindingRemoval, "qt.qml.binding.removal", QtWarningMsg) +Q_STATIC_LOGGING_CATEGORY(lcObjectConnect, "qt.qml.object.connect", QtWarningMsg) +Q_STATIC_LOGGING_CATEGORY(lcOverloadResolution, "qt.qml.overloadresolution", QtWarningMsg) +Q_STATIC_LOGGING_CATEGORY(lcMethodBehavior, "qt.qml.method.behavior") +Q_STATIC_LOGGING_CATEGORY(lcSignalHandler, "qt.qml.signalhandler") // 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 QT_WARNING_DISABLE_GCC("-Wstrict-aliasing") -using namespace QV4; +using namespace Qt::StringLiterals; -QPair<QObject *, int> QObjectMethod::extractQtMethod(const QV4::FunctionObject *function) +namespace QV4 { + +QPair<QObject *, int> QObjectMethod::extractQtMethod(const FunctionObject *function) { - QV4::ExecutionEngine *v4 = function->engine(); + ExecutionEngine *v4 = function->engine(); if (v4) { - QV4::Scope scope(v4); - QV4::Scoped<QObjectMethod> method(scope, function->as<QObjectMethod>()); + Scope scope(v4); + Scoped<QObjectMethod> method(scope, function->as<QObjectMethod>()); if (method) return qMakePair(method->object(), method->methodIndex()); } @@ -110,16 +76,16 @@ QPair<QObject *, int> QObjectMethod::extractQtMethod(const QV4::FunctionObject * return qMakePair((QObject *)nullptr, -1); } -static QPair<QObject *, int> extractQtSignal(const QV4::Value &value) +static QPair<QObject *, int> extractQtSignal(const Value &value) { if (value.isObject()) { - QV4::ExecutionEngine *v4 = value.as<QV4::Object>()->engine(); - QV4::Scope scope(v4); - QV4::ScopedFunctionObject function(scope, value); + ExecutionEngine *v4 = value.as<Object>()->engine(); + Scope scope(v4); + ScopedFunctionObject function(scope, value); if (function) return QObjectMethod::extractQtMethod(function); - QV4::Scoped<QV4::QmlSignalHandler> handler(scope, value); + Scoped<QmlSignalHandler> handler(scope, value); if (handler) return qMakePair(handler->object(), handler->signalIndex()); } @@ -127,83 +93,220 @@ static QPair<QObject *, int> extractQtSignal(const QV4::Value &value) return qMakePair((QObject *)nullptr, -1); } -static QV4::ReturnedValue loadProperty(QV4::ExecutionEngine *v4, QObject *object, - const QQmlPropertyData &property) +static Heap::ReferenceObject::Flags referenceFlags( + ExecutionEngine *v4, + const QQmlPropertyData &property) +{ + Heap::ReferenceObject::Flags flags = Heap::ReferenceObject::NoFlag; + if (CppStackFrame *stackFrame = v4->currentStackFrame) { + if (stackFrame->v4Function->executableCompilationUnit()->valueTypesAreCopied()) + flags |= Heap::ReferenceObject::EnforcesLocation; + } + + if (property.isWritable()) + flags |= Heap::ReferenceObject::CanWriteBack; + + return flags; +} + +static ReturnedValue loadProperty( + ExecutionEngine *v4, Heap::Object *wrapper, + QObject *object, const QQmlPropertyData &property) { Q_ASSERT(!property.isFunction()); - QV4::Scope scope(v4); + Scope scope(v4); + const QMetaType propMetaType = property.propType(); if (property.isQObject()) { QObject *rv = nullptr; property.readProperty(object, &rv); - return QV4::QObjectWrapper::wrap(v4, rv); - } else if (property.isQList()) { - return QmlListWrapper::create(v4, object, property.coreIndex(), property.propType()); - } else if (property.propType() == QMetaType::QReal) { - qreal v = 0; + if (propMetaType.flags().testFlag(QMetaType::IsConst)) + return QObjectWrapper::wrapConst(v4, rv); + else + return QObjectWrapper::wrap(v4, rv); + } + + if (property.isQList() && propMetaType.flags().testFlag(QMetaType::IsQmlList)) + return QmlListWrapper::create(v4, object, property.coreIndex(), propMetaType); + + const auto encodeSimple = [&](auto v) { + property.readProperty(object, &v); + return Encode(v); + }; + + const auto encodeInt = [&](auto v) { property.readProperty(object, &v); - return QV4::Encode(v); - } else if (property.propType() == QMetaType::Int || property.isEnum()) { - int v = 0; + return Encode(int(v)); + }; + + const auto encodeDouble = [&](auto v) { property.readProperty(object, &v); - return QV4::Encode(v); - } else if (property.propType() == QMetaType::Bool) { - bool v = false; + return Encode(double(v)); + }; + + const auto encodeDate = [&](auto v) { property.readProperty(object, &v); - return QV4::Encode(v); - } else if (property.propType() == QMetaType::QString) { - QString v; + return Encode(v4->newDateObject( + v, wrapper, property.coreIndex(), referenceFlags(scope.engine, property))); + }; + + const auto encodeString = [&](auto v) { property.readProperty(object, &v); return v4->newString(v)->asReturnedValue(); - } else if (property.propType() == QMetaType::UInt) { - uint v = 0; + }; + + const auto encodeSequence = [&](QMetaSequence metaSequence) { + // Pass nullptr as data. It's lazy-loaded. + return QV4::SequencePrototype::newSequence( + v4, propMetaType, metaSequence, nullptr, + wrapper, property.coreIndex(), referenceFlags(scope.engine, property)); + }; + + + switch (property.isEnum() ? propMetaType.underlyingType().id() : propMetaType.id()) { + case QMetaType::UnknownType: + case QMetaType::Void: + return Encode::undefined(); + case QMetaType::Nullptr: + case QMetaType::VoidStar: + return Encode::null(); + case QMetaType::Int: + return encodeSimple(int()); + case QMetaType::Bool: + return encodeSimple(bool()); + case QMetaType::QString: + return encodeString(QString()); + case QMetaType::QByteArray: { + QByteArray v; + property.readProperty(object, &v); + return v4->newArrayBuffer(v)->asReturnedValue(); + } + case QMetaType::QChar: + return encodeString(QChar()); + case QMetaType::Char16: + return encodeString(char16_t()); + case QMetaType::UInt: + return encodeSimple(uint()); + case QMetaType::Float: + return encodeSimple(float()); + case QMetaType::Double: + return encodeSimple(double()); + case QMetaType::Short: + return encodeInt(short()); + case QMetaType::UShort: + return encodeInt(ushort()); + case QMetaType::Char: + return encodeInt(char()); + case QMetaType::UChar: + return encodeInt(uchar()); + case QMetaType::SChar: + return encodeInt(qint8()); + case QMetaType::Long: + return encodeDouble(long()); + case QMetaType::ULong: + return encodeDouble(ulong()); + case QMetaType::LongLong: + return encodeDouble(qlonglong()); + case QMetaType::ULongLong: + return encodeDouble(qulonglong()); + case QMetaType::QDateTime: + return encodeDate(QDateTime()); + case QMetaType::QDate: + return encodeDate(QDate()); + case QMetaType::QTime: + return encodeDate(QTime()); +#if QT_CONFIG(regularexpression) + case QMetaType::QRegularExpression: { + QRegularExpression v; property.readProperty(object, &v); - return QV4::Encode(v); - } else if (property.propType() == QMetaType::Float) { - float v = 0; + return Encode(v4->newRegExpObject(v)); + } +#endif + case QMetaType::QVariantMap: { + QVariantMap v; property.readProperty(object, &v); - return QV4::Encode(v); - } else if (property.propType() == QMetaType::Double) { - double v = 0; + return scope.engine->fromData( + propMetaType, &v, wrapper, property.coreIndex(), referenceFlags(v4, property)); + } + case QMetaType::QJsonValue: { + QJsonValue v; property.readProperty(object, &v); - return QV4::Encode(v); - } else if (property.propType() == qMetaTypeId<QJSValue>()) { - QJSValue v; + return QV4::JsonObject::fromJsonValue(v4, v); + } + case QMetaType::QJsonObject: { + QJsonObject v; property.readProperty(object, &v); - return QJSValuePrivate::convertedToValue(v4, v); - } else if (property.isQVariant()) { - QVariant v; + return QV4::JsonObject::fromJsonObject(v4, v); + } + case QMetaType::QJsonArray: + return encodeSequence(QMetaSequence::fromContainer<QJsonArray>()); + case QMetaType::QStringList: + return encodeSequence(QMetaSequence::fromContainer<QStringList>()); + case QMetaType::QVariantList: + return encodeSequence(QMetaSequence::fromContainer<QVariantList>()); + case QMetaType::QUrl: { + // ### Qt7: We really want this to be a JS URL object, but that would break things. + QUrl v; property.readProperty(object, &v); + return Encode(v4->newVariantObject(propMetaType, &v)); + } + case QMetaType::QPixmap: + case QMetaType::QImage: { + // Scarce value types + QVariant v(propMetaType); + property.readProperty(object, v.data()); + return Encode(v4->newVariantObject(propMetaType, v.constData())); + } + default: + break; + } - if (QQmlValueTypeFactory::isValueType(v.userType())) { - if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(v.userType())) - return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, v.userType()); // VariantReference value-type. - } + if (propMetaType == QMetaType::fromType<QJSValue>()) { + QJSValue v; + property.readProperty(object, &v); + return QJSValuePrivate::convertToReturnedValue(v4, v); + } - return scope.engine->fromVariant(v); - } else if (QQmlValueTypeFactory::isValueType(property.propType())) { - if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property.propType())) - return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, property.propType()); - } else { -#if QT_CONFIG(qml_sequence_object) - // see if it's a sequence type - bool succeeded = false; - QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType(), object, property.coreIndex(), !property.isWritable(), &succeeded)); - if (succeeded) - return retn->asReturnedValue(); -#endif + if (property.isQVariant()) { + // We have to read the property even if it's a lazy-loaded reference object. + // Without reading it, we wouldn't know its inner type. + QVariant v; + property.readProperty(object, &v); + return scope.engine->fromVariant( + v, wrapper, property.coreIndex(), + referenceFlags(scope.engine, property) | Heap::ReferenceObject::IsVariant); } - if (property.propType() == QMetaType::UnknownType) { + if (!propMetaType.isValid()) { 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::Encode::undefined(); - } else { - QVariant v(property.propType(), (void *)nullptr); - property.readProperty(object, v.data()); - return scope.engine->fromVariant(v); + return Encode::undefined(); + } + + // TODO: For historical reasons we don't enforce locations for reference objects here. + // Once we do, we can eager load and use the fromVariant() below. + // Then the extra checks for value types and sequences can be dropped. + + if (QQmlMetaType::isValueType(propMetaType)) { + if (const QMetaObject *valueTypeMetaObject + = QQmlMetaType::metaObjectForValueType(propMetaType)) { + // Lazy loaded value type reference. Pass nullptr as data. + return QQmlValueTypeWrapper::create( + v4, nullptr, valueTypeMetaObject, propMetaType, wrapper, + property.coreIndex(), referenceFlags(scope.engine, property)); + } } + + // See if it's a sequence type. + const QQmlType qmlType = QQmlMetaType::qmlListType(propMetaType); + if (qmlType.isSequentialContainer()) + return encodeSequence(qmlType.listMetaSequence()); + + QVariant v(propMetaType); + property.readProperty(object, v.data()); + return scope.engine->fromVariant( + v, wrapper, property.coreIndex(), referenceFlags(scope.engine, property)); } void QObjectWrapper::initializeBindings(ExecutionEngine *engine) @@ -212,28 +315,33 @@ void QObjectWrapper::initializeBindings(ExecutionEngine *engine) engine->functionPrototype()->defineDefaultProperty(QStringLiteral("disconnect"), method_disconnect); } -QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const +const QQmlPropertyData *QObjectWrapper::findProperty( + const QQmlRefPointer<QQmlContextData> &qmlContext, String *name, + Flags flags, QQmlPropertyData *local) const { - QObject *o = d()->object(); - return findProperty(engine, o, qmlContext, name, revisionMode, local); + return findProperty(d()->object(), qmlContext, name, flags, local); } -QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QObject *o, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) +const QQmlPropertyData *QObjectWrapper::findProperty( + QObject *o, const QQmlRefPointer<QQmlContextData> &qmlContext, + String *name, Flags flags, QQmlPropertyData *local) { - Q_UNUSED(revisionMode); + Q_UNUSED(flags); QQmlData *ddata = QQmlData::get(o, false); - QQmlPropertyData *result = nullptr; + const QQmlPropertyData *result = nullptr; if (ddata && ddata->propertyCache) result = ddata->propertyCache->property(name, o, qmlContext); else - result = QQmlPropertyCache::property(engine->jsEngine(), o, name, qmlContext, *local); + result = QQmlPropertyCache::property(o, name, qmlContext, local); return result; } -ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property) +ReturnedValue QObjectWrapper::getProperty( + ExecutionEngine *engine, Heap::Object *wrapper, QObject *object, + const QQmlPropertyData *property, Flags flags) { - QQmlData::flushPendingBinding(object, QQmlPropertyIndex(property->coreIndex())); + QQmlData::flushPendingBinding(object, property->coreIndex()); if (property->isFunction() && !property->isVarProperty()) { if (property->isVMEFunction()) { @@ -241,151 +349,162 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje Q_ASSERT(vmemo); return vmemo->vmeMethod(property->coreIndex()); } else if (property->isV4Function()) { - Scope scope(engine); - ScopedContext global(scope, engine->qmlContext()); - if (!global) - global = engine->rootContext(); - return QV4::QObjectMethod::create(global, object, property->coreIndex()); + return QObjectMethod::create( + engine, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex()); } else if (property->isSignalHandler()) { QmlSignalHandler::initProto(engine); - return engine->memoryManager->allocate<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue(); + return engine->memoryManager->allocate<QmlSignalHandler>( + object, property->coreIndex())->asReturnedValue(); } else { - ExecutionContext *global = engine->rootContext(); - return QV4::QObjectMethod::create(global, object, property->coreIndex()); + return QObjectMethod::create( + engine, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex()); } } QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr; if (ep && ep->propertyCapture && !property->isConstant()) - ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex()); + if (!property->isBindable() || ep->propertyCapture->expression->mustCaptureBindableProperty()) + ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex()); if (property->isVarProperty()) { QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); Q_ASSERT(vmemo); return vmemo->vmeProperty(property->coreIndex()); } else { - return loadProperty(engine, object, *property); + return loadProperty(engine, wrapper, object, *property); } } -static OptionalReturnedValue getDestroyOrToStringMethod(ExecutionEngine *v4, String *name, QObject *qobj, bool *hasProperty = nullptr) +static OptionalReturnedValue getDestroyOrToStringMethod( + ExecutionEngine *v4, String *name, Heap::Object *qobj, bool *hasProperty = nullptr) { int index = 0; if (name->equals(v4->id_destroy())) - index = QV4::QObjectMethod::DestroyMethod; + index = QObjectMethod::DestroyMethod; else if (name->equals(v4->id_toString())) - index = QV4::QObjectMethod::ToStringMethod; + index = QObjectMethod::ToStringMethod; else return OptionalReturnedValue(); if (hasProperty) *hasProperty = true; - ExecutionContext *global = v4->rootContext(); - return OptionalReturnedValue(QV4::QObjectMethod::create(global, qobj, index)); + return OptionalReturnedValue(QObjectMethod::create(v4, qobj, index)); } -static OptionalReturnedValue getPropertyFromImports(ExecutionEngine *v4, String *name, QQmlContextData *qmlContext, QObject *qobj, - bool *hasProperty = nullptr) +static OptionalReturnedValue getPropertyFromImports( + ExecutionEngine *v4, String *name, const QQmlRefPointer<QQmlContextData> &qmlContext, + QObject *qobj, bool *hasProperty = nullptr) { - if (!qmlContext || !qmlContext->imports) + if (!qmlContext || !qmlContext->imports()) return OptionalReturnedValue(); - QQmlTypeNameCache::Result r = qmlContext->imports->query(name); - if (hasProperty) *hasProperty = true; - if (!r.isValid()) + if (QQmlTypeLoader *typeLoader = v4->typeLoader()) { + QQmlTypeNameCache::Result r = qmlContext->imports()->query(name, typeLoader); + + if (!r.isValid()) + return OptionalReturnedValue(); + + if (r.scriptIndex != -1) { + return OptionalReturnedValue(Encode::undefined()); + } else if (r.type.isValid()) { + return OptionalReturnedValue( + QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums)); + } else if (r.importNamespace) { + return OptionalReturnedValue(QQmlTypeWrapper::create( + v4, qobj, qmlContext->imports(), r.importNamespace, + Heap::QQmlTypeWrapper::ExcludeEnums)); + } + Q_UNREACHABLE_RETURN(OptionalReturnedValue()); + } else { return OptionalReturnedValue(); - - if (r.scriptIndex != -1) { - return OptionalReturnedValue(QV4::Encode::undefined()); - } else if (r.type.isValid()) { - return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums)); - } else if (r.importNamespace) { - return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj, qmlContext->imports, r.importNamespace, - Heap::QQmlTypeWrapper::ExcludeEnums)); } - Q_UNREACHABLE(); - return OptionalReturnedValue(); } -ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String *name, QObjectWrapper::RevisionMode revisionMode, - bool *hasProperty, bool includeImports) const +ReturnedValue QObjectWrapper::getQmlProperty( + const QQmlRefPointer<QQmlContextData> &qmlContext, String *name, + QObjectWrapper::Flags flags, bool *hasProperty) const { // Keep this code in sync with ::virtualResolveLookupGetter if (QQmlData::wasDeleted(d()->object())) { if (hasProperty) *hasProperty = false; - return QV4::Encode::undefined(); + return Encode::undefined(); } ExecutionEngine *v4 = engine(); - if (auto methodValue = getDestroyOrToStringMethod(v4, name, d()->object(), hasProperty)) + if (auto methodValue = getDestroyOrToStringMethod(v4, name, d(), hasProperty)) return *methodValue; QQmlPropertyData local; - QQmlPropertyData *result = findProperty(v4, qmlContext, name, revisionMode, &local); + const QQmlPropertyData *result = findProperty(qmlContext, name, flags, &local); if (!result) { // Check for attached properties - if (includeImports && name->startsWithUpper()) { - if (auto importProperty = getPropertyFromImports(v4, name, qmlContext, d()->object(), hasProperty)) + if ((flags & IncludeImports) && name->startsWithUpper()) { + if (auto importProperty = getPropertyFromImports( + v4, name, qmlContext, d()->object(), hasProperty)) return *importProperty; } - return QV4::Object::virtualGet(this, name->propertyKey(), this, hasProperty); + return Object::virtualGet(this, name->propertyKey(), this, hasProperty); } QQmlData *ddata = QQmlData::get(d()->object(), false); - if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) { + if ((flags & CheckRevision) && result->hasRevision()) { if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) { if (hasProperty) *hasProperty = false; - return QV4::Encode::undefined(); + return Encode::undefined(); } } if (hasProperty) *hasProperty = true; - return getProperty(v4, d()->object(), result); + return getProperty(v4, d(), d()->object(), result, flags); } -ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, QQmlPropertyData **property) +ReturnedValue QObjectWrapper::getQmlProperty( + ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, + Heap::Object *wrapper, QObject *object, String *name, QObjectWrapper::Flags flags, + bool *hasProperty, const QQmlPropertyData **property) { if (QQmlData::wasDeleted(object)) { if (hasProperty) *hasProperty = false; - return QV4::Encode::null(); + return Encode::null(); } - if (auto methodValue = getDestroyOrToStringMethod(engine, name, object, hasProperty)) + if (auto methodValue = getDestroyOrToStringMethod(engine, name, wrapper, hasProperty)) return *methodValue; QQmlData *ddata = QQmlData::get(object, false); QQmlPropertyData local; - QQmlPropertyData *result = findProperty(engine, object, qmlContext, name, revisionMode, &local); + const QQmlPropertyData *result = findProperty(object, qmlContext, name, flags, &local); if (result) { - if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) { - if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) { + if ((flags & QObjectWrapper::CheckRevision) && result->hasRevision()) { + if (ddata && ddata->propertyCache + && !ddata->propertyCache->isAllowedInRevision(result)) { if (hasProperty) *hasProperty = false; - return QV4::Encode::undefined(); + return Encode::undefined(); } } if (hasProperty) *hasProperty = true; - if (property) + if (property && result != &local) *property = result; - return getProperty(engine, object, result); + return getProperty(engine, wrapper, object, result, flags); } else { // Check if this object is already wrapped. if (!ddata || (ddata->jsWrapper.isUndefined() && @@ -395,7 +514,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC // Not wrapped. Last chance: try query QObjectWrapper's prototype. // If it can't handle this, then there is no point // to wrap the QObject just to look at an empty set of JS props. - QV4::Object *proto = QObjectWrapper::defaultPrototype(engine); + Object *proto = QObjectWrapper::defaultPrototype(engine); return proto->get(name, hasProperty); } } @@ -404,29 +523,30 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC // There's no point wrapping again, as there wouldn't be any new props. Q_ASSERT(ddata); - QV4::Scope scope(engine); - QV4::Scoped<QObjectWrapper> wrapper(scope, wrap(engine, object)); - if (!wrapper) { + Scope scope(engine); + Scoped<QObjectWrapper> rewrapped(scope, wrap(engine, object)); + if (!rewrapped) { if (hasProperty) *hasProperty = false; - return QV4::Encode::null(); + return Encode::null(); } - return wrapper->getQmlProperty(qmlContext, name, revisionMode, hasProperty); + return rewrapped->getQmlProperty(qmlContext, name, flags, hasProperty); } -bool QObjectWrapper::setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, - QObjectWrapper::RevisionMode revisionMode, const Value &value) +bool QObjectWrapper::setQmlProperty( + ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, QObject *object, + String *name, QObjectWrapper::Flags flags, const Value &value) { if (QQmlData::wasDeleted(object)) return false; QQmlPropertyData local; - QQmlPropertyData *result = QQmlPropertyCache::property(engine->jsEngine(), object, name, qmlContext, local); + const QQmlPropertyData *result = QQmlPropertyCache::property(object, name, qmlContext, &local); if (!result) return false; - if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) { + if ((flags & QObjectWrapper::CheckRevision) && result->hasRevision()) { QQmlData *ddata = QQmlData::get(object); if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) return false; @@ -436,7 +556,31 @@ bool QObjectWrapper::setQmlProperty(ExecutionEngine *engine, QQmlContextData *qm return true; } -void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value) +/*! + \internal + If an QObjectWrapper is created via wrap, then it needs to be stored somewhere. + Otherwise, the garbage collector will immediately collect it if it is already + past the "mark QObjectWrapper's" phase (note that QObjectWrapper are marked + by iterating over a list of all QObjectWrapper, and then checking if the + wrapper fulfills some conditions). + However, sometimes we don't really want to keep a reference to the wrapper, + but just want to make sure that it exists (and we know that the wrapper + already fulfills the conditions to be kept alive). Then ensureWrapper + can be used, which creates the wrapper and ensures that it is also + marked. + */ +void QObjectWrapper::ensureWrapper(ExecutionEngine *engine, QObject *object) +{ + QV4::Scope scope(engine); + QV4::Scoped<QV4::QObjectWrapper> wrapper {scope, QV4::QObjectWrapper::wrap(engine, object)}; + QV4::WriteBarrier::markCustom(engine, [&wrapper](QV4::MarkStack *ms) { + wrapper->mark(ms); + }); +} + +void QObjectWrapper::setProperty( + ExecutionEngine *engine, QObject *object, + const QQmlPropertyData *property, const Value &value) { if (!property->isWritable() && !property->isQList()) { QString error = QLatin1String("Cannot assign to read-only property \"") + @@ -445,56 +589,108 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP return; } - QQmlBinding *newBinding = nullptr; - QV4::Scope scope(engine); - QV4::ScopedFunctionObject f(scope, value); - if (f) { - if (!f->isBinding()) { - if (!property->isVarProperty() && property->propType() != qMetaTypeId<QJSValue>()) { + Scope scope(engine); + if (ScopedFunctionObject f(scope, value); f) { + if (f->as<QQmlTypeWrapper>()) { + // Ignore. It's probably a singleton or an attached type. + } else if (!f->isBinding()) { + const bool isAliasToAllowed = [&]() { + if (property->isAlias()) { + const QQmlPropertyIndex originalIndex(property->coreIndex(), -1); + auto [targetObject, targetIndex] = QQmlPropertyPrivate::findAliasTarget(object, originalIndex); + Q_ASSERT(targetObject); + const QQmlPropertyCache *targetCache + = QQmlData::get(targetObject)->propertyCache.data(); + Q_ASSERT(targetCache); + const QQmlPropertyData *targetProperty + = targetCache->property(targetIndex.coreIndex()); + object = targetObject; + property = targetProperty; + return targetProperty->isVarProperty() || targetProperty->propType() == QMetaType::fromType<QJSValue>(); + } else { + return false; + } + }(); + if (!isAliasToAllowed && !property->isVarProperty() + && property->propType() != QMetaType::fromType<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(property->propType())) + if (!QMetaType(property->propType()).name()) error += QLatin1String("[unknown property type]"); else - error += QLatin1String(QMetaType::typeName(property->propType())); + error += QLatin1String(QMetaType(property->propType()).name()); scope.engine->throwError(error); return; } } else { - // binding assignment. - QQmlContextData *callingQmlContext = scope.engine->callingQmlContext(); - QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f); + QQmlRefPointer<QQmlContextData> callingQmlContext = scope.engine->callingQmlContext(); + Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f); + Scoped<JavaScriptFunctionObject> f(scope, bindingFunction->bindingFunction()); + ScopedContext ctx(scope, f->scope()); - QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction()); - QV4::ScopedContext ctx(scope, bindingFunction->scope()); - newBinding = QQmlBinding::create(property, f->function(), object, callingQmlContext, ctx); - newBinding->setSourceLocation(bindingFunction->currentLocation()); - if (f->isBoundFunction()) - newBinding->setBoundFunction(static_cast<QV4::BoundFunction *>(f.getPointer())); - newBinding->setTarget(object, *property, nullptr); + // binding assignment. + if (property->isBindable()) { + const QQmlPropertyIndex idx(property->coreIndex(), /*not a value type*/-1); + auto [targetObject, targetIndex] = QQmlPropertyPrivate::findAliasTarget(object, idx); + QUntypedPropertyBinding binding; + if (f->isBoundFunction()) { + auto boundFunction = static_cast<BoundFunction *>(f.getPointer()); + binding = QQmlPropertyBinding::createFromBoundFunction(property, boundFunction, object, callingQmlContext, + ctx, targetObject, targetIndex); + } else { + binding = QQmlPropertyBinding::create(property, f->function(), object, callingQmlContext, + ctx, targetObject, targetIndex); + } + QUntypedBindable bindable; + void *argv = {&bindable}; + // indirect metacall in case interceptors are installed + targetObject->metaObject()->metacall(targetObject, QMetaObject::BindableProperty, targetIndex.coreIndex(), &argv); + bool ok = bindable.setBinding(binding); + if (!ok) { + auto error = QStringLiteral("Failed to set binding on %1::%2."). + arg(QString::fromUtf8(object->metaObject()->className()), property->name(object)); + scope.engine->throwError(error); + } + } else { + QQmlBinding *newBinding = QQmlBinding::create(property, f->function(), object, callingQmlContext, ctx); + newBinding->setSourceLocation(bindingFunction->currentLocation()); + if (f->isBoundFunction()) + newBinding->setBoundFunction(static_cast<BoundFunction *>(f.getPointer())); + newBinding->setTarget(object, *property, nullptr); + QQmlPropertyPrivate::setBinding(newBinding); + } + return; } } - if (newBinding) { - QQmlPropertyPrivate::setBinding(newBinding); - } else { - if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) { - if (auto binding = QQmlPropertyPrivate::binding(object, QQmlPropertyIndex(property->coreIndex()))) { - Q_ASSERT(!binding->isValueTypeProxy()); + if (Q_UNLIKELY(lcBuiltinsBindingRemoval().isInfoEnabled())) { + if (auto binding = QQmlPropertyPrivate::binding(object, QQmlPropertyIndex(property->coreIndex()))) { + const auto stackFrame = engine->currentStackFrame; + switch (binding->kind()) { + case QQmlAbstractBinding::QmlBinding: { const auto qmlBinding = static_cast<const QQmlBinding*>(binding); - const auto stackFrame = engine->currentStackFrame; - qCInfo(lcBindingRemoval, + qCInfo(lcBuiltinsBindingRemoval, "Overwriting binding on %s::%s at %s:%d that was initially bound at %s", object->metaObject()->className(), qPrintable(property->name(object)), qPrintable(stackFrame->source()), stackFrame->lineNumber(), qPrintable(qmlBinding->expressionIdentifier())); + break; + } + case QQmlAbstractBinding::ValueTypeProxy: + case QQmlAbstractBinding::PropertyToPropertyBinding: { + qCInfo(lcBuiltinsBindingRemoval, + "Overwriting binding on %s::%s at %s:%d", + object->metaObject()->className(), qPrintable(property->name(object)), + qPrintable(stackFrame->source()), stackFrame->lineNumber()); + break; + } } } - QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex())); } + QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex())); - if (!newBinding && property->isVarProperty()) { + if (property->isVarProperty()) { // allow assignment of "special" values (null, undefined, function) to var properties QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); Q_ASSERT(vmemo); @@ -509,65 +705,70 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP void *argv[] = { &o, 0, &status, &flags }; \ QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex(), argv); + const QMetaType propType = property->propType(); + // functions are already handled, except for the QJSValue case + Q_ASSERT(!value.as<FunctionObject>() + || value.as<QV4::QQmlTypeWrapper>() + || propType == QMetaType::fromType<QJSValue>()); + if (value.isNull() && property->isQObject()) { PROPERTY_STORE(QObject*, nullptr); } else if (value.isUndefined() && property->isResettable()) { void *a[] = { nullptr }; QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex(), a); - } else if (value.isUndefined() && property->propType() == qMetaTypeId<QVariant>()) { + } else if (value.isUndefined() && propType == QMetaType::fromType<QVariant>()) { PROPERTY_STORE(QVariant, QVariant()); - } else if (value.isUndefined() && property->propType() == QMetaType::QJsonValue) { + } else if (value.isUndefined() && propType == QMetaType::fromType<QJsonValue>()) { PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined)); - } else if (!newBinding && property->propType() == qMetaTypeId<QJSValue>()) { - PROPERTY_STORE(QJSValue, QJSValue(scope.engine, value.asReturnedValue())); - } else if (value.isUndefined() && property->propType() != qMetaTypeId<QQmlScriptString>()) { + } else if (propType == QMetaType::fromType<QJSValue>()) { + PROPERTY_STORE(QJSValue, QJSValuePrivate::fromReturnedValue(value.asReturnedValue())); + } else if (value.isUndefined() && propType != QMetaType::fromType<QQmlScriptString>()) { QString error = QLatin1String("Cannot assign [undefined] to "); - if (!QMetaType::typeName(property->propType())) + if (!propType.name()) error += QLatin1String("[unknown property type]"); else - error += QLatin1String(QMetaType::typeName(property->propType())); + error += QLatin1String(propType.name()); scope.engine->throwError(error); return; - } else if (value.as<FunctionObject>()) { - // this is handled by the binding creation above - } else if (property->propType() == QMetaType::Int && value.isNumber()) { - PROPERTY_STORE(int, value.asDouble()); - } else if (property->propType() == QMetaType::QReal && value.isNumber()) { + } else if (propType == QMetaType::fromType<int>() && value.isNumber()) { + PROPERTY_STORE(int, value.toInt32()); + } else if (propType == QMetaType::fromType<qreal>() && value.isNumber()) { PROPERTY_STORE(qreal, qreal(value.asDouble())); - } else if (property->propType() == QMetaType::Float && value.isNumber()) { + } else if (propType == QMetaType::fromType<float>() && value.isNumber()) { PROPERTY_STORE(float, float(value.asDouble())); - } else if (property->propType() == QMetaType::Double && value.isNumber()) { + } else if (propType == QMetaType::fromType<double>() && value.isNumber()) { PROPERTY_STORE(double, double(value.asDouble())); - } else if (property->propType() == QMetaType::QString && value.isString()) { + } else if (propType == QMetaType::fromType<QString>() && value.isString()) { PROPERTY_STORE(QString, value.toQStringNoThrow()); } else if (property->isVarProperty()) { QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); Q_ASSERT(vmemo); vmemo->setVMEProperty(property->coreIndex(), value); - } else if (property->propType() == qMetaTypeId<QQmlScriptString>() && (value.isUndefined() || value.isPrimitive())) { + } else if (propType == QMetaType::fromType<QQmlScriptString>() + && (value.isUndefined() || value.isPrimitive())) { QQmlScriptString ss(value.toQStringNoThrow(), nullptr /* context */, object); if (value.isNumber()) { ss.d->numberValue = value.toNumber(); ss.d->isNumberLiteral = true; } else if (value.isString()) { - ss.d->script = QV4::CompiledData::Binding::escapedString(ss.d->script); + ss.d->script = CompiledData::Binding::escapedString(ss.d->script); ss.d->isStringLiteral = true; } PROPERTY_STORE(QQmlScriptString, ss); } else { QVariant v; - if (property->isQList()) - v = scope.engine->toVariant(value, qMetaTypeId<QList<QObject *> >()); + if (property->isQList() && propType.flags().testFlag(QMetaType::IsQmlList)) + v = ExecutionEngine::toVariant(value, QMetaType::fromType<QList<QObject *> >()); else - v = scope.engine->toVariant(value, property->propType()); + v = ExecutionEngine::toVariant(value, propType); - QQmlContextData *callingQmlContext = scope.engine->callingQmlContext(); + QQmlRefPointer<QQmlContextData> callingQmlContext = scope.engine->callingQmlContext(); if (!QQmlPropertyPrivate::write(object, *property, v, callingQmlContext)) { const char *valueType = (v.userType() == QMetaType::UnknownType) ? "an unknown type" - : QMetaType::typeName(v.userType()); + : QMetaType(v.userType()).name(); - const char *targetTypeName = QMetaType::typeName(property->propType()); + const char *targetTypeName = propType.name(); if (!targetTypeName) targetTypeName = "an unregistered type"; @@ -587,7 +788,7 @@ ReturnedValue QObjectWrapper::wrap_slowPath(ExecutionEngine *engine, QObject *ob QQmlData *ddata = QQmlData::get(object, true); if (!ddata) - return QV4::Encode::undefined(); + return Encode::undefined(); Scope scope(engine); @@ -596,7 +797,7 @@ ReturnedValue QObjectWrapper::wrap_slowPath(ExecutionEngine *engine, QObject *ob ddata->jsEngineId == 0 || // No one owns the QObject !ddata->hasTaintedV4Object)) { // Someone else has used the QObject, but it isn't tainted - QV4::ScopedValue rv(scope, create(engine, object)); + ScopedValue rv(scope, create(engine, object)); ddata->jsWrapper.set(scope.engine, rv); ddata->jsEngineId = engine->m_engineId; return rv->asReturnedValue(); @@ -611,7 +812,7 @@ ReturnedValue QObjectWrapper::wrap_slowPath(ExecutionEngine *engine, QObject *ob // 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->jsWrapper if (ddata->jsWrapper.isUndefined() && !alternateWrapper) { - QV4::ScopedValue result(scope, create(engine, object)); + ScopedValue result(scope, create(engine, object)); ddata->jsWrapper.set(scope.engine, result); ddata->jsEngineId = engine->m_engineId; return result->asReturnedValue(); @@ -629,6 +830,29 @@ ReturnedValue QObjectWrapper::wrap_slowPath(ExecutionEngine *engine, QObject *ob } } +ReturnedValue QObjectWrapper::wrapConst_slowPath(ExecutionEngine *engine, QObject *object) +{ + const QObject *constObject = object; + + QQmlData *ddata = QQmlData::get(object, true); + + Scope scope(engine); + ScopedObject constWrapper(scope); + if (engine->m_multiplyWrappedQObjects && ddata->hasConstWrapper) + constWrapper = engine->m_multiplyWrappedQObjects->value(constObject); + + if (!constWrapper) { + constWrapper = create(engine, object); + constWrapper->setInternalClass(constWrapper->internalClass()->cryopreserved()); + if (!engine->m_multiplyWrappedQObjects) + engine->m_multiplyWrappedQObjects = new MultiplyWrappedQObjectMap; + engine->m_multiplyWrappedQObjects->insert(constObject, constWrapper->d()); + ddata->hasConstWrapper = true; + } + + return constWrapper.asReturnedValue(); +} + void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack) { if (QQmlData::wasDeleted(object)) @@ -638,10 +862,15 @@ void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack) if (!ddata) return; - if (ddata->jsEngineId == markStack->engine->m_engineId) + const ExecutionEngine *engine = markStack->engine(); + if (ddata->jsEngineId == engine->m_engineId) ddata->jsWrapper.markOnce(markStack); - else if (markStack->engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object) - markStack->engine->m_multiplyWrappedQObjects->mark(object, markStack); + else if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object) + engine->m_multiplyWrappedQObjects->mark(object, markStack); + if (ddata->hasConstWrapper) { + Q_ASSERT(engine->m_multiplyWrappedQObjects); + engine->m_multiplyWrappedQObjects->mark(static_cast<const QObject *>(object), markStack); + } } void QObjectWrapper::setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value) @@ -660,40 +889,36 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int p if (!ddata) return; - QQmlPropertyCache *cache = ddata->propertyCache; - Q_ASSERT(cache); - QQmlPropertyData *property = cache->property(propertyIndex); + Q_ASSERT(ddata->propertyCache); + const QQmlPropertyData *property = ddata->propertyCache->property(propertyIndex); Q_ASSERT(property); // We resolved this property earlier, so it better exist! return setProperty(engine, object, property, value); } bool QObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b) { - Q_ASSERT(a->as<QV4::QObjectWrapper>()); - QV4::QObjectWrapper *qobjectWrapper = static_cast<QV4::QObjectWrapper *>(a); - QV4::Object *o = b->as<Object>(); - if (o) { - if (QV4::QQmlTypeWrapper *qmlTypeWrapper = o->as<QV4::QQmlTypeWrapper>()) - return qmlTypeWrapper->toVariant().value<QObject*>() == qobjectWrapper->object(); - } + Q_ASSERT(a->as<QObjectWrapper>()); + const QObjectWrapper *aobjectWrapper = static_cast<QObjectWrapper *>(a); + if (const QQmlTypeWrapper *qmlTypeWrapper = b->as<QQmlTypeWrapper>()) + return qmlTypeWrapper->object() == aobjectWrapper->object(); - return false; + // We can have a const and a non-const wrapper for the same object. + const QObjectWrapper *bobjectWrapper = b->as<QObjectWrapper>(); + return bobjectWrapper && aobjectWrapper->object() == bobjectWrapper->object(); } ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QObject *object) { - if (QJSEngine *jsEngine = engine->jsEngine()) { - if (QQmlPropertyCache *cache = QQmlData::ensurePropertyCache(jsEngine, object)) { - ReturnedValue result = QV4::Encode::null(); - void *args[] = { &result, &engine }; - if (cache->callJSFactoryMethod(object, args)) - return result; - } + if (QQmlPropertyCache::ConstPtr cache = QQmlData::ensurePropertyCache(object)) { + ReturnedValue result = Encode::null(); + void *args[] = { &result, &engine }; + if (cache->callJSFactoryMethod(object, args)) + return result; } - return (engine->memoryManager->allocate<QV4::QObjectWrapper>(object))->asReturnedValue(); + return (engine->memoryManager->allocate<QObjectWrapper>(object))->asReturnedValue(); } -QV4::ReturnedValue QObjectWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) +ReturnedValue QObjectWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { if (!id.isString()) return Object::virtualGet(m, id, receiver, hasProperty); @@ -701,8 +926,8 @@ QV4::ReturnedValue QObjectWrapper::virtualGet(const Managed *m, PropertyKey id, const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m); Scope scope(that); ScopedString n(scope, id.asStringOrSymbol()); - QQmlContextData *qmlContext = that->engine()->callingQmlContext(); - return that->getQmlProperty(qmlContext, n, IgnoreRevision, hasProperty, /*includeImports*/ true); + QQmlRefPointer<QQmlContextData> qmlContext = that->engine()->callingQmlContext(); + return that->getQmlProperty(qmlContext, n, IncludeImports | AttachMethods, hasProperty); } bool QObjectWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) @@ -714,11 +939,18 @@ bool QObjectWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, QObjectWrapper *that = static_cast<QObjectWrapper*>(m); ScopedString name(scope, id.asStringOrSymbol()); - if (scope.engine->hasException || QQmlData::wasDeleted(that->d()->object())) + if (that->internalClass()->isFrozen()) { + QString error = QLatin1String("Cannot assign to property \"") + + name->toQString() + QLatin1String("\" of read-only object"); + scope.engine->throwError(error); return false; + } - QQmlContextData *qmlContext = scope.engine->callingQmlContext(); - if (!setQmlProperty(scope.engine, qmlContext, that->d()->object(), name, QV4::QObjectWrapper::IgnoreRevision, value)) { + if (scope.hasException() || QQmlData::wasDeleted(that->d()->object())) + return false; + + QQmlRefPointer<QQmlContextData> qmlContext = scope.engine->callingQmlContext(); + if (!setQmlProperty(scope.engine, qmlContext, that->d()->object(), name, NoFlag, value)) { QQmlData *ddata = QQmlData::get(that->d()->object()); // Types created by QML are not extensible at run-time, but for other QObjects we can store them // as regular JavaScript properties, like on JavaScript objects. @@ -728,7 +960,7 @@ bool QObjectWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, scope.engine->throwError(error); return false; } else { - return QV4::Object::virtualPut(m, id, value, receiver); + return Object::virtualPut(m, id, value, receiver); } } @@ -743,34 +975,35 @@ PropertyAttributes QObjectWrapper::virtualGetOwnProperty(const Managed *m, Prope if (!QQmlData::wasDeleted(thatObject)) { Scope scope(m); ScopedString n(scope, id.asStringOrSymbol()); - QQmlContextData *qmlContext = scope.engine->callingQmlContext(); + QQmlRefPointer<QQmlContextData> qmlContext = scope.engine->callingQmlContext(); QQmlPropertyData local; - if (that->findProperty(scope.engine, qmlContext, n, IgnoreRevision, &local) + if (that->findProperty(qmlContext, n, NoFlag, &local) || n->equals(scope.engine->id_destroy()) || n->equals(scope.engine->id_toString())) { if (p) { // ### probably not the fastest implementation bool hasProperty; - p->value = that->getQmlProperty(qmlContext, n, IgnoreRevision, &hasProperty, /*includeImports*/ true); + p->value = that->getQmlProperty( + qmlContext, n, IncludeImports | AttachMethods, &hasProperty); } - return QV4::Attr_Data; + return Attr_Data; } } } - return QV4::Object::virtualGetOwnProperty(m, id, p); + return Object::virtualGetOwnProperty(m, id, p); } struct QObjectWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator { int propertyIndex = 0; ~QObjectWrapperOwnPropertyKeyIterator() override = default; - PropertyKey next(const QV4::Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; + PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; private: QSet<QByteArray> m_alreadySeen; }; -PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Property *pd, PropertyAttributes *attrs) +PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) { // Used to block access to QObject::destroyed() and QObject::deleteLater() from QML static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)"); @@ -792,11 +1025,13 @@ PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Pro ScopedString propName(scope, thatEngine->newString(QString::fromUtf8(property.name()))); ++propertyIndex; if (attrs) - *attrs= QV4::Attr_Data; + *attrs= Attr_Data; if (pd) { QQmlPropertyData local; local.load(property); - pd->value = that->getProperty(thatEngine, thatObject, &local); + pd->value = that->getProperty( + thatEngine, that->d(), thatObject, &local, + QObjectWrapper::AttachMethods); } return propName->toPropertyKey(); } @@ -817,11 +1052,13 @@ PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Pro Scope scope(thatEngine); ScopedString methodName(scope, thatEngine->newString(QString::fromUtf8(method.name()))); if (attrs) - *attrs = QV4::Attr_Data; + *attrs = Attr_Data; if (pd) { QQmlPropertyData local; local.load(method); - pd->value = that->getProperty(thatEngine, thatObject, &local); + pd->value = that->getProperty( + thatEngine, that->d(), thatObject, &local, + QObjectWrapper::AttachMethods); } return methodName->toPropertyKey(); } @@ -847,23 +1084,32 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E const QObjectWrapper *This = static_cast<const QObjectWrapper *>(object); ScopedString name(scope, id.asStringOrSymbol()); - QQmlContextData *qmlContext = engine->callingQmlContext(); + QQmlRefPointer<QQmlContextData> qmlContext = engine->callingQmlContext(); QObject * const qobj = This->d()->object(); if (QQmlData::wasDeleted(qobj)) - return QV4::Encode::undefined(); - - if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj)) - return *methodValue; + return Encode::undefined(); QQmlData *ddata = QQmlData::get(qobj, false); + if (auto methodValue = getDestroyOrToStringMethod(engine, name, This->d())) { + Scoped<QObjectMethod> method(scope, *methodValue); + setupQObjectMethodLookup( + lookup, ddata ? ddata : QQmlData::get(qobj, true), nullptr, This, method->d()); + lookup->getter = Lookup::getterQObjectMethod; + return method.asReturnedValue(); + } + if (!ddata || !ddata->propertyCache) { QQmlPropertyData local; - QQmlPropertyData *property = QQmlPropertyCache::property(engine->jsEngine(), qobj, name, qmlContext, local); - return property ? getProperty(engine, qobj, property) : QV4::Encode::undefined(); + const QQmlPropertyData *property = QQmlPropertyCache::property( + qobj, name, qmlContext, &local); + return property + ? getProperty(engine, This->d(), qobj, property, + lookup->forCall ? NoFlag : AttachMethods) + : Encode::undefined(); } - QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext); + const QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext); if (!property) { // Check for attached properties @@ -871,27 +1117,28 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E if (auto importProperty = getPropertyFromImports(engine, name, qmlContext, qobj)) return *importProperty; } - return QV4::Object::virtualResolveLookupGetter(object, engine, lookup); + return Object::virtualResolveLookupGetter(object, engine, lookup); + } + + if (property->isFunction() + && !property->isVarProperty() + && !property->isVMEFunction() // Handled by QObjectLookup + && !property->isSignalHandler()) { // TODO: Optimize SignalHandler, too + QV4::Heap::QObjectMethod *method = nullptr; + setupQObjectMethodLookup(lookup, ddata, property, This, method); + lookup->getter = Lookup::getterQObjectMethod; + return lookup->getter(lookup, engine, *object); } - lookup->qobjectLookup.ic = This->internalClass(); - lookup->qobjectLookup.propertyCache = ddata->propertyCache; - lookup->qobjectLookup.propertyCache->addref(); - lookup->qobjectLookup.propertyData = property; - lookup->getter = QV4::QObjectWrapper::lookupGetter; + setupQObjectLookup(lookup, ddata, property, This); + lookup->getter = Lookup::getterQObject; return lookup->getter(lookup, engine, *object); } -ReturnedValue QObjectWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object) +ReturnedValue QObjectWrapper::lookupAttached( + Lookup *l, ExecutionEngine *engine, const Value &object) { - const auto revertLookup = [lookup, engine, &object]() { - lookup->qobjectLookup.propertyCache->release(); - lookup->qobjectLookup.propertyCache = nullptr; - lookup->getter = Lookup::getterGeneric; - return Lookup::getterGeneric(lookup, engine, object); - }; - - return lookupGetterImpl(lookup, engine, object, /*useOriginalProperty*/ false, revertLookup); + return Lookup::getterGeneric(l, engine, object); } bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, @@ -900,20 +1147,60 @@ bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine return Object::virtualResolveLookupSetter(object, engine, lookup, value); } -namespace QV4 { +int QObjectWrapper::virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a) +{ + QObjectWrapper *wrapper = object->as<QObjectWrapper>(); + Q_ASSERT(wrapper); + + if (QObject *qObject = wrapper->object()) + return QMetaObject::metacall(qObject, call, index, a); + + return 0; +} + +QString QObjectWrapper::objectToString( + ExecutionEngine *engine, const QMetaObject *metaObject, QObject *object) +{ + if (!metaObject) + return QLatin1String("null"); + + if (!object) + return QString::fromUtf8(metaObject->className()) + QLatin1String("(0x0)"); + + const int id = metaObject->indexOfMethod("toString()"); + if (id >= 0) { + const QMetaMethod method = metaObject->method(id); + const QMetaType returnType = method.returnMetaType(); + QVariant result(returnType); + method.invoke(object, QGenericReturnArgument(returnType.name(), result.data())); + if (result.metaType() == QMetaType::fromType<QString>()) + return result.toString(); + QV4::Scope scope(engine); + QV4::ScopedValue value(scope, engine->fromVariant(result)); + return value->toQString(); + } + + QString result; + result += QString::fromUtf8(metaObject->className()) + + QLatin1String("(0x") + QString::number(quintptr(object), 16); + QString objectName = object->objectName(); + if (!objectName.isEmpty()) + result += QLatin1String(", \"") + objectName + QLatin1Char('\"'); + result += QLatin1Char(')'); + return result; +} struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase { - QV4::PersistentValue function; - QV4::PersistentValue thisObject; - int signalIndex; + PersistentValue function; + PersistentValue thisObject; + QMetaMethod signal; QObjectSlotDispatcher() : QtPrivate::QSlotObjectBase(&impl) - , signalIndex(-1) {} - static void impl(int which, QSlotObjectBase *this_, QObject *r, void **metaArgs, bool *ret) + static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **metaArgs, bool *ret) { switch (which) { case Destroy: { @@ -921,30 +1208,33 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase } break; case Call: { + if (QQmlData::wasDeleted(receiver)) + break; + QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_); - QV4::ExecutionEngine *v4 = This->function.engine(); + ExecutionEngine *v4 = This->function.engine(); // Might be that we're still connected to a signal that's emitted long // after the engine died. We don't track connections in a global list, so // we need this safeguard. if (!v4) break; - QQmlMetaObject::ArgTypeStorage storage; - int *argsTypes = QQmlMetaObject(r).methodParameterTypes(This->signalIndex, &storage, nullptr); + QQmlMetaObject::ArgTypeStorage<9> storage; + QQmlMetaObject::methodParameterTypes(This->signal, &storage, nullptr); - int argCount = argsTypes ? argsTypes[0]:0; + int argCount = storage.size(); - QV4::Scope scope(v4); - QV4::ScopedFunctionObject f(scope, This->function.value()); + Scope scope(v4); + ScopedFunctionObject f(scope, This->function.value()); - QV4::JSCallData jsCallData(scope, argCount); - *jsCallData->thisObject = This->thisObject.isUndefined() ? v4->globalObject->asReturnedValue() : This->thisObject.value(); + JSCallArguments jsCallData(scope, argCount); + *jsCallData.thisObject = This->thisObject.isUndefined() ? v4->globalObject->asReturnedValue() : This->thisObject.value(); for (int ii = 0; ii < argCount; ++ii) { - int type = argsTypes[ii + 1]; - if (type == qMetaTypeId<QVariant>()) { - jsCallData->args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1])); + QMetaType type = storage[ii]; + if (type == QMetaType::fromType<QVariant>()) { + jsCallData.args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1])); } else { - jsCallData->args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1])); + jsCallData.args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1])); } } @@ -952,7 +1242,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase if (scope.hasException()) { QQmlError error = v4->catchExceptionAsQmlError(); if (error.description().isEmpty()) { - QV4::ScopedString name(scope, f->name()); + ScopedString name(scope, f->name()); error.setDescription(QStringLiteral("Unknown exception occurred during evaluation of connected function: %1").arg(name->toQString())); } if (QQmlEngine *qmlEngine = v4->qmlEngine()) { @@ -975,15 +1265,15 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase // 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]); + ExecutionEngine *v4 = reinterpret_cast<ExecutionEngine*>(metaArgs[0]); if (v4 != connection->function.engine()) { *ret = false; return; } - QV4::Scope scope(v4); - QV4::ScopedValue function(scope, *reinterpret_cast<QV4::Value*>(metaArgs[1])); - QV4::ScopedValue thisObject(scope, *reinterpret_cast<QV4::Value*>(metaArgs[2])); + Scope scope(v4); + ScopedValue function(scope, *reinterpret_cast<Value*>(metaArgs[1])); + ScopedValue thisObject(scope, *reinterpret_cast<Value*>(metaArgs[2])); QObject *receiverToDisconnect = reinterpret_cast<QObject*>(metaArgs[3]); int slotIndexToDisconnect = *reinterpret_cast<int*>(metaArgs[4]); @@ -992,7 +1282,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase if (connection->thisObject.isUndefined() == thisObject->isUndefined() && (connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) { - QV4::ScopedFunctionObject f(scope, connection->function.value()); + ScopedFunctionObject f(scope, connection->function.value()); QPair<QObject *, int> connectedFunctionData = QObjectMethod::extractQtMethod(f); if (connectedFunctionData.first == receiverToDisconnect && connectedFunctionData.second == slotIndexToDisconnect) { @@ -1019,11 +1309,9 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase }; }; -} // namespace QV4 - ReturnedValue QObjectWrapper::method_connect(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - QV4::Scope scope(b); + Scope scope(b); if (argc == 0) THROW_GENERIC_ERROR("Function.prototype.connect: no arguments given"); @@ -1038,11 +1326,12 @@ ReturnedValue QObjectWrapper::method_connect(const FunctionObject *b, const Valu if (!signalObject) THROW_GENERIC_ERROR("Function.prototype.connect: cannot connect to deleted QObject"); - if (signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal) + auto signalMetaMethod = signalObject->metaObject()->method(signalIndex); + if (signalMetaMethod.methodType() != QMetaMethod::Signal) THROW_GENERIC_ERROR("Function.prototype.connect: this object is not a signal"); - QV4::ScopedFunctionObject f(scope); - QV4::ScopedValue object (scope, QV4::Encode::undefined()); + ScopedFunctionObject f(scope); + ScopedValue object (scope, Encode::undefined()); if (argc == 1) { f = argv[0]; @@ -1057,25 +1346,43 @@ ReturnedValue QObjectWrapper::method_connect(const FunctionObject *b, const Valu if (!object->isUndefined() && !object->isObject()) THROW_GENERIC_ERROR("Function.prototype.connect: target this is not an object"); - QV4::QObjectSlotDispatcher *slot = new QV4::QObjectSlotDispatcher; - slot->signalIndex = signalIndex; + QObjectSlotDispatcher *slot = new QObjectSlotDispatcher; + slot->signal = signalMetaMethod; slot->thisObject.set(scope.engine, object); slot->function.set(scope.engine, f); if (QQmlData *ddata = QQmlData::get(signalObject)) { - if (QQmlPropertyCache *propertyCache = ddata->propertyCache) { + if (const QQmlPropertyCache *propertyCache = ddata->propertyCache.data()) { QQmlPropertyPrivate::flushSignal(signalObject, propertyCache->methodIndexToSignalIndex(signalIndex)); } } - QObjectPrivate::connect(signalObject, signalIndex, slot, Qt::AutoConnection); + + QPair<QObject *, int> functionData = QObjectMethod::extractQtMethod(f); // align with disconnect + QObject *receiver = nullptr; + + if (functionData.first) + receiver = functionData.first; + else if (auto qobjectWrapper = object->as<QV4::QObjectWrapper>()) + receiver = qobjectWrapper->object(); + else if (auto typeWrapper = object->as<QV4::QQmlTypeWrapper>()) + receiver = typeWrapper->object(); + + if (receiver) { + QObjectPrivate::connect(signalObject, signalIndex, receiver, slot, Qt::AutoConnection); + } else { + qCInfo(lcObjectConnect, + "Could not find receiver of the connection, using sender as receiver. Disconnect " + "explicitly (or delete the sender) to make sure the connection is removed."); + QObjectPrivate::connect(signalObject, signalIndex, signalObject, slot, Qt::AutoConnection); + } RETURN_UNDEFINED(); } ReturnedValue QObjectWrapper::method_disconnect(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { - QV4::Scope scope(b); + Scope scope(b); if (argc == 0) THROW_GENERIC_ERROR("Function.prototype.disconnect: no arguments given"); @@ -1093,8 +1400,8 @@ ReturnedValue QObjectWrapper::method_disconnect(const FunctionObject *b, const V if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal) THROW_GENERIC_ERROR("Function.prototype.disconnect: this object is not a signal"); - QV4::ScopedFunctionObject functionValue(scope); - QV4::ScopedValue functionThisValue(scope, QV4::Encode::undefined()); + ScopedFunctionObject functionValue(scope); + ScopedValue functionThisValue(scope, Encode::undefined()); if (argc == 1) { functionValue = argv[0]; @@ -1119,31 +1426,76 @@ ReturnedValue QObjectWrapper::method_disconnect(const FunctionObject *b, const V &functionData.second }; - QObjectPrivate::disconnect(signalObject, signalIndex, reinterpret_cast<void**>(&a)); + QObject *receiver = nullptr; + + if (functionData.first) + receiver = functionData.first; + else if (auto qobjectWrapper = functionThisValue->as<QV4::QObjectWrapper>()) + receiver = qobjectWrapper->object(); + else if (auto typeWrapper = functionThisValue->as<QV4::QQmlTypeWrapper>()) + receiver = typeWrapper->object(); + + if (receiver) { + QObjectPrivate::disconnect(signalObject, signalIndex, receiver, + reinterpret_cast<void **>(&a)); + } else { + QObjectPrivate::disconnect(signalObject, signalIndex, signalObject, + reinterpret_cast<void **>(&a)); + } RETURN_UNDEFINED(); } -static void markChildQObjectsRecursively(QObject *parent, QV4::MarkStack *markStack) +static void markChildQObjectsRecursively(QObject *parent, MarkStack *markStack) { - const QObjectList &children = parent->children(); - for (int i = 0; i < children.count(); ++i) { - QObject *child = children.at(i); + QQueue<QObject *> queue; + queue.append(parent->children()); + + while (!queue.isEmpty()) { + QObject *child = queue.dequeue(); if (!child) continue; QObjectWrapper::markWrapper(child, markStack); - markChildQObjectsRecursively(child, markStack); + queue.append(child->children()); } } -void Heap::QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markStack) +void Heap::QObjectWrapper::markObjects(Heap::Base *that, MarkStack *markStack) { QObjectWrapper *This = static_cast<QObjectWrapper *>(that); if (QObject *o = This->object()) { - QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o); - if (vme) - vme->mark(markStack); + if (QQmlData *ddata = QQmlData::get(o)) { + if (ddata->hasVMEMetaObject) { + if (QQmlVMEMetaObject *vme + = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(o)->metaObject)) { + vme->mark(markStack); + } + } + + if (ddata->hasConstWrapper) { + Scope scope(that->internalClass->engine); + Q_ASSERT(scope.engine->m_multiplyWrappedQObjects); + + Scoped<QV4::QObjectWrapper> constWrapper( + scope, + scope.engine->m_multiplyWrappedQObjects->value( + static_cast<const QObject *>(o))); + + Q_ASSERT(constWrapper); + + if (This == constWrapper->d()) { + // We've got the const wrapper. Also mark the non-const one + if (ddata->jsEngineId == scope.engine->m_engineId) + ddata->jsWrapper.markOnce(markStack); + else + scope.engine->m_multiplyWrappedQObjects->mark(o, markStack); + } else { + // We've got the non-const wrapper. Also mark the const one. + constWrapper->mark(markStack); + } + } + } // 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 @@ -1159,38 +1511,45 @@ void Heap::QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markSta void QObjectWrapper::destroyObject(bool lastCall) { Heap::QObjectWrapper *h = d(); - if (!h->internalClass) - return; // destroyObject already got called + Q_ASSERT(h->internalClass); - if (h->object()) { - QQmlData *ddata = QQmlData::get(h->object(), false); + if (QObject *o = h->object()) { + QQmlData *ddata = QQmlData::get(o, false); if (ddata) { - if (!h->object()->parent() && !ddata->indestructible) { + if (!o->parent() && !ddata->indestructible) { if (ddata && ddata->ownContext) { - Q_ASSERT(ddata->ownContext == ddata->context); - ddata->ownContext->emitDestruction(); - ddata->ownContext = nullptr; + Q_ASSERT(ddata->ownContext.data() == ddata->context); + ddata->ownContext->deepClearContextObject(o); + ddata->ownContext.reset(); ddata->context = nullptr; } - // This object is notionally destroyed now + + // This object is notionally destroyed now. It might still live until the next + // event loop iteration, but it won't need its connections, CU, or deferredData + // anymore. + ddata->isQueuedForDeletion = true; + ddata->disconnectNotifiers(QQmlData::DeleteNotifyList::No); + ddata->compilationUnit.reset(); + + qDeleteAll(ddata->deferredData); + ddata->deferredData.clear(); + if (lastCall) - delete h->object(); + delete o; else - h->object()->deleteLater(); + o->deleteLater(); } else { // If the object is C++-owned, we still have to release the weak reference we have // to it. ddata->jsWrapper.clear(); - if (lastCall && ddata->propertyCache) { - ddata->propertyCache->release(); - ddata->propertyCache = nullptr; - } + if (lastCall && ddata->propertyCache) + ddata->propertyCache.reset(); } } } - h->~Data(); + h->destroy(); } @@ -1209,21 +1568,26 @@ public: }; struct CallArgument { - inline CallArgument(); - inline ~CallArgument(); + Q_DISABLE_COPY_MOVE(CallArgument); + + CallArgument() = default; + ~CallArgument() { cleanup(); } + inline void *dataPtr(); - inline void initAsType(int type); - inline bool fromValue(int type, ExecutionEngine *, const QV4::Value &); + inline void initAsType(QMetaType type); + inline bool fromValue(QMetaType type, ExecutionEngine *, const Value &); inline ReturnedValue toValue(ExecutionEngine *); private: - CallArgument(const CallArgument &); + // QVariantWrappedType denotes that we're storing a QVariant, but we mean + // the type inside the QVariant, not QVariant itself. + enum { QVariantWrappedType = -1 }; inline void cleanup(); template <class T, class M> - void fromContainerValue(const QV4::Object *object, int type, M CallArgument::*member, bool &queryEngine); + bool fromContainerValue(const Value &object, M CallArgument::*member); union { float floatValue; @@ -1262,12 +1626,12 @@ private: QJsonValue *jsonValuePtr; }; - int type; + int type = QMetaType::UnknownType; }; } -static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, int returnType, int argCount, - int *argTypes, QV4::ExecutionEngine *engine, QV4::CallData *callArgs, +static ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, QMetaType returnType, int argCount, + const QMetaType *argTypes, ExecutionEngine *engine, CallData *callArgs, QMetaObject::Call callType = QMetaObject::InvokeMetaMethod) { if (argCount > 0) { @@ -1276,7 +1640,7 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index args[0].initAsType(returnType); for (int ii = 0; ii < argCount; ++ii) { if (!args[ii + 1].fromValue(argTypes[ii], engine, - callArgs->args[ii].asValue<QV4::Value>())) { + callArgs->args[ii].asValue<Value>())) { qWarning() << QString::fromLatin1("Could not convert argument %1 at").arg(ii); const StackTrace stack = engine->stackTrace(); for (const StackFrame &frame : stack) { @@ -1286,22 +1650,27 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index : QString()); } - qWarning() << QLatin1String("Passing incompatible arguments to C++ functions from " - "JavaScript is dangerous and deprecated."); - qWarning() << QLatin1String("This will throw a JavaScript TypeError in future " - "releases of Qt!"); + const bool is_signal = + object.metaObject()->method(index).methodType() == QMetaMethod::Signal; + if (is_signal) { + qWarning() << "Passing incompatible arguments to signals is not supported."; + } else { + return engine->throwTypeError( + QLatin1String("Passing incompatible arguments to C++ functions from " + "JavaScript is not allowed.")); + } } } - QVarLengthArray<void *, 9> argData(args.count()); - for (int ii = 0; ii < args.count(); ++ii) + QVarLengthArray<void *, 9> argData(args.size()); + for (int ii = 0; ii < args.size(); ++ii) argData[ii] = args[ii].dataPtr(); object.metacall(callType, index, argData.data()); return args[0].toValue(engine); - } else if (returnType != QMetaType::Void) { + } else if (returnType != QMetaType::fromType<void>()) { CallArgument arg; arg.initAsType(returnType); @@ -1321,14 +1690,35 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index } } +template<typename Retrieve> +int MatchVariant(QMetaType conversionMetaType, Retrieve &&retrieve) { + if (conversionMetaType == QMetaType::fromType<QVariant>()) + return 0; + + const QMetaType type = retrieve(); + if (type == conversionMetaType) + return 0; + + if (const QMetaObject *conversionMetaObject = conversionMetaType.metaObject()) { + if (const QMetaObject *mo = type.metaObject(); mo && mo->inherits(conversionMetaObject)) + return 1; + } + + if (QMetaType::canConvert(type, conversionMetaType)) + return 5; + + return 10; +}; + /* 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) +static int MatchScore(const Value &actual, QMetaType conversionMetaType) { + const int conversionType = conversionMetaType.id(); if (actual.isNumber()) { switch (conversionType) { case QMetaType::Double: @@ -1362,6 +1752,8 @@ static int MatchScore(const QV4::Value &actual, int conversionType) return 0; case QMetaType::QJsonValue: return 5; + case QMetaType::QUrl: + return 6; // we like to convert strings to URLs in QML default: return 10; } @@ -1385,13 +1777,12 @@ static int MatchScore(const QV4::Value &actual, int conversionType) default: return 10; } - } else if (actual.as<QV4::RegExpObject>()) { + } else if (actual.as<RegExpObject>()) { switch (conversionType) { - case QMetaType::QRegExp: #if QT_CONFIG(regularexpression) case QMetaType::QRegularExpression: -#endif return 0; +#endif default: return 10; } @@ -1425,149 +1816,172 @@ static int MatchScore(const QV4::Value &actual, int conversionType) case QMetaType::QJsonValue: return 0; default: { - const char *typeName = QMetaType::typeName(conversionType); - if (typeName && typeName[strlen(typeName) - 1] == '*') + if (conversionMetaType.flags().testFlag(QMetaType::IsPointer)) return 0; else return 10; } } - } else if (const QV4::Object *obj = actual.as<QV4::Object>()) { - if (obj->as<QV4::VariantObject>()) { - if (conversionType == qMetaTypeId<QVariant>()) - return 0; - if (obj->engine()->toVariant(actual, -1).userType() == conversionType) - return 0; - else - return 10; + } else if (const Object *obj = actual.as<Object>()) { + if (const VariantObject *variantObject = obj->as<VariantObject>()) { + return MatchVariant(conversionMetaType, [variantObject]() { + return variantObject->d()->data().metaType(); + }); } - if (obj->as<QObjectWrapper>()) { + if (const QObjectWrapper *wrapper = obj->as<QObjectWrapper>()) { switch (conversionType) { case QMetaType::QObjectStar: return 0; default: - return 10; + if (conversionMetaType.flags() & QMetaType::PointerToQObject) { + QObject *wrapped = wrapper->object(); + if (!wrapped) + return 0; + if (qmlobject_can_cpp_cast(wrapped, conversionMetaType.metaObject())) + return 0; + } } + return 10; } - if (obj->as<QV4::QQmlValueTypeWrapper>()) { - if (obj->engine()->toVariant(actual, -1).userType() == conversionType) - return 0; - return 10; - } else if (conversionType == QMetaType::QJsonObject) { - return 5; - } else if (conversionType == qMetaTypeId<QJSValue>()) { - return 0; - } else { + if (const QQmlTypeWrapper *wrapper = obj->as<QQmlTypeWrapper>()) { + const QQmlType type = wrapper->d()->type(); + if (type.isSingleton()) { + const QMetaType metaType = type.typeId(); + if (metaType == conversionMetaType) + return 0; + + if (conversionMetaType.flags() & QMetaType::PointerToQObject + && metaType.flags() & QMetaType::PointerToQObject + && type.metaObject()->inherits(conversionMetaType.metaObject())) { + return 0; + } + } else if (QObject *object = wrapper->object()) { + if (conversionMetaType.flags() & QMetaType::PointerToQObject + && qmlobject_can_cpp_cast(object, conversionMetaType.metaObject())) { + return 0; + } + } + return 10; } - } else { - return 10; + if (const Sequence *sequence = obj->as<Sequence>()) { + if (SequencePrototype::metaTypeForSequence(sequence) == conversionMetaType) + return 1; + else + return 10; + } + + if (const QQmlValueTypeWrapper *wrapper = obj->as<QQmlValueTypeWrapper>()) { + return MatchVariant(conversionMetaType, [wrapper]() { + return wrapper->d()->isVariant() + ? wrapper->toVariant().metaType() + : wrapper->type(); + }); + } + + if (conversionType == QMetaType::QJsonObject) + return 5; + if (conversionType == qMetaTypeId<QJSValue>()) + return 0; + if (conversionType == QMetaType::QVariantMap) + return 5; } + + return 10; } -static inline int QMetaObject_methods(const QMetaObject *metaObject) +static int numDefinedArguments(CallData *callArgs) { - struct Private - { - int revision; - int className; - int classInfoCount, classInfoData; - int methodCount, methodData; - }; - - return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount; + int numDefinedArguments = callArgs->argc(); + while (numDefinedArguments > 0 + && callArgs->args[numDefinedArguments - 1].type() == StaticValue::Undefined_Type) { + --numDefinedArguments; + } + return numDefinedArguments; } -/* -Returns the next related method, if one, or 0. -*/ -static const QQmlPropertyData * RelatedMethod(const QQmlObjectOrGadget &object, - const QQmlPropertyData *current, - QQmlPropertyData &dummy, - const QQmlPropertyCache *propertyCache) +static bool requiresStrictArguments(const QQmlObjectOrGadget &object) { - if (!current->isOverload()) - return nullptr; - - Q_ASSERT(!current->overrideIndexIsProperty()); - - if (propertyCache) { - return propertyCache->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); - } - - // If we've been called before with the same override index, then - // we can't go any further... - if (&dummy == current && dummy.coreIndex() == current->overrideIndex()) - return nullptr; - - 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.setOverload(true); - dummy.setOverrideIndexIsProperty(0); - dummy.setOverrideIndex(ii); - return &dummy; - } - } - - return &dummy; - } + const QMetaObject *metaObject = object.metaObject(); + const int indexOfClassInfo = metaObject->indexOfClassInfo("QML.StrictArguments"); + return indexOfClassInfo != -1 + && metaObject->classInfo(indexOfClassInfo).value() == QByteArrayView("true"); } -static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPropertyData &data, - QV4::ExecutionEngine *engine, QV4::CallData *callArgs, - QMetaObject::Call callType = QMetaObject::InvokeMetaMethod) +ReturnedValue QObjectMethod::callPrecise( + const QQmlObjectOrGadget &object, const QQmlPropertyData &data, ExecutionEngine *engine, + CallData *callArgs, QMetaObject::Call callType) { QByteArray unknownTypeError; - int returnType = object.methodReturnType(data, &unknownTypeError); + QMetaType returnType = object.methodReturnType(data, &unknownTypeError); - if (returnType == QMetaType::UnknownType) { + if (!returnType.isValid()) { return engine->throwError(QLatin1String("Unknown method return type: ") + QLatin1String(unknownTypeError)); } + auto handleTooManyArguments = [&](int expectedArguments) { + if (requiresStrictArguments(object)) { + engine->throwError(QStringLiteral("Too many arguments")); + return false; + } + + const auto stackTrace = engine->stackTrace(); + if (stackTrace.isEmpty()) { + qWarning().nospace().noquote() + << "When matching arguments for " + << object.className() << "::" << data.name(object.metaObject()) << "():"; + } else { + const StackFrame frame = stackTrace.first(); + qWarning().noquote() << frame.function + QLatin1Char('@') + frame.source + + (frame.line > 0 ? (QLatin1Char(':') + QString::number(frame.line)) + : QString()); + } + + qWarning().noquote() << QStringLiteral("Too many arguments, ignoring %1") + .arg(callArgs->argc() - expectedArguments); + return true; + }; + + const int definedArgumentCount = numDefinedArguments(callArgs); + if (data.hasArguments()) { - int *args = nullptr; - QQmlMetaObject::ArgTypeStorage storage; + QQmlMetaObject::ArgTypeStorage<9> storage; + bool ok = false; if (data.isConstructor()) - args = static_cast<const QQmlStaticMetaObject&>(object).constructorParameterTypes( - data.coreIndex(), &storage, &unknownTypeError); + ok = object.constructorParameterTypes(data.coreIndex(), &storage, &unknownTypeError); else - args = object.methodParameterTypes(data.coreIndex(), &storage, &unknownTypeError); + ok = object.methodParameterTypes(data.coreIndex(), &storage, &unknownTypeError); - if (!args) { + if (!ok) { return engine->throwError(QLatin1String("Unknown method parameter type: ") + QLatin1String(unknownTypeError)); } - if (args[0] > callArgs->argc()) { + if (storage.size() > callArgs->argc()) { QString error = QLatin1String("Insufficient arguments"); return engine->throwError(error); } - return CallMethod(object, data.coreIndex(), returnType, args[0], args + 1, engine, callArgs, callType); + if (storage.size() < definedArgumentCount) { + if (!handleTooManyArguments(storage.size())) + return Encode::undefined(); + + } + + return CallMethod(object, data.coreIndex(), returnType, storage.size(), storage.constData(), engine, callArgs, callType); } else { + if (definedArgumentCount > 0 && !handleTooManyArguments(0)) + return Encode::undefined(); return CallMethod(object, data.coreIndex(), returnType, 0, nullptr, engine, callArgs, callType); - } } @@ -1581,736 +1995,1081 @@ Resolve the overloaded method to call. The algorithm works conceptually like th 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 + If two or more overloads have the same match score, return the last one. The match score is constructed by adding the matchScore() result for each of the parameters. */ -static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const QQmlPropertyData &data, - QV4::ExecutionEngine *engine, QV4::CallData *callArgs, const QQmlPropertyCache *propertyCache, - QMetaObject::Call callType = QMetaObject::InvokeMetaMethod) +const QQmlPropertyData *QObjectMethod::resolveOverloaded( + const QQmlObjectOrGadget &object, const QQmlPropertyData *methods, int methodCount, + ExecutionEngine *engine, CallData *callArgs) { - int argumentCount = callArgs->argc(); + const int argumentCount = callArgs->argc(); + const int definedArgumentCount = numDefinedArguments(callArgs); - QQmlPropertyData best; + const QQmlPropertyData *best = nullptr; int bestParameterScore = INT_MAX; - int bestMatchScore = INT_MAX; + int bestMaxMatchScore = INT_MAX; + int bestSumMatchScore = INT_MAX; - QQmlPropertyData dummy; - const QQmlPropertyData *attempt = &data; + Scope scope(engine); + ScopedValue v(scope); - QV4::Scope scope(engine); - QV4::ScopedValue v(scope); - - do { - QQmlMetaObject::ArgTypeStorage storage; - int methodArgumentCount = 0; - int *methodArgTypes = nullptr; - if (attempt->hasArguments()) { - int *args = object.methodParameterTypes(attempt->coreIndex(), &storage, nullptr); - if (!args) // Must be an unknown argument - continue; + for (int i = 0; i < methodCount; ++i) { + const QQmlPropertyData *attempt = methods + i; - methodArgumentCount = args[0]; - methodArgTypes = args + 1; + if (lcOverloadResolution().isDebugEnabled()) { + const QQmlPropertyData &candidate = methods[i]; + const QMetaMethod m = candidate.isConstructor() + ? object.metaObject()->constructor(candidate.coreIndex()) + : object.metaObject()->method(candidate.coreIndex()); + qCDebug(lcOverloadResolution) << "::: considering signature" << m.methodSignature(); } - if (methodArgumentCount > argumentCount) - continue; // We don't have sufficient arguments to call this method + // QQmlV4Function overrides anything that doesn't provide the exact number of arguments + int methodParameterScore = 1; + // QQmlV4Function overrides the "no idea" option, which is 10 + int maxMethodMatchScore = 9; + // QQmlV4Function cannot provide a best sum of match scores as we don't match the arguments + int sumMethodMatchScore = bestSumMatchScore; + + if (!attempt->isV4Function()) { + QQmlMetaObject::ArgTypeStorage<9> storage; + int methodArgumentCount = 0; + if (attempt->hasArguments()) { + if (attempt->isConstructor()) { + if (!object.constructorParameterTypes(attempt->coreIndex(), &storage, nullptr)) { + qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types"); + continue; + } + } else { + if (!object.methodParameterTypes(attempt->coreIndex(), &storage, nullptr)) { + qCDebug(lcOverloadResolution, "rejected, could not get ctor argument types"); + continue; + } + } + methodArgumentCount = storage.size(); + } + + if (methodArgumentCount > argumentCount) { + qCDebug(lcOverloadResolution, "rejected, insufficient arguments"); + 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 + methodParameterScore = (definedArgumentCount == methodArgumentCount) + ? 0 + : (definedArgumentCount - methodArgumentCount + 1); + if (methodParameterScore > bestParameterScore) { + qCDebug(lcOverloadResolution) << "rejected, score too bad. own" << methodParameterScore << "vs best:" << bestParameterScore; + continue; // We already have a better option + } - int methodMatchScore = 0; - for (int ii = 0; ii < methodArgumentCount; ++ii) { - methodMatchScore += MatchScore((v = Value::fromStaticValue(callArgs->args[ii])), - methodArgTypes[ii]); + maxMethodMatchScore = 0; + sumMethodMatchScore = 0; + for (int ii = 0; ii < methodArgumentCount; ++ii) { + const int score = MatchScore((v = Value::fromStaticValue(callArgs->args[ii])), + storage[ii]); + maxMethodMatchScore = qMax(maxMethodMatchScore, score); + sumMethodMatchScore += score; + } } - if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { - best = *attempt; + if (bestParameterScore > methodParameterScore || bestMaxMatchScore > maxMethodMatchScore + || (bestParameterScore == methodParameterScore + && bestMaxMatchScore == maxMethodMatchScore + && bestSumMatchScore > sumMethodMatchScore)) { + best = attempt; bestParameterScore = methodParameterScore; - bestMatchScore = methodMatchScore; + bestMaxMatchScore = maxMethodMatchScore; + bestSumMatchScore = sumMethodMatchScore; + qCDebug(lcOverloadResolution) << "updated best" << "bestParameterScore" << bestParameterScore << "\n" + << "bestMaxMatchScore" << bestMaxMatchScore << "\n" + << "bestSumMatchScore" << bestSumMatchScore << "\n"; + } else { + qCDebug(lcOverloadResolution) << "did not update best\n" + << "bestParameterScore" << bestParameterScore << "\t" + << "methodParameterScore" << methodParameterScore << "\n" + << "bestMaxMatchScore" << bestMaxMatchScore << "\t" + << "maxMethodMatchScore" << maxMethodMatchScore << "\n" + << "bestSumMatchScore" << bestSumMatchScore << "\t" + << "sumMethodMatchScore" << sumMethodMatchScore << "\n"; } - if (bestParameterScore == 0 && bestMatchScore == 0) + if (bestParameterScore == 0 && bestMaxMatchScore == 0) { + qCDebug(lcOverloadResolution, "perfect match"); break; // We can't get better than that + } - } while ((attempt = RelatedMethod(object, attempt, dummy, propertyCache)) != nullptr); + }; - if (best.isValid()) { - return CallPrecise(object, best, engine, callArgs, callType); + if (best && best->isValid()) { + return best; } 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()); - candidate = RelatedMethod(object, candidate, dummy, propertyCache); + for (int i = 0; i < methodCount; ++i) { + const QQmlPropertyData &candidate = methods[i]; + const QMetaMethod m = candidate.isConstructor() + ? object.metaObject()->constructor(candidate.coreIndex()) + : object.metaObject()->method(candidate.coreIndex()); + error += u"\n " + QString::fromUtf8(m.methodSignature()); } - return engine->throwError(error); + engine->throwError(error); + return nullptr; } } -CallArgument::CallArgument() -: type(QVariant::Invalid) +static bool ExactMatch(QMetaType passed, QMetaType required, const void *data) { + if (required == QMetaType::fromType<QVariant>() + || required == QMetaType::fromType<QJSValue>() + || required == QMetaType::fromType<QJSManagedValue>()) { + return true; + } + + if (data) { + if (passed == QMetaType::fromType<QVariant>()) + passed = static_cast<const QVariant *>(data)->metaType(); + else if (passed == QMetaType::fromType<QJSPrimitiveValue>()) + passed = static_cast<const QJSPrimitiveValue *>(data)->metaType(); + } + + if (passed == required) + return true; + + if (required == QMetaType::fromType<QJSPrimitiveValue>()) { + switch (passed.id()) { + case QMetaType::UnknownType: + case QMetaType::Nullptr: + case QMetaType::Bool: + case QMetaType::Int: + case QMetaType::Double: + case QMetaType::QString: + return true; + default: + break; + } + } + + return false; } -CallArgument::~CallArgument() +const QQmlPropertyData *QObjectMethod::resolveOverloaded( + const QQmlPropertyData *methods, int methodCount, + void **argv, int argc, const QMetaType *types) { - cleanup(); + // We only accept exact matches here. Everything else goes through the JavaScript conversion. + for (int i = 0; i < methodCount; ++i) { + const QQmlPropertyData *attempt = methods + i; + if (types[0].isValid() && !ExactMatch(attempt->propType(), types[0], nullptr)) + continue; + + const QMetaMethod method = attempt->metaMethod(); + if (method.parameterCount() != argc) + continue; + + bool valid = true; + for (int i = 0; i < argc; ++i) { + if (!ExactMatch(types[i + 1], method.parameterMetaType(i), argv[i + 1])) { + valid = false; + break; + } + } + + if (valid) + return attempt; + } + + return nullptr; } void CallArgument::cleanup() { - if (type == QMetaType::QString) { + switch (type) { + case QMetaType::QString: qstringPtr->~QString(); - } else if (type == QMetaType::QByteArray) { + break; + case QMetaType::QByteArray: qbyteArrayPtr->~QByteArray(); - } else if (type == -1 || type == QMetaType::QVariant) { + break; + case QMetaType::QVariant: + case QVariantWrappedType: qvariantPtr->~QVariant(); - } else if (type == qMetaTypeId<QJSValue>()) { - qjsValuePtr->~QJSValue(); - } else if (type == qMetaTypeId<QList<QObject *> >()) { - qlistPtr->~QList<QObject *>(); - } else if (type == QMetaType::QJsonArray) { + break; + case QMetaType::QJsonArray: jsonArrayPtr->~QJsonArray(); - } else if (type == QMetaType::QJsonObject) { + break; + case QMetaType::QJsonObject: jsonObjectPtr->~QJsonObject(); - } else if (type == QMetaType::QJsonValue) { + break; + case QMetaType::QJsonValue: jsonValuePtr->~QJsonValue(); + break; + default: + if (type == qMetaTypeId<QJSValue>()) { + qjsValuePtr->~QJSValue(); + break; + } + + if (type == qMetaTypeId<QList<QObject *> >()) { + qlistPtr->~QList<QObject *>(); + break; + } + + // The sequence types need no cleanup because we don't own them. + + break; } } void *CallArgument::dataPtr() { - if (type == -1) + switch (type) { + case QMetaType::UnknownType: + return nullptr; + case QVariantWrappedType: return qvariantPtr->data(); - else if (type == qMetaTypeId<std::vector<int>>()) - return stdVectorIntPtr; - else if (type == qMetaTypeId<std::vector<qreal>>()) - return stdVectorRealPtr; - else if (type == qMetaTypeId<std::vector<bool>>()) - return stdVectorBoolPtr; - else if (type == qMetaTypeId<std::vector<QString>>()) - return stdVectorQStringPtr; - else if (type == qMetaTypeId<std::vector<QUrl>>()) - return stdVectorQUrlPtr; + default: + if (type == qMetaTypeId<std::vector<int>>()) + return stdVectorIntPtr; + if (type == qMetaTypeId<std::vector<qreal>>()) + return stdVectorRealPtr; + if (type == qMetaTypeId<std::vector<bool>>()) + return stdVectorBoolPtr; + if (type == qMetaTypeId<std::vector<QString>>()) + return stdVectorQStringPtr; + if (type == qMetaTypeId<std::vector<QUrl>>()) + return stdVectorQUrlPtr; #if QT_CONFIG(qml_itemmodel) - else if (type == qMetaTypeId<std::vector<QModelIndex>>()) - return stdVectorQModelIndexPtr; + if (type == qMetaTypeId<std::vector<QModelIndex>>()) + return stdVectorQModelIndexPtr; #endif - else if (type != 0) - return (void *)&allocData; - return nullptr; + break; + } + + return (void *)&allocData; } -void CallArgument::initAsType(int callType) +void CallArgument::initAsType(QMetaType metaType) { - if (type != 0) { cleanup(); type = 0; } - if (callType == QMetaType::UnknownType || callType == QMetaType::Void) return; + if (type != QMetaType::UnknownType) + cleanup(); - 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) { + type = metaType.id(); + switch (type) { + case QMetaType::Void: + type = QMetaType::UnknownType; + break; + case QMetaType::UnknownType: + case QMetaType::Int: + case QMetaType::UInt: + case QMetaType::Bool: + case QMetaType::Double: + case QMetaType::Float: + break; + case QMetaType::QObjectStar: qobjectPtr = nullptr; - type = callType; - } else if (callType == QMetaType::QString) { + break; + case QMetaType::QString: qstringPtr = new (&allocData) QString(); - type = callType; - } else if (callType == QMetaType::QVariant) { - type = callType; + break; + case QMetaType::QVariant: qvariantPtr = new (&allocData) QVariant(); - } else if (callType == qMetaTypeId<QList<QObject *> >()) { - type = callType; - qlistPtr = new (&allocData) QList<QObject *>(); - } else if (callType == QMetaType::QJsonArray) { - type = callType; + break; + case QMetaType::QJsonArray: jsonArrayPtr = new (&allocData) QJsonArray(); - } else if (callType == QMetaType::QJsonObject) { - type = callType; + break; + case QMetaType::QJsonObject: jsonObjectPtr = new (&allocData) QJsonObject(); - } else if (callType == QMetaType::QJsonValue) { - type = callType; + break; + case QMetaType::QJsonValue: jsonValuePtr = new (&allocData) QJsonValue(); - } else { - type = -1; - qvariantPtr = new (&allocData) QVariant(callType, (void *)nullptr); + break; + default: { + if (metaType == QMetaType::fromType<QJSValue>()) { + qjsValuePtr = new (&allocData) QJSValue(); + break; + } + + if (metaType == QMetaType::fromType<QList<QObject *>>()) { + qlistPtr = new (&allocData) QList<QObject *>(); + break; + } + + type = QVariantWrappedType; + qvariantPtr = new (&allocData) QVariant(metaType, (void *)nullptr); + break; + } } } -#if QT_CONFIG(qml_sequence_object) template <class T, class M> -void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M CallArgument::*member, bool &queryEngine) +bool CallArgument::fromContainerValue(const Value &value, M CallArgument::*member) { - if (object && object->isListType()) { - T* ptr = static_cast<T*>(QV4::SequencePrototype::getRawContainerPtr(object, callType)); - if (ptr) { - (this->*member) = ptr; - type = callType; - queryEngine = false; + if (const Sequence *sequence = value.as<Sequence>()) { + if (T* ptr = static_cast<T *>(SequencePrototype::getRawContainerPtr( + sequence, QMetaType(type)))) { + (this->*member) = ptr; + return true; + } } - } + (this->*member) = nullptr; + return false; } -#endif -bool CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const QV4::Value &value) +bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const Value &value) { - if (type != 0) { + if (type != QMetaType::UnknownType) cleanup(); - type = 0; - } - QV4::Scope scope(engine); + type = metaType.id(); - bool queryEngine = false; - if (callType == qMetaTypeId<QJSValue>()) { - qjsValuePtr = new (&allocData) QJSValue(scope.engine, value.asReturnedValue()); - type = qMetaTypeId<QJSValue>(); - } else if (callType == QMetaType::Int) { + switch (type) { + case QMetaType::Int: intValue = quint32(value.toInt32()); - type = callType; - } else if (callType == QMetaType::UInt) { + return true; + case QMetaType::UInt: intValue = quint32(value.toUInt32()); - type = callType; - } else if (callType == QMetaType::Bool) { + return true; + case QMetaType::Bool: boolValue = value.toBoolean(); - type = callType; - } else if (callType == QMetaType::Double) { + return true; + case QMetaType::Double: doubleValue = double(value.toNumber()); - type = callType; - } else if (callType == QMetaType::Float) { + return true; + case QMetaType::Float: floatValue = float(value.toNumber()); - type = callType; - } else if (callType == QMetaType::QString) { - if (value.isNull() || value.isUndefined()) + return true; + case QMetaType::QString: + if (value.isNullOrUndefined()) qstringPtr = new (&allocData) QString(); else qstringPtr = new (&allocData) QString(value.toQStringNoThrow()); - type = callType; - } else if (callType == QMetaType::QObjectStar) { - qobjectPtr = nullptr; - type = callType; - if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) + return true; + case QMetaType::QByteArray: + qbyteArrayPtr = new (&allocData) QByteArray(); + ExecutionEngine::metaTypeFromJS(value, metaType, qbyteArrayPtr); + return true; + case QMetaType::QObjectStar: + if (const QObjectWrapper *qobjectWrapper = value.as<QObjectWrapper>()) { qobjectPtr = qobjectWrapper->object(); - else if (const QV4::QQmlTypeWrapper *qmlTypeWrapper = value.as<QV4::QQmlTypeWrapper>()) - queryEngine = qmlTypeWrapper->isSingleton(); - else if (!value.isNull() && !value.isUndefined()) // null and undefined are nullptr - return false; - } else if (callType == qMetaTypeId<QVariant>()) { - qvariantPtr = new (&allocData) QVariant(scope.engine->toVariant(value, -1)); - type = callType; - } else if (callType == qMetaTypeId<QList<QObject*> >()) { - qlistPtr = new (&allocData) QList<QObject *>(); - type = callType; - QV4::ScopedArrayObject array(scope, value); - if (array) { - Scoped<QV4::QObjectWrapper> qobjectWrapper(scope); - - uint length = array->getLength(); - for (uint ii = 0; ii < length; ++ii) { - QObject *o = nullptr; - qobjectWrapper = array->get(ii); - if (!!qobjectWrapper) - o = qobjectWrapper->object(); - qlistPtr->append(o); - } - } else { - if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) { - qlistPtr->append(qobjectWrapper->object()); - } else { - qlistPtr->append(nullptr); - if (!value.isNull() && !value.isUndefined()) - return false; + return true; + } + + if (const QQmlTypeWrapper *qmlTypeWrapper = value.as<QQmlTypeWrapper>()) { + if (qmlTypeWrapper->isSingleton()) { + // Convert via QVariant below. + // TODO: Can't we just do qobjectPtr = qmlTypeWrapper->object() instead? + break; + } else if (QObject *obj = qmlTypeWrapper->object()) { + // attached object case + qobjectPtr = obj; + return true; } + + // If this is a plain type wrapper without an instance, + // then we got a namespace, and that's a type error + type = QMetaType::UnknownType; + return false; } - } else if (callType == QMetaType::QJsonArray) { - QV4::ScopedArrayObject a(scope, value); - jsonArrayPtr = new (&allocData) QJsonArray(QV4::JsonObject::toJsonArray(a)); - type = callType; - } else if (callType == QMetaType::QJsonObject) { - QV4::ScopedObject o(scope, value); - jsonObjectPtr = new (&allocData) QJsonObject(QV4::JsonObject::toJsonObject(o)); - type = callType; - } else if (callType == QMetaType::QJsonValue) { - jsonValuePtr = new (&allocData) QJsonValue(QV4::JsonObject::toJsonValue(value)); - type = callType; - } else if (callType == QMetaType::Void) { + + qobjectPtr = nullptr; + return value.isNullOrUndefined(); // null and undefined are nullptr + case QMetaType::QVariant: + qvariantPtr = new (&allocData) QVariant(ExecutionEngine::toVariant(value, QMetaType {})); + return true; + case QMetaType::QJsonArray: { + Scope scope(engine); + ScopedObject o(scope, value); + jsonArrayPtr = new (&allocData) QJsonArray(JsonObject::toJsonArray(o)); + return true; + } + case QMetaType::QJsonObject: { + Scope scope(engine); + ScopedObject o(scope, value); + jsonObjectPtr = new (&allocData) QJsonObject(JsonObject::toJsonObject(o)); + return true; + } + case QMetaType::QJsonValue: + jsonValuePtr = new (&allocData) QJsonValue(JsonObject::toJsonValue(value)); + return true; + case QMetaType::Void: + type = QMetaType::UnknownType; + // TODO: This only doesn't leak because a default constructed QVariant doesn't allocate. *qvariantPtr = QVariant(); -#if QT_CONFIG(qml_sequence_object) - } else if (callType == qMetaTypeId<std::vector<int>>() - || callType == qMetaTypeId<std::vector<qreal>>() - || callType == qMetaTypeId<std::vector<bool>>() - || callType == qMetaTypeId<std::vector<QString>>() - || callType == qMetaTypeId<std::vector<QUrl>>() -#if QT_CONFIG(qml_itemmodel) - || callType == qMetaTypeId<std::vector<QModelIndex>>() -#endif - ) { - queryEngine = true; - const QV4::Object* object = value.as<QV4::Object>(); - if (callType == qMetaTypeId<std::vector<int>>()) { - stdVectorIntPtr = nullptr; - fromContainerValue<std::vector<int>>(object, callType, &CallArgument::stdVectorIntPtr, queryEngine); - } else if (callType == qMetaTypeId<std::vector<qreal>>()) { - stdVectorRealPtr = nullptr; - fromContainerValue<std::vector<qreal>>(object, callType, &CallArgument::stdVectorRealPtr, queryEngine); - } else if (callType == qMetaTypeId<std::vector<bool>>()) { - stdVectorBoolPtr = nullptr; - fromContainerValue<std::vector<bool>>(object, callType, &CallArgument::stdVectorBoolPtr, queryEngine); - } else if (callType == qMetaTypeId<std::vector<QString>>()) { - stdVectorQStringPtr = nullptr; - fromContainerValue<std::vector<QString>>(object, callType, &CallArgument::stdVectorQStringPtr, queryEngine); - } else if (callType == qMetaTypeId<std::vector<QUrl>>()) { - stdVectorQUrlPtr = nullptr; - fromContainerValue<std::vector<QUrl>>(object, callType, &CallArgument::stdVectorQUrlPtr, queryEngine); -#if QT_CONFIG(qml_itemmodel) - } else if (callType == qMetaTypeId<std::vector<QModelIndex>>()) { - stdVectorQModelIndexPtr = nullptr; - fromContainerValue<std::vector<QModelIndex>>(object, callType, &CallArgument::stdVectorQModelIndexPtr, queryEngine); -#endif - } -#endif - } else if (QMetaType::typeFlags(callType) - & (QMetaType::PointerToQObject | QMetaType::PointerToGadget)) { - // You can assign null or undefined to any pointer. The result is a nullptr. - if (value.isNull() || value.isUndefined()) { - qvariantPtr = new (&allocData) QVariant(callType, nullptr); - type = callType; - } else { - queryEngine = true; + return true; + default: + if (type == qMetaTypeId<QJSValue>()) { + qjsValuePtr = new (&allocData) QJSValue; + QJSValuePrivate::setValue(qjsValuePtr, value.asReturnedValue()); + return true; } - } else { - queryEngine = true; - } - if (queryEngine) { - qvariantPtr = new (&allocData) QVariant(); - type = -1; + if (type == qMetaTypeId<QList<QObject*> >()) { + qlistPtr = new (&allocData) QList<QObject *>(); + Scope scope(engine); + ScopedArrayObject array(scope, value); + if (array) { + Scoped<QObjectWrapper> qobjectWrapper(scope); + + uint length = array->getLength(); + for (uint ii = 0; ii < length; ++ii) { + QObject *o = nullptr; + qobjectWrapper = array->get(ii); + if (!!qobjectWrapper) + o = qobjectWrapper->object(); + qlistPtr->append(o); + } + return true; + } - QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr; - QVariant v = scope.engine->toVariant(value, callType); + if (const QObjectWrapper *qobjectWrapper = value.as<QObjectWrapper>()) { + qlistPtr->append(qobjectWrapper->object()); + return true; + } - if (v.userType() == callType) { - *qvariantPtr = v; - } else if (v.canConvert(callType)) { - *qvariantPtr = v; - qvariantPtr->convert(callType); - } else { - QQmlMetaObject mo = ep ? ep->rawMetaObjectForType(callType) : QQmlMetaObject(); - if (!mo.isNull()) { - QObject *obj = ep->toQObject(v); + if (const QmlListWrapper *listWrapper = value.as<QmlListWrapper>()) { + *qlistPtr = listWrapper->d()->property()->toList<QList<QObject *>>(); + return true; + } - if (obj != nullptr && !QQmlMetaObject::canConvert(obj, mo)) { - *qvariantPtr = QVariant(callType, nullptr); - return false; - } + qlistPtr->append(nullptr); + return value.isNullOrUndefined(); + } - *qvariantPtr = QVariant(callType, &obj); + if (metaType.flags() & (QMetaType::PointerToQObject | QMetaType::PointerToGadget)) { + // You can assign null or undefined to any pointer. The result is a nullptr. + if (value.isNullOrUndefined()) { + qvariantPtr = new (&allocData) QVariant(metaType, nullptr); return true; } + break; + } - *qvariantPtr = QVariant(callType, (void *)nullptr); - return false; + if (type == qMetaTypeId<std::vector<int>>()) { + if (fromContainerValue<std::vector<int>>(value, &CallArgument::stdVectorIntPtr)) + return true; + } else if (type == qMetaTypeId<std::vector<qreal>>()) { + if (fromContainerValue<std::vector<qreal>>(value, &CallArgument::stdVectorRealPtr)) + return true; + } else if (type == qMetaTypeId<std::vector<bool>>()) { + if (fromContainerValue<std::vector<bool>>(value, &CallArgument::stdVectorBoolPtr)) + return true; + } else if (type == qMetaTypeId<std::vector<QString>>()) { + if (fromContainerValue<std::vector<QString>>(value, &CallArgument::stdVectorQStringPtr)) + return true; + } else if (type == qMetaTypeId<std::vector<QUrl>>()) { + if (fromContainerValue<std::vector<QUrl>>(value, &CallArgument::stdVectorQUrlPtr)) + return true; +#if QT_CONFIG(qml_itemmodel) + } else if (type == qMetaTypeId<std::vector<QModelIndex>>()) { + if (fromContainerValue<std::vector<QModelIndex>>( + value, &CallArgument::stdVectorQModelIndexPtr)) { + return true; + } +#endif } + break; } - return true; -} -QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine) -{ - QV4::Scope scope(engine); + // Convert via QVariant through the QML engine. + qvariantPtr = new (&allocData) QVariant(metaType); + type = QVariantWrappedType; - if (type == qMetaTypeId<QJSValue>()) { - return QJSValuePrivate::convertedToValue(scope.engine, *qjsValuePtr); - } else if (type == QMetaType::Int) { - return QV4::Encode(int(intValue)); - } else if (type == QMetaType::UInt) { - return QV4::Encode((uint)intValue); - } else if (type == QMetaType::Bool) { - return QV4::Encode(boolValue); - } else if (type == QMetaType::Double) { - return QV4::Encode(doubleValue); - } else if (type == QMetaType::Float) { - return QV4::Encode(floatValue); - } else if (type == QMetaType::QString) { - return QV4::Encode(engine->newString(*qstringPtr)); - } else if (type == QMetaType::QByteArray) { - return QV4::Encode(engine->newArrayBuffer(*qbyteArrayPtr)); - } else if (type == QMetaType::QObjectStar) { - QObject *object = qobjectPtr; - if (object) - QQmlData::get(object, true)->setImplicitDestructible(); - return QV4::QObjectWrapper::wrap(scope.engine, 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::ScopedArrayObject array(scope, scope.engine->newArrayObject()); - array->arrayReserve(list.count()); - QV4::ScopedValue v(scope); - for (int ii = 0; ii < list.count(); ++ii) - array->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(scope.engine, list.at(ii)))); - array->setArrayLengthUnchecked(list.count()); - return array.asReturnedValue(); - } else if (type == QMetaType::QJsonArray) { - return QV4::JsonObject::fromJsonArray(scope.engine, *jsonArrayPtr); - } else if (type == QMetaType::QJsonObject) { - return QV4::JsonObject::fromJsonObject(scope.engine, *jsonObjectPtr); - } else if (type == QMetaType::QJsonValue) { - return QV4::JsonObject::fromJsonValue(scope.engine, *jsonValuePtr); - } else if (type == -1 || type == qMetaTypeId<QVariant>()) { - QVariant value = *qvariantPtr; - QV4::ScopedValue rv(scope, scope.engine->fromVariant(value)); - QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, rv); + if (ExecutionEngine::metaTypeFromJS(value, metaType, qvariantPtr->data())) + return true; + + const QVariant v = ExecutionEngine::toVariant(value, metaType); + return QMetaType::convert(v.metaType(), v.constData(), metaType, qvariantPtr->data()); +} + +ReturnedValue CallArgument::toValue(ExecutionEngine *engine) +{ + switch (type) { + case QMetaType::Int: + return Encode(int(intValue)); + case QMetaType::UInt: + return Encode((uint)intValue); + case QMetaType::Bool: + return Encode(boolValue); + case QMetaType::Double: + return Encode(doubleValue); + case QMetaType::Float: + return Encode(floatValue); + case QMetaType::QString: + return Encode(engine->newString(*qstringPtr)); + case QMetaType::QByteArray: + return Encode(engine->newArrayBuffer(*qbyteArrayPtr)); + case QMetaType::QObjectStar: + if (qobjectPtr) + QQmlData::get(qobjectPtr, true)->setImplicitDestructible(); + return QObjectWrapper::wrap(engine, qobjectPtr); + case QMetaType::QJsonArray: + return JsonObject::fromJsonArray(engine, *jsonArrayPtr); + case QMetaType::QJsonObject: + return JsonObject::fromJsonObject(engine, *jsonObjectPtr); + case QMetaType::QJsonValue: + return JsonObject::fromJsonValue(engine, *jsonValuePtr); + case QMetaType::QVariant: + case QVariantWrappedType: { + Scope scope(engine); + ScopedValue rv(scope, scope.engine->fromVariant(*qvariantPtr)); + Scoped<QObjectWrapper> qobjectWrapper(scope, rv); if (!!qobjectWrapper) { if (QObject *object = qobjectWrapper->object()) QQmlData::get(object, true)->setImplicitDestructible(); } return rv->asReturnedValue(); - } else { - return QV4::Encode::undefined(); } -} + default: + break; + } -ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index) -{ - Scope valueScope(scope); - Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope)); - method->d()->setObject(object); + if (type == qMetaTypeId<QJSValue>()) { + // The QJSValue can be passed around via dataPtr() + QJSValuePrivate::manageStringOnV4Heap(engine, qjsValuePtr); + return QJSValuePrivate::asReturnedValue(qjsValuePtr); + } - if (QQmlData *ddata = QQmlData::get(object)) - method->d()->setPropertyCache(ddata->propertyCache); + 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; + Scope scope(engine); + ScopedArrayObject array(scope, engine->newArrayObject()); + array->arrayReserve(list.size()); + ScopedValue v(scope); + for (int ii = 0; ii < list.size(); ++ii) + array->arrayPut(ii, (v = QObjectWrapper::wrap(engine, list.at(ii)))); + array->setArrayLengthUnchecked(list.size()); + return array.asReturnedValue(); + } - method->d()->index = index; - return method.asReturnedValue(); + return Encode::undefined(); } -ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index) +ReturnedValue QObjectMethod::create(ExecutionEngine *engine, Heap::Object *wrapper, int index) { - Scope valueScope(scope); - Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope)); - method->d()->setPropertyCache(valueType->propertyCache()); - method->d()->index = index; - method->d()->valueTypeWrapper.set(valueScope.engine, valueType); + Scope valueScope(engine); + Scoped<QObjectMethod> method( + valueScope, + engine->memoryManager->allocate<QObjectMethod>(engine, wrapper, index)); return method.asReturnedValue(); } -void Heap::QObjectMethod::init(QV4::ExecutionContext *scope) +ReturnedValue QObjectMethod::create( + ExecutionEngine *engine, Heap::QQmlValueTypeWrapper *valueType, int index) { - Heap::FunctionObject::init(scope); + Scope valueScope(engine); + Scoped<QObjectMethod> method( + valueScope, + engine->memoryManager->allocate<QObjectMethod>(engine, valueType, index)); + return method.asReturnedValue(); } -const QMetaObject *Heap::QObjectMethod::metaObject() +ReturnedValue QObjectMethod::create( + ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom, + Heap::Object *wrapper, Heap::Object *object) { - if (propertyCache()) - return propertyCache()->createMetaObject(); - return object()->metaObject(); -} + Scope valueScope(engine); -QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionEngine *engine) const -{ - QString result; - if (const QMetaObject *metaObject = d()->metaObject()) { + Scoped<QQmlValueTypeWrapper> valueTypeWrapper(valueScope); + if (cloneFrom->wrapper) { + Scoped<QQmlValueTypeWrapper> ref(valueScope, cloneFrom->wrapper); + if (ref) { + valueTypeWrapper = QQmlValueTypeWrapper::create(engine, ref->d(), wrapper); + } else { + // We cannot re-attach a plain QQmlValueTypeWrapper because don't we know what + // value we should operate on. Without knowledge of the property the value + // was read from, we cannot load the value from the given object. + return Encode::undefined(); + } + } - result += QString::fromUtf8(metaObject->className()) + - QLatin1String("(0x") + QString::number((quintptr)d()->object(),16); + Scoped<QObjectMethod> method( + valueScope, + engine->memoryManager->allocate<QV4::QObjectMethod>( + engine, valueTypeWrapper ? valueTypeWrapper->d() : object, cloneFrom->index)); - if (d()->object()) { - QString objectName = d()->object()->objectName(); - if (!objectName.isEmpty()) - result += QLatin1String(", \"") + objectName + QLatin1Char('\"'); - } + method->d()->methodCount = cloneFrom->methodCount; - result += QLatin1Char(')'); - } else { - result = QLatin1String("null"); + Q_ASSERT(method->d()->methods == nullptr); + switch (cloneFrom->methodCount) { + case 0: + Q_ASSERT(cloneFrom->methods == nullptr); + break; + case 1: + Q_ASSERT(cloneFrom->methods + == reinterpret_cast<QQmlPropertyData *>(&cloneFrom->_singleMethod)); + method->d()->methods = reinterpret_cast<QQmlPropertyData *>(&method->d()->_singleMethod); + *method->d()->methods = *cloneFrom->methods; + break; + default: + Q_ASSERT(cloneFrom->methods != nullptr); + method->d()->methods = new QQmlPropertyData[cloneFrom->methodCount]; + memcpy(method->d()->methods, cloneFrom->methods, + cloneFrom->methodCount * sizeof(QQmlPropertyData)); + break; } - return engine->newString(result)->asReturnedValue(); + return method.asReturnedValue(); } -QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionEngine *engine, const Value *args, int argc) const +void Heap::QObjectMethod::init(QV4::ExecutionEngine *engine, Object *object, int methodIndex) { - if (!d()->object()) - return Encode::undefined(); - if (QQmlData::keepAliveDuringGarbageCollection(d()->object())) - return engine->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object")); + Heap::FunctionObject::init(engine); + wrapper.set(engine, object); + index = methodIndex; +} - int delay = 0; - if (argc > 0) - delay = args[0].toUInt32(); +const QMetaObject *Heap::QObjectMethod::metaObject() const +{ + Scope scope(internalClass->engine); - if (delay > 0) - QTimer::singleShot(delay, d()->object(), SLOT(deleteLater())); - else - d()->object()->deleteLater(); + if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper) + return valueWrapper->metaObject(); + if (QObject *self = object()) + return self->metaObject(); - return Encode::undefined(); + return nullptr; } -ReturnedValue QObjectMethod::virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc) +QObject *Heap::QObjectMethod::object() const { - const QObjectMethod *This = static_cast<const QObjectMethod*>(m); - return This->callInternal(thisObject, argv, argc); + Scope scope(internalClass->engine); + + if (Scoped<QV4::QObjectWrapper> objectWrapper(scope, wrapper); objectWrapper) + return objectWrapper->object(); + if (Scoped<QV4::QQmlTypeWrapper> typeWrapper(scope, wrapper); typeWrapper) + return typeWrapper->object(); + return nullptr; } -ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *argv, int argc) const +bool Heap::QObjectMethod::isDetached() const { - ExecutionEngine *v4 = engine(); - if (d()->index == DestroyMethod) - return method_destroy(v4, argv, argc); - else if (d()->index == ToStringMethod) - return method_toString(v4); + if (!wrapper) + return true; - QQmlObjectOrGadget object(d()->object()); - if (!d()->object()) { - if (!d()->valueTypeWrapper) - return Encode::undefined(); + QV4::Scope scope(internalClass->engine); + if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper) + return valueWrapper->d()->object() == nullptr; + + return false; +} + +bool Heap::QObjectMethod::isAttachedTo(QObject *o) const +{ + QV4::Scope scope(internalClass->engine); + if (Scoped<QV4::QObjectWrapper> objectWrapper(scope, wrapper); objectWrapper) + return objectWrapper->object() == o; + if (Scoped<QV4::QQmlTypeWrapper> typeWrapper(scope, wrapper); typeWrapper) + return typeWrapper->object() == o; - object = QQmlObjectOrGadget(d()->propertyCache(), d()->valueTypeWrapper->gadgetPtr()); + if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper) { + QV4::Scope scope(wrapper->internalClass->engine); + if (QV4::Scoped<QV4::QObjectWrapper> qobject(scope, valueWrapper->d()->object()); qobject) + return qobject->object() == o; + if (QV4::Scoped<QV4::QQmlTypeWrapper> type(scope, valueWrapper->d()->object()); type) + return type->object() == o; + + // Attached to some nested value type or sequence object + return false; } - QQmlPropertyData method; + return false; +} - if (d()->propertyCache()) { - QQmlPropertyData *data = d()->propertyCache()->method(d()->index); - if (!data) - return QV4::Encode::undefined(); - method = *data; - } else { - const QMetaObject *mo = d()->object()->metaObject(); - const QMetaMethod moMethod = mo->method(d()->index); - method.load(moMethod); - - if (method.coreIndex() == -1) - return QV4::Encode::undefined(); - - // Look for overloaded methods - QByteArray methodName = moMethod.name(); - const int methodOffset = mo->methodOffset(); - for (int ii = d()->index - 1; ii >= methodOffset; --ii) { - if (methodName == mo->method(ii).name()) { - method.setOverload(true); - method.setOverrideIndexIsProperty(0); - method.setOverrideIndex(ii); - break; - } - } +Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject( + const QMetaObject *thisMeta) const +{ + // Check that the metaobject matches. + + if (!thisMeta) { + // You can only get a detached method via a lookup, and then you have a thisObject. + Q_ASSERT(wrapper); + return Included; } - Scope scope(v4); - JSCallData cData(scope, argc, argv, thisObject); - CallData *callData = cData.callData(); + const auto check = [&](const QMetaObject *included) { + const auto stackFrame = internalClass->engine->currentStackFrame; + if (stackFrame && !stackFrame->v4Function->executableCompilationUnit() + ->nativeMethodsAcceptThisObjects()) { + qCWarning(lcMethodBehavior, + "%s:%d: Calling C++ methods with 'this' objects different from the one " + "they were retrieved from is broken, due to historical reasons. The " + "original object is used as 'this' object. You can allow the given " + "'this' object to be used by setting " + "'pragma NativeMethodBehavior: AcceptThisObject'", + qPrintable(stackFrame->source()), stackFrame->lineNumber()); + return Included; + } - if (method.isV4Function()) { - QV4::ScopedValue rv(scope, QV4::Value::undefinedValue()); - QQmlV4Function func(callData, rv, v4); - QQmlV4Function *funcptr = &func; + // destroy() and toString() can be called on all QObjects, but not on gadgets. + if (index < 0) + return thisMeta->inherits(&QObject::staticMetaObject) ? Explicit : Invalid; - void *args[] = { nullptr, &funcptr }; - object.metacall(QMetaObject::InvokeMetaMethod, method.coreIndex(), args); + // Find the base type the method belongs to. + int methodOffset = included->methodOffset(); + while (true) { + if (included == thisMeta) + return Explicit; - return rv->asReturnedValue(); - } + if (methodOffset <= index) + return thisMeta->inherits(included) ? Explicit : Invalid; - if (!method.isOverload()) { - return CallPrecise(object, method, v4, callData); - } else { - return CallOverloaded(object, method, v4, callData, d()->propertyCache()); - } -} + included = included->superClass(); + Q_ASSERT(included); + methodOffset -= QMetaObjectPrivate::get(included)->methodCount; + }; -DEFINE_OBJECT_VTABLE(QObjectMethod); + Q_UNREACHABLE_RETURN(Invalid); + }; + if (const QMetaObject *meta = metaObject()) + return check(meta); -void Heap::QMetaObjectWrapper::init(const QMetaObject *metaObject) -{ - FunctionObject::init(); - this->metaObject = metaObject; - constructors = nullptr; - constructorCount = 0; + // If the QObjectMethod is detached, we can only have gotten here via a lookup. + // The lookup checks that the QQmlPropertyCache matches. + return Explicit; } -void Heap::QMetaObjectWrapper::destroy() +QString Heap::QObjectMethod::name() const { - delete[] constructors; -} + if (index == QV4::QObjectMethod::DestroyMethod) + return QStringLiteral("destroy"); + else if (index == QV4::QObjectMethod::ToStringMethod) + return QStringLiteral("toString"); -void Heap::QMetaObjectWrapper::ensureConstructorsCache() { + const QMetaObject *mo = metaObject(); + if (!mo) + return QString(); - const int count = metaObject->constructorCount(); - if (constructorCount != count) { - delete[] constructors; - constructorCount = count; - if (count == 0) { - constructors = nullptr; - return; - } - constructors = new QQmlPropertyData[count]; - - for (int i = 0; i < count; ++i) { - QMetaMethod method = metaObject->constructor(i); - QQmlPropertyData &d = constructors[i]; - d.load(method); - d.setCoreIndex(i); - } + int methodOffset = mo->methodOffset(); + while (methodOffset > index) { + mo = mo->superClass(); + methodOffset -= QMetaObjectPrivate::get(mo)->methodCount; } + + return "%1::%2"_L1.arg(QLatin1StringView{mo->className()}, + QLatin1StringView{mo->method(index).name()}); } +void Heap::QObjectMethod::ensureMethodsCache(const QMetaObject *thisMeta) +{ + if (methods) { + Q_ASSERT(methodCount > 0); + return; + } -ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) { + const QMetaObject *mo = metaObject(); - QV4::Scope scope(engine); - Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocate<QV4::QMetaObjectWrapper>(metaObject)->asReturnedValue()); - mo->init(engine); - return mo->asReturnedValue(); -} + if (!mo) + mo = thisMeta; -void QMetaObjectWrapper::init(ExecutionEngine *) { - const QMetaObject & mo = *d()->metaObject; + Q_ASSERT(mo); - for (int i = 0; i < mo.enumeratorCount(); i++) { - QMetaEnum Enum = mo.enumerator(i); - for (int k = 0; k < Enum.keyCount(); k++) { - const char* key = Enum.key(k); - const int value = Enum.value(k); - defineReadonlyProperty(QLatin1String(key), Value::fromInt32(value)); + int methodOffset = mo->methodOffset(); + while (methodOffset > index) { + mo = mo->superClass(); + methodOffset -= QMetaObjectPrivate::get(mo)->methodCount; + } + QVarLengthArray<QQmlPropertyData, 9> resolvedMethods; + QQmlPropertyData dummy; + QMetaMethod method = mo->method(index); + dummy.load(method); + dummy.setMetaObject(mo); + resolvedMethods.append(dummy); + // Look for overloaded methods + QByteArray methodName = method.name(); + for (int ii = index - 1; ii >= methodOffset; --ii) { + if (methodName == mo->method(ii).name()) { + method = mo->method(ii); + dummy.load(method); + resolvedMethods.append(dummy); } } + if (resolvedMethods.size() > 1) { + methods = new QQmlPropertyData[resolvedMethods.size()]; + memcpy(methods, resolvedMethods.data(), resolvedMethods.size()*sizeof(QQmlPropertyData)); + methodCount = resolvedMethods.size(); + } else { + methods = reinterpret_cast<QQmlPropertyData *>(&_singleMethod); + *methods = resolvedMethods.at(0); + methodCount = 1; + } + + Q_ASSERT(methodCount > 0); } -ReturnedValue QMetaObjectWrapper::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +ReturnedValue QObjectMethod::method_toString(ExecutionEngine *engine, QObject *o) const { - const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(f); - return This->constructInternal(argv, argc); + return engine->newString( + QObjectWrapper::objectToString( + engine, o ? o->metaObject() : d()->metaObject(), o))->asReturnedValue(); } -ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc) const +ReturnedValue QObjectMethod::method_destroy( + ExecutionEngine *engine, QObject *o, const Value *args, int argc) const { + if (!o) + return Encode::undefined(); - d()->ensureConstructorsCache(); - - ExecutionEngine *v4 = engine(); - const QMetaObject* mo = d()->metaObject; - if (d()->constructorCount == 0) { - return v4->throwTypeError(QLatin1String(mo->className()) - + QLatin1String(" has no invokable constructor")); - } + if (QQmlData::keepAliveDuringGarbageCollection(o)) + return engine->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object")); - Scope scope(v4); - Scoped<QObjectWrapper> object(scope); - JSCallData cData(scope, argc, argv); - CallData *callData = cData.callData(); + int delay = 0; + if (argc > 0) + delay = args[0].toUInt32(); - if (d()->constructorCount == 1) { - object = callConstructor(d()->constructors[0], v4, callData); - } - else { - object = callOverloadedConstructor(v4, callData); - } - Scoped<QMetaObjectWrapper> metaObject(scope, this); - object->defineDefaultProperty(v4->id_constructor(), metaObject); - object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this)); - return object.asReturnedValue(); + if (delay > 0) + QTimer::singleShot(delay, o, SLOT(deleteLater())); + else + o->deleteLater(); + return Encode::undefined(); } -ReturnedValue QMetaObjectWrapper::callConstructor(const QQmlPropertyData &data, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const { +ReturnedValue QObjectMethod::virtualCall( + const FunctionObject *m, const Value *thisObject, const Value *argv, int argc) +{ + const QObjectMethod *This = static_cast<const QObjectMethod*>(m); + return This->callInternal(thisObject, argv, argc); +} - const QMetaObject* mo = d()->metaObject; - const QQmlStaticMetaObject object(mo); - return CallPrecise(object, data, engine, callArgs, QMetaObject::CreateInstance); +void QObjectMethod::virtualCallWithMetaTypes( + const FunctionObject *m, QObject *thisObject, void **argv, const QMetaType *types, int argc) +{ + const QObjectMethod *This = static_cast<const QObjectMethod*>(m); + This->callInternalWithMetaTypes(thisObject, argv, types, argc); } +ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *argv, int argc) const +{ + ExecutionEngine *v4 = engine(); -ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const { - const int numberOfConstructors = d()->constructorCount; - const int argumentCount = callArgs->argc(); - const QQmlStaticMetaObject object(d()->metaObject); + const QMetaObject *thisMeta = nullptr; + + QObject *o = nullptr; + Heap::QQmlValueTypeWrapper *valueWrapper = nullptr; + if (const QObjectWrapper *w = thisObject->as<QObjectWrapper>()) { + thisMeta = w->metaObject(); + o = w->object(); + } else if (const QQmlTypeWrapper *w = thisObject->as<QQmlTypeWrapper>()) { + thisMeta = w->metaObject(); + o = w->object(); + } else if (const QQmlValueTypeWrapper *w = thisObject->as<QQmlValueTypeWrapper>()) { + thisMeta = w->metaObject(); + valueWrapper = w->d(); + } - QQmlPropertyData best; - int bestParameterScore = INT_MAX; - int bestMatchScore = INT_MAX; + Heap::QObjectMethod::ThisObjectMode mode = Heap::QObjectMethod::Invalid; + if (o && o == d()->object()) { + mode = Heap::QObjectMethod::Explicit; + // Nothing to do; objects are the same. This should be common + } else if (valueWrapper && valueWrapper == d()->wrapper) { + mode = Heap::QObjectMethod::Explicit; + // Nothing to do; gadgets are the same. This should be somewhat common + } else { + mode = d()->checkThisObject(thisMeta); + if (mode == Heap::QObjectMethod::Invalid) { + v4->throwError(QLatin1String("Cannot call method %1 on %2").arg( + d()->name(), thisObject->toQStringNoThrow())); + return Encode::undefined(); + } + } - QV4::Scope scope(engine); - QV4::ScopedValue v(scope); - - for (int i = 0; i < numberOfConstructors; i++) { - const QQmlPropertyData & attempt = d()->constructors[i]; - QQmlMetaObject::ArgTypeStorage storage; - int methodArgumentCount = 0; - int *methodArgTypes = nullptr; - if (attempt.hasArguments()) { - int *args = object.constructorParameterTypes(attempt.coreIndex(), &storage, nullptr); - if (!args) // Must be an unknown argument - continue; + QQmlObjectOrGadget object = [&](){ + if (mode == Heap::QObjectMethod::Included) { + QV4::Scope scope(v4); + if (QV4::Scoped<QV4::QObjectWrapper> qobject(scope, d()->wrapper); qobject) + return QQmlObjectOrGadget(qobject->object()); + if (QV4::Scoped<QV4::QQmlTypeWrapper> type(scope, d()->wrapper); type) + return QQmlObjectOrGadget(type->object()); + if (QV4::Scoped<QV4::QQmlValueTypeWrapper> value(scope, d()->wrapper); value) { + valueWrapper = value->d(); + return QQmlObjectOrGadget(valueWrapper->metaObject(), valueWrapper->gadgetPtr()); + } + Q_UNREACHABLE(); + } else { + if (o) + return QQmlObjectOrGadget(o); - methodArgumentCount = args[0]; - methodArgTypes = args + 1; + Q_ASSERT(valueWrapper); + if (!valueWrapper->enforcesLocation()) + QV4::ReferenceObject::readReference(valueWrapper); + return QQmlObjectOrGadget(thisMeta, valueWrapper->gadgetPtr()); } + }(); - if (methodArgumentCount > argumentCount) - continue; // We don't have sufficient arguments to call this method + if (object.isNull()) + return Encode::undefined(); - int methodParameterScore = argumentCount - methodArgumentCount; - if (methodParameterScore > bestParameterScore) - continue; // We already have a better option + if (d()->index == DestroyMethod) + return method_destroy(v4, object.qObject(), argv, argc); + else if (d()->index == ToStringMethod) + return method_toString(v4, object.qObject()); - int methodMatchScore = 0; - for (int ii = 0; ii < methodArgumentCount; ++ii) { - methodMatchScore += MatchScore((v = Value::fromStaticValue(callArgs->args[ii])), - methodArgTypes[ii]); - } + d()->ensureMethodsCache(thisMeta); - if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { - best = attempt; - bestParameterScore = methodParameterScore; - bestMatchScore = methodMatchScore; + Scope scope(v4); + JSCallData cData(thisObject, argv, argc); + CallData *callData = cData.callData(scope); + + const QQmlPropertyData *method = d()->methods; + + // If we call the method, we have to write back any value type references afterwards. + // The method might change the value. + const auto doCall = [&](const auto &call) { + if (!method->isConstant()) { + if (valueWrapper && valueWrapper->isReference()) { + ScopedValue rv(scope, call()); + valueWrapper->writeBack(); + return rv->asReturnedValue(); + } } - if (bestParameterScore == 0 && bestMatchScore == 0) - break; // We can't get better than that + return call(); }; - if (best.isValid()) { - return CallPrecise(object, best, engine, callArgs, QMetaObject::CreateInstance); - } else { - QString error = QLatin1String("Unable to determine callable overload. Candidates are:"); - for (int i = 0; i < numberOfConstructors; i++) { - const QQmlPropertyData & candidate = d()->constructors[i]; - error += QLatin1String("\n ") + - QString::fromUtf8(d()->metaObject->constructor(candidate.coreIndex()) - .methodSignature()); - } + if (d()->methodCount != 1) { + Q_ASSERT(d()->methodCount > 0); + method = resolveOverloaded(object, d()->methods, d()->methodCount, v4, callData); + if (method == nullptr) + return Encode::undefined(); + } + + if (method->isV4Function()) { + return doCall([&]() { + ScopedValue rv(scope, Value::undefinedValue()); + QQmlV4Function func(callData, rv, v4); + QQmlV4FunctionPtr funcptr = &func; - return engine->throwError(error); + void *args[] = { nullptr, &funcptr }; + object.metacall(QMetaObject::InvokeMetaMethod, method->coreIndex(), args); + + return rv->asReturnedValue(); + }); } + + return doCall([&]() { return callPrecise(object, *method, v4, callData); }); } -bool QMetaObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b) +struct ToStringMetaMethod { - Q_ASSERT(a->as<QMetaObjectWrapper>()); - QMetaObjectWrapper *aMetaObject = a->as<QMetaObjectWrapper>(); - QMetaObjectWrapper *bMetaObject = b->as<QMetaObjectWrapper>(); - if (!bMetaObject) - return true; - return aMetaObject->metaObject() == bMetaObject->metaObject(); -} + constexpr int parameterCount() const { return 0; } + constexpr QMetaType returnMetaType() const { return QMetaType::fromType<QString>(); } + constexpr QMetaType parameterMetaType(int) const { return QMetaType(); } +}; -DEFINE_OBJECT_VTABLE(QMetaObjectWrapper); +void QObjectMethod::callInternalWithMetaTypes( + QObject *thisObject, void **argv, const QMetaType *types, int argc) const +{ + ExecutionEngine *v4 = engine(); + + const QMetaObject *thisMeta = nullptr; + Heap::QQmlValueTypeWrapper *valueWrapper = nullptr; + + if (thisObject) { + thisMeta = thisObject->metaObject(); + } else { + Q_ASSERT(Value::fromHeapObject(d()->wrapper).as<QQmlValueTypeWrapper>()); + valueWrapper = d()->wrapper.cast<Heap::QQmlValueTypeWrapper>(); + thisMeta = valueWrapper->metaObject(); + } + + QQmlObjectOrGadget object = [&](){ + if (thisObject) + return QQmlObjectOrGadget(thisObject); + Scope scope(v4); + Scoped<QQmlValueTypeWrapper> wrapper(scope, d()->wrapper); + Q_ASSERT(wrapper); + Heap::QQmlValueTypeWrapper *valueWrapper = wrapper->d(); + if (!valueWrapper->enforcesLocation()) + QV4::ReferenceObject::readReference(valueWrapper); + return QQmlObjectOrGadget(thisMeta, valueWrapper->gadgetPtr()); + }(); + if (object.isNull()) + return; + + if (d()->index == DestroyMethod) { + // method_destroy will use at most one argument + QV4::convertAndCall( + v4, thisObject, argv, types, std::min(argc, 1), + [this, v4, object](const Value *thisObject, const Value *argv, int argc) { + Q_UNUSED(thisObject); + return method_destroy(v4, object.qObject(), argv, argc); + }); + return; + } + + if (d()->index == ToStringMethod) { + const ToStringMetaMethod metaMethod; + QV4::coerceAndCall( + v4, &metaMethod, argv, types, argc, + [v4, thisMeta, object](void **argv, int) { + *static_cast<QString *>(argv[0]) + = QObjectWrapper::objectToString(v4, thisMeta, object.qObject()); + }); + return; + } + + d()->ensureMethodsCache(thisMeta); + + const QQmlPropertyData *method = d()->methods; + if (d()->methodCount != 1) { + Q_ASSERT(d()->methodCount > 0); + method = resolveOverloaded(d()->methods, d()->methodCount, argv, argc, types); + } + + if (!method || method->isV4Function()) { + QV4::convertAndCall( + v4, thisObject, argv, types, argc, + [this](const Value *thisObject, const Value *argv, int argc) { + return callInternal(thisObject, argv, argc); + }); + } else { + const QMetaMethod metaMethod = method->metaMethod(); + QV4::coerceAndCall( + v4, &metaMethod, argv, types, argc, + [v4, object, valueWrapper, method](void **argv, int argc) { + Q_UNUSED(argc); + + // If we call the method, we have to write back any value type references afterwards. + // The method might change the value. + object.metacall(QMetaObject::InvokeMetaMethod, method->coreIndex(), argv); + if (!method->isConstant()) { + if (valueWrapper && valueWrapper->isReference()) + valueWrapper->writeBack(); + } + + // If the method returns a QObject* we need to track it on the JS heap + // (if it's destructible). + QObject *qobjectPtr = nullptr; + const QMetaType resultType = method->propType(); + if (argv[0]) { + if (resultType.flags() & QMetaType::PointerToQObject) { + qobjectPtr = *static_cast<QObject **>(argv[0]); + } else if (resultType == QMetaType::fromType<QVariant>()) { + const QVariant *result = static_cast<const QVariant *>(argv[0]); + const QMetaType variantType = result->metaType(); + if (variantType.flags() & QMetaType::PointerToQObject) + qobjectPtr = *static_cast<QObject *const *>(result->data()); + } + } + + if (qobjectPtr) { + QQmlData *ddata = QQmlData::get(qobjectPtr, true); + if (!ddata->explicitIndestructibleSet) { + ddata->indestructible = false; + QObjectWrapper::ensureWrapper(v4, qobjectPtr); + } + } + }); + } +} + +DEFINE_OBJECT_VTABLE(QObjectMethod); void Heap::QmlSignalHandler::init(QObject *object, int signalIndex) { @@ -2321,56 +3080,59 @@ void Heap::QmlSignalHandler::init(QObject *object, int signalIndex) DEFINE_OBJECT_VTABLE(QmlSignalHandler); -void QmlSignalHandler::initProto(ExecutionEngine *engine) +ReturnedValue QmlSignalHandler::call(const Value *thisObject, const Value *argv, int argc) const { - if (engine->signalHandlerPrototype()->d_unchecked()) - return; + const QString handlerName = QQmlSignalNames::signalNameToHandlerName( + object()->metaObject()->method(signalIndex()).name()); + qCWarning(lcSignalHandler).noquote() + << QStringLiteral("Property '%1' of object %2 is a signal handler. You should " + "not call it directly. Make it a proper function and call " + "that or emit the signal.") + .arg(handlerName, thisObject->toQStringNoThrow()); - Scope scope(engine); - ScopedObject o(scope, engine->newObject()); - QV4::ScopedString connect(scope, engine->newIdentifier(QStringLiteral("connect"))); - QV4::ScopedString disconnect(scope, engine->newIdentifier(QStringLiteral("disconnect"))); - o->put(connect, QV4::ScopedValue(scope, engine->functionPrototype()->get(connect))); - o->put(disconnect, QV4::ScopedValue(scope, engine->functionPrototype()->get(disconnect))); + Scope scope(engine()); + Scoped<QObjectMethod> method( + scope, QObjectMethod::create( + scope.engine, + static_cast<Heap::QObjectWrapper *>(nullptr), + signalIndex())); - engine->jsObjects[QV4::ExecutionEngine::SignalHandlerProto] = o->d(); + return method->call(thisObject, argv, argc); } -void MultiplyWrappedQObjectMap::insert(QObject *key, Heap::Object *value) +void QmlSignalHandler::initProto(ExecutionEngine *engine) { - QHash<QObject*, QV4::WeakValue>::operator[](key).set(value->internalClass->engine, value); - connect(key, SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*))); -} - + if (engine->signalHandlerPrototype()->d_unchecked()) + return; + Scope scope(engine); + ScopedObject o(scope, engine->newObject()); + ScopedString connect(scope, engine->newIdentifier(QStringLiteral("connect"))); + ScopedString disconnect(scope, engine->newIdentifier(QStringLiteral("disconnect"))); + o->put(connect, ScopedValue(scope, engine->functionPrototype()->get(connect))); + o->put(disconnect, ScopedValue(scope, engine->functionPrototype()->get(disconnect))); -MultiplyWrappedQObjectMap::Iterator MultiplyWrappedQObjectMap::erase(MultiplyWrappedQObjectMap::Iterator it) -{ - disconnect(it.key(), SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*))); - return QHash<QObject*, QV4::WeakValue>::erase(it); + engine->jsObjects[ExecutionEngine::SignalHandlerProto] = o->d(); } -void MultiplyWrappedQObjectMap::remove(QObject *key) -{ - Iterator it = find(key); - if (it == end()) - return; - erase(it); -} -void MultiplyWrappedQObjectMap::mark(QObject *key, MarkStack *markStack) +MultiplyWrappedQObjectMap::Iterator MultiplyWrappedQObjectMap::erase( + MultiplyWrappedQObjectMap::Iterator it) { - Iterator it = find(key); - if (it == end()) - return; - it->markOnce(markStack); + const QObjectBiPointer key = it.key(); + const QObject *obj = key.isT1() ? key.asT1() : key.asT2(); + disconnect(obj, &QObject::destroyed, this, &MultiplyWrappedQObjectMap::removeDestroyedObject); + return QHash<QObjectBiPointer, WeakValue>::erase(it); } void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object) { - QHash<QObject*, QV4::WeakValue>::remove(object); + QHash<QObjectBiPointer, WeakValue>::remove(object); + QHash<QObjectBiPointer, WeakValue>::remove(static_cast<const QObject *>(object)); } +} // namespace QV4 + QT_END_NAMESPACE #include "moc_qv4qobjectwrapper_p.cpp" diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index ac9cad2bdb..afd5b8ddc8 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4QOBJECTWRAPPER_P_H #define QV4QOBJECTWRAPPER_P_H @@ -51,23 +15,27 @@ // We mean it. // +#include <private/qbipointer_p.h> +#include <private/qintrusivelist_p.h> +#include <private/qqmldata_p.h> +#include <private/qv4functionobject_p.h> +#include <private/qv4lookup_p.h> +#include <private/qv4value_p.h> + #include <QtCore/qglobal.h> #include <QtCore/qmetatype.h> #include <QtCore/qpair.h> #include <QtCore/qhash.h> -#include <private/qqmldata_p.h> -#include <private/qintrusivelist_p.h> - -#include <private/qv4value_p.h> -#include <private/qv4functionobject_p.h> -#include <private/qv4lookup_p.h> QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcBuiltinsBindingRemoval) + class QObject; class QQmlData; class QQmlPropertyCache; class QQmlPropertyData; +class QQmlObjectOrGadget; namespace QV4 { struct QObjectSlotDispatcher; @@ -92,49 +60,44 @@ struct Q_QML_EXPORT QObjectWrapper : Object { static void markObjects(Heap::Base *that, MarkStack *markStack); private: - QQmlQPointer<QObject> qObj; + QV4QPointer<QObject> qObj; }; #define QObjectMethodMembers(class, Member) \ - Member(class, Pointer, QQmlValueTypeWrapper *, valueTypeWrapper) \ - Member(class, NoMark, QQmlQPointer<QObject>, qObj) \ - Member(class, NoMark, QQmlPropertyCache *, _propertyCache) \ - Member(class, NoMark, int, index) + Member(class, Pointer, Object *, wrapper) \ + +DECLARE_EXPORTED_HEAP_OBJECT(QObjectMethod, FunctionObject) { + DECLARE_MARKOBJECTS(QObjectMethod) -DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) { - DECLARE_MARKOBJECTS(QObjectMethod); + QQmlPropertyData *methods; + alignas(alignof(QQmlPropertyData)) std::byte _singleMethod[sizeof(QQmlPropertyData)]; + int methodCount; + int index; - void init(QV4::ExecutionContext *scope); + void init(QV4::ExecutionEngine *engine, Object *wrapper, int index); void destroy() { - setPropertyCache(nullptr); - qObj.destroy(); + if (methods != reinterpret_cast<const QQmlPropertyData *>(&_singleMethod)) + delete[] methods; FunctionObject::destroy(); } - QQmlPropertyCache *propertyCache() const { return _propertyCache; } - void setPropertyCache(QQmlPropertyCache *c) { - if (c) - c->addref(); - if (_propertyCache) - _propertyCache->release(); - _propertyCache = c; - } + void ensureMethodsCache(const QMetaObject *thisMeta); + QString name() const; - const QMetaObject *metaObject(); - QObject *object() const { return qObj.data(); } - void setObject(QObject *o) { qObj = o; } + const QMetaObject *metaObject() const; + QObject *object() const; -}; + bool isDetached() const; + bool isAttachedTo(QObject *o) const; -struct QMetaObjectWrapper : FunctionObject { - const QMetaObject* metaObject; - QQmlPropertyData *constructors; - int constructorCount; + enum ThisObjectMode { + Invalid, + Included, + Explicit, + }; - void init(const QMetaObject* metaObject); - void destroy(); - void ensureConstructorsCache(); + QV4::Heap::QObjectMethod::ThisObjectMode checkThisObject(const QMetaObject *thisMeta) const; }; struct QmlSignalHandler : Object { @@ -149,7 +112,7 @@ struct QmlSignalHandler : Object { void setObject(QObject *o) { qObj = o; } private: - QQmlQPointer<QObject> qObj; + QV4QPointer<QObject> qObj; }; } @@ -159,55 +122,108 @@ struct Q_QML_EXPORT QObjectWrapper : public Object V4_OBJECT2(QObjectWrapper, Object) V4_NEEDS_DESTROY - enum RevisionMode { IgnoreRevision, CheckRevision }; + enum Flag { + NoFlag = 0x0, + CheckRevision = 0x1, + AttachMethods = 0x2, + AllowOverride = 0x4, + IncludeImports = 0x8, + }; + + Q_DECLARE_FLAGS(Flags, Flag); static void initializeBindings(ExecutionEngine *engine); + const QMetaObject *metaObject() const + { + if (QObject *o = object()) + return o->metaObject(); + return nullptr; + } + QObject *object() const { return d()->object(); } - ReturnedValue getQmlProperty(QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, bool includeImports = false) const; - static ReturnedValue getQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, QQmlPropertyData **property = nullptr); + ReturnedValue getQmlProperty( + const QQmlRefPointer<QQmlContextData> &qmlContext, String *name, + Flags flags, bool *hasProperty = nullptr) const; - static bool setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, const Value &value); + static ReturnedValue getQmlProperty( + ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, + Heap::Object *wrapper, QObject *object, String *name, Flags flags, + bool *hasProperty = nullptr, const QQmlPropertyData **property = nullptr); + static bool setQmlProperty( + ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext, + QObject *object, String *name, Flags flags, const Value &value); + + Q_NODISCARD_X("Use ensureWrapper if you don't need the return value") static ReturnedValue wrap(ExecutionEngine *engine, QObject *object); + Q_NODISCARD_X("Throwing the const wrapper away can cause it to be garbage collected") + static ReturnedValue wrapConst(ExecutionEngine *engine, QObject *object); + static void ensureWrapper(ExecutionEngine *engine, QObject *object); static void markWrapper(QObject *object, MarkStack *markStack); using Object::get; static void setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value); void setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value); + static void setProperty( + ExecutionEngine *engine, QObject *object, + const QQmlPropertyData *property, const Value &value); void destroyObject(bool lastCall); - static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property); + static ReturnedValue getProperty( + ExecutionEngine *engine, Heap::Object *wrapper, QObject *object, + const QQmlPropertyData *property, Flags flags); static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); - static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object); - template <typename ReversalFunctor> static ReturnedValue lookupGetterImpl(Lookup *l, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revert); - static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value); + static ReturnedValue lookupAttached(Lookup *l, ExecutionEngine *engine, const Value &object); + + template <typename ReversalFunctor> static ReturnedValue lookupPropertyGetterImpl( + Lookup *l, ExecutionEngine *engine, const Value &object, + Flags flags, ReversalFunctor revert); + template <typename ReversalFunctor> static ReturnedValue lookupMethodGetterImpl( + Lookup *l, ExecutionEngine *engine, const Value &object, + Flags flags, ReversalFunctor revert); + static bool virtualResolveLookupSetter( + Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value); + static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target); -protected: - static void setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value); + static int virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a); + static QString objectToString( + ExecutionEngine *engine, const QMetaObject *metaObject, QObject *object); + +protected: static bool virtualIsEqualTo(Managed *that, Managed *o); static ReturnedValue create(ExecutionEngine *engine, QObject *object); - static QQmlPropertyData *findProperty(ExecutionEngine *engine, QObject *o, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local); - QQmlPropertyData *findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const; + static const QQmlPropertyData *findProperty( + QObject *o, const QQmlRefPointer<QQmlContextData> &qmlContext, + String *name, Flags flags, QQmlPropertyData *local); + + const QQmlPropertyData *findProperty( + const QQmlRefPointer<QQmlContextData> &qmlContext, + String *name, Flags flags, QQmlPropertyData *local) const; - static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static ReturnedValue virtualGet( + const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p); - static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target); - static ReturnedValue method_connect(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_disconnect(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_connect( + const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_disconnect( + const FunctionObject *, const Value *thisObject, const Value *argv, int argc); private: Q_NEVER_INLINE static ReturnedValue wrap_slowPath(ExecutionEngine *engine, QObject *object); + Q_NEVER_INLINE static ReturnedValue wrapConst_slowPath(ExecutionEngine *engine, QObject *object); }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QObjectWrapper::Flags) + inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *object) { if (Q_UNLIKELY(QQmlData::wasDeleted(object))) @@ -222,8 +238,29 @@ inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *obje return wrap_slowPath(engine, object); } +// Unfortunately we still need a non-const QObject* here because QQmlData needs to register itself in QObjectPrivate. +inline ReturnedValue QObjectWrapper::wrapConst(ExecutionEngine *engine, QObject *object) +{ + if (Q_UNLIKELY(QQmlData::wasDeleted(object))) + return QV4::Encode::null(); + + return wrapConst_slowPath(engine, object); +} + +inline bool canConvert(const QQmlPropertyCache *fromMo, const QQmlPropertyCache *toMo) +{ + while (fromMo) { + if (fromMo == toMo) + return true; + fromMo = fromMo->parent().data(); + } + return false; +} + template <typename ReversalFunctor> -inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revertLookup) +inline ReturnedValue QObjectWrapper::lookupPropertyGetterImpl( + Lookup *lookup, ExecutionEngine *engine, const Value &object, + QObjectWrapper::Flags flags, ReversalFunctor revertLookup) { // we can safely cast to a QV4::Object here. If object is something else, // the internal class won't match @@ -231,7 +268,7 @@ inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionE if (!o || o->internalClass != lookup->qobjectLookup.ic) return revertLookup(); - const Heap::QObjectWrapper *This = static_cast<const Heap::QObjectWrapper *>(o); + Heap::QObjectWrapper *This = static_cast<Heap::QObjectWrapper *>(o); QObject *qobj = This->object(); if (QQmlData::wasDeleted(qobj)) return QV4::Encode::undefined(); @@ -240,26 +277,68 @@ inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionE if (!ddata) return revertLookup(); - QQmlPropertyData *property = lookup->qobjectLookup.propertyData; - if (ddata->propertyCache != lookup->qobjectLookup.propertyCache) { - if (property->isOverridden() && (!useOriginalProperty || property->isFunction() || property->isSignalHandler())) + const QQmlPropertyData *property = lookup->qobjectLookup.propertyData; + if (ddata->propertyCache.data() != lookup->qobjectLookup.propertyCache) { + // If the property is overridden and the lookup allows overrides to be considered, + // we have to revert here and redo the lookup from scratch. + if (property->isOverridden() + && ((flags & AllowOverride) + || property->isFunction() + || property->isSignalHandler())) { return revertLookup(); - - QQmlPropertyCache *fromMo = ddata->propertyCache; - QQmlPropertyCache *toMo = lookup->qobjectLookup.propertyCache; - bool canConvert = false; - while (fromMo) { - if (fromMo == toMo) { - canConvert = true; - break; - } - fromMo = fromMo->parent(); } - if (!canConvert) + + if (!canConvert(ddata->propertyCache.data(), lookup->qobjectLookup.propertyCache)) return revertLookup(); } - return getProperty(engine, qobj, property); + return getProperty(engine, This, qobj, property, flags); +} + +template <typename ReversalFunctor> +inline ReturnedValue QObjectWrapper::lookupMethodGetterImpl( + Lookup *lookup, ExecutionEngine *engine, const Value &object, + QObjectWrapper::Flags flags, ReversalFunctor revertLookup) +{ + // we can safely cast to a QV4::Object here. If object is something else, + // the internal class won't match + Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); + if (!o || o->internalClass != lookup->qobjectMethodLookup.ic) + return revertLookup(); + + Heap::QObjectWrapper *This = static_cast<Heap::QObjectWrapper *>(o); + QObject *qobj = This->object(); + if (QQmlData::wasDeleted(qobj)) + return QV4::Encode::undefined(); + + QQmlData *ddata = QQmlData::get(qobj, /*create*/false); + if (!ddata) + return revertLookup(); + + const QQmlPropertyData *property = lookup->qobjectMethodLookup.propertyData; + if (ddata->propertyCache.data() != lookup->qobjectMethodLookup.propertyCache) { + if (property && property->isOverridden()) + return revertLookup(); + + if (!canConvert(ddata->propertyCache.data(), lookup->qobjectMethodLookup.propertyCache)) + return revertLookup(); + } + + if (Heap::QObjectMethod *method = lookup->qobjectMethodLookup.method) { + if (method->isDetached()) + return method->asReturnedValue(); + } + + if (!property) // was toString() or destroy() + return revertLookup(); + + QV4::Scope scope(engine); + QV4::ScopedValue v(scope, getProperty(engine, This, qobj, property, flags)); + if (!v->as<QObjectMethod>()) + return revertLookup(); + + lookup->qobjectMethodLookup.method.set(engine, static_cast<Heap::QObjectMethod *>(v->heapObject())); + return v->asReturnedValue(); } struct QQmlValueTypeWrapper; @@ -271,41 +350,51 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject enum { DestroyMethod = -1, ToStringMethod = -2 }; - static ReturnedValue create(QV4::ExecutionContext *scope, QObject *object, int index); - static ReturnedValue create(QV4::ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index); + static ReturnedValue create(ExecutionEngine *engine, Heap::Object *wrapper, int index); + static ReturnedValue create( + ExecutionEngine *engine, Heap::QQmlValueTypeWrapper *valueType, int index); + static ReturnedValue create( + ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom, + Heap::Object *wrapper, Heap::Object *object); int methodIndex() const { return d()->index; } QObject *object() const { return d()->object(); } - QV4::ReturnedValue method_toString(QV4::ExecutionEngine *engine) const; - QV4::ReturnedValue method_destroy(QV4::ExecutionEngine *ctx, const Value *args, int argc) const; + QV4::ReturnedValue method_toString(QV4::ExecutionEngine *engine, QObject *o) const; + QV4::ReturnedValue method_destroy( + QV4::ExecutionEngine *ctx, QObject *o, const Value *args, int argc) const; + void method_destroy( + QV4::ExecutionEngine *engine, QObject *o, + void **argv, const QMetaType *types, int argc) const; - static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCall( + const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static void virtualCallWithMetaTypes( + const FunctionObject *m, QObject *thisObject, + void **argv, const QMetaType *types, int argc); - ReturnedValue callInternal(const Value *thisObject, const Value *argv, int argc) const; + ReturnedValue callInternal( + const Value *thisObject, const Value *argv, int argc) const; + void callInternalWithMetaTypes( + QObject *thisObject, void **argv, const QMetaType *types, int argc) const; static QPair<QObject *, int> extractQtMethod(const QV4::FunctionObject *function); -}; +private: + friend struct QMetaObjectWrapper; -struct Q_QML_EXPORT QMetaObjectWrapper : public QV4::FunctionObject -{ - V4_OBJECT2(QMetaObjectWrapper, QV4::FunctionObject) - V4_NEEDS_DESTROY - - static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject); - const QMetaObject *metaObject() const { return d()->metaObject; } + static const QQmlPropertyData *resolveOverloaded( + const QQmlObjectOrGadget &object, const QQmlPropertyData *methods, int methodCount, + ExecutionEngine *engine, CallData *callArgs); -protected: - static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); - static bool virtualIsEqualTo(Managed *a, Managed *b); - -private: - void init(ExecutionEngine *engine); - ReturnedValue constructInternal(const Value *argv, int argc) const; - ReturnedValue callConstructor(const QQmlPropertyData &data, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const; - ReturnedValue callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const; + static const QQmlPropertyData *resolveOverloaded( + const QQmlPropertyData *methods, int methodCount, + void **argv, int argc, const QMetaType *types); + static ReturnedValue callPrecise( + const QQmlObjectOrGadget &object, const QQmlPropertyData &data, + ExecutionEngine *engine, CallData *callArgs, + QMetaObject::Call callType = QMetaObject::InvokeMetaMethod); }; struct Q_QML_EXPORT QmlSignalHandler : public QV4::Object @@ -317,24 +406,37 @@ struct Q_QML_EXPORT QmlSignalHandler : public QV4::Object int signalIndex() const { return d()->signalIndex; } QObject *object() const { return d()->object(); } + ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const; + static void initProto(ExecutionEngine *v4); }; +using QObjectBiPointer = QBiPointer<QObject, const QObject>; + class MultiplyWrappedQObjectMap : public QObject, - private QHash<QObject*, QV4::WeakValue> + private QHash<QObjectBiPointer, QV4::WeakValue> { Q_OBJECT public: - typedef QHash<QObject*, QV4::WeakValue>::ConstIterator ConstIterator; - typedef QHash<QObject*, QV4::WeakValue>::Iterator Iterator; + typedef QHash<QObjectBiPointer, QV4::WeakValue>::ConstIterator ConstIterator; + typedef QHash<QObjectBiPointer, QV4::WeakValue>::Iterator Iterator; + + using value_type = QHash<QObjectBiPointer, QV4::WeakValue>::value_type; + + ConstIterator begin() const { return QHash<QObjectBiPointer, QV4::WeakValue>::constBegin(); } + Iterator begin() { return QHash<QObjectBiPointer, QV4::WeakValue>::begin(); } + ConstIterator end() const { return QHash<QObjectBiPointer, QV4::WeakValue>::constEnd(); } + Iterator end() { return QHash<QObjectBiPointer, QV4::WeakValue>::end(); } - ConstIterator begin() const { return QHash<QObject*, QV4::WeakValue>::constBegin(); } - Iterator begin() { return QHash<QObject*, QV4::WeakValue>::begin(); } - ConstIterator end() const { return QHash<QObject*, QV4::WeakValue>::constEnd(); } - Iterator end() { return QHash<QObject*, QV4::WeakValue>::end(); } + template<typename Pointer> + void insert(Pointer key, Heap::Object *value) + { + QHash<QObjectBiPointer, WeakValue>::operator[](key).set(value->internalClass->engine, value); + connect(key, SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*))); + } - void insert(QObject *key, Heap::Object *value); - ReturnedValue value(QObject *key) const + template<typename Pointer> + ReturnedValue value(Pointer key) const { ConstIterator it = find(key); return it == end() @@ -343,8 +445,24 @@ public: } Iterator erase(Iterator it); - void remove(QObject *key); - void mark(QObject *key, MarkStack *markStack); + + template<typename Pointer> + void remove(Pointer key) + { + Iterator it = find(key); + if (it == end()) + return; + erase(it); + } + + template<typename Pointer> + void mark(Pointer key, MarkStack *markStack) + { + Iterator it = find(key); + if (it == end()) + return; + it->markOnce(markStack); + } private Q_SLOTS: void removeDestroyedObject(QObject*); diff --git a/src/qml/jsruntime/qv4referenceobject.cpp b/src/qml/jsruntime/qv4referenceobject.cpp new file mode 100644 index 0000000000..af6ee60abc --- /dev/null +++ b/src/qml/jsruntime/qv4referenceobject.cpp @@ -0,0 +1,10 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <private/qv4referenceobject_p.h> + +QT_BEGIN_NAMESPACE + +DEFINE_OBJECT_VTABLE(QV4::ReferenceObject); + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4referenceobject_p.h b/src/qml/jsruntime/qv4referenceobject_p.h new file mode 100644 index 0000000000..094749ce6e --- /dev/null +++ b/src/qml/jsruntime/qv4referenceobject_p.h @@ -0,0 +1,171 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QV4REFERENCEOBJECT_P_H +#define QV4REFERENCEOBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qv4object_p.h> +#include <private/qv4stackframe_p.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { +namespace Heap { + + +#define ReferenceObjectMembers(class, Member) \ + Member(class, Pointer, Object *, m_object) + +DECLARE_HEAP_OBJECT(ReferenceObject, Object) { + DECLARE_MARKOBJECTS(ReferenceObject); + + enum Flag : quint8 { + NoFlag = 0, + CanWriteBack = 1 << 0, + IsVariant = 1 << 1, + EnforcesLocation = 1 << 2, + }; + Q_DECLARE_FLAGS(Flags, Flag); + + void init(Object *object, int property, Flags flags) + { + setObject(object); + m_property = property; + m_flags = flags; + Object::init(); + } + + Flags flags() const { return Flags(m_flags); } + + Object *object() const { return m_object.get(); } + void setObject(Object *object) { m_object.set(internalClass->engine, object); } + + int property() const { return m_property; } + + bool canWriteBack() const { return hasFlag(CanWriteBack); } + bool isVariant() const { return hasFlag(IsVariant); } + bool enforcesLocation() const { return hasFlag(EnforcesLocation); } + + void setLocation(const Function *function, quint16 statement) + { + m_function = function; + m_statementIndex = statement; + } + + const Function *function() const { return m_function; } + quint16 statementIndex() const { return m_statementIndex; } + + bool isAttachedToProperty() const + { + if (enforcesLocation()) { + if (CppStackFrame *frame = internalClass->engine->currentStackFrame) { + if (frame->v4Function != function() || frame->statementNumber() != statementIndex()) + return false; + } else { + return false; + } + } + + return true; + } + + bool isReference() const { return m_object; } + +private: + + bool hasFlag(Flag flag) const + { + return m_flags & quint8(flag); + } + + void setFlag(Flag flag, bool set) + { + m_flags = set ? (m_flags | quint8(flag)) : (m_flags & ~quint8(flag)); + } + + const Function *m_function; + int m_property; + quint16 m_statementIndex; + quint8 m_flags; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(ReferenceObject::Flags) + +} // namespace Heap + + +struct ReferenceObject : public Object +{ + V4_OBJECT2(ReferenceObject, Object) + V4_NEEDS_DESTROY + +public: + static constexpr const int AllProperties = -1; + + template<typename HeapObject> + static bool readReference(HeapObject *ref) + { + if (!ref->object()) + return false; + + QV4::Scope scope(ref->internalClass->engine); + QV4::ScopedObject object(scope, ref->object()); + + if (ref->isVariant()) { + QVariant variant; + void *a[] = { &variant }; + return object->metacall(QMetaObject::ReadProperty, ref->property(), a) + && ref->setVariant(variant); + } + + void *a[] = { ref->storagePointer() }; + return object->metacall(QMetaObject::ReadProperty, ref->property(), a); + } + + template<typename HeapObject> + static bool writeBack(HeapObject *ref, int internalIndex = AllProperties) + { + if (!ref->object() || !ref->canWriteBack()) + return false; + + QV4::Scope scope(ref->internalClass->engine); + QV4::ScopedObject object(scope, ref->object()); + + int flags = QQmlPropertyData::HasInternalIndex; + int status = -1; + if (ref->isVariant()) { + QVariant variant = ref->toVariant(); + void *a[] = { &variant, nullptr, &status, &flags, &internalIndex }; + return object->metacall(QMetaObject::WriteProperty, ref->property(), a); + } + + void *a[] = { ref->storagePointer(), nullptr, &status, &flags, &internalIndex }; + return object->metacall(QMetaObject::WriteProperty, ref->property(), a); + } + + template<typename HeapObject> + static HeapObject *detached(HeapObject *ref) + { + if (ref->object() && !ref->enforcesLocation() && !readReference(ref)) + return ref; // It's dead. No point in detaching it anymore + + return ref->detached(); + } +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4REFERENCEOBJECT_P_H diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp index 0772770d63..dc9dbe48ae 100644 --- a/src/qml/jsruntime/qv4reflect.cpp +++ b/src/qml/jsruntime/qv4reflect.cpp @@ -1,44 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4reflect_p.h" -#include "qv4symbol_p.h" #include "qv4runtimeapi_p.h" #include "qv4objectproto_p.h" #include "qv4propertykey_p.h" @@ -76,7 +39,10 @@ struct CallArgs { static CallArgs createListFromArrayLike(Scope &scope, const Object *o) { - int len = o->getLength(); + int len = scope.engine->safeForAllocLength(o->getLength()); + if (scope.engine->hasException) + return {nullptr, 0}; + Value *arguments = scope.alloc(len); for (int i = 0; i < len; ++i) { @@ -98,7 +64,8 @@ ReturnedValue Reflect::method_apply(const FunctionObject *f, const Value *, cons if (scope.hasException()) return Encode::undefined(); - return static_cast<const FunctionObject &>(argv[0]).call(&argv[1], arguments.argv, arguments.argc); + return checkedResult(scope.engine, static_cast<const FunctionObject &>(argv[0]).call( + &argv[1], arguments.argv, arguments.argc)); } ReturnedValue Reflect::method_construct(const FunctionObject *f, const Value *, const Value *argv, int argc) @@ -127,14 +94,14 @@ ReturnedValue Reflect::method_defineProperty(const FunctionObject *f, const Valu ScopedObject O(scope, argv[0]); ScopedPropertyKey name(scope, (argc > 1 ? argv[1] : Value::undefinedValue()).toPropertyKey(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); ScopedValue attributes(scope, argc > 2 ? argv[2] : Value::undefinedValue()); ScopedProperty pd(scope); PropertyAttributes attrs; ObjectPrototype::toPropertyDescriptor(scope.engine, attributes, pd, &attrs); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); bool result = O->defineOwnProperty(name, pd, attrs); @@ -198,7 +165,7 @@ ReturnedValue Reflect::method_has(const FunctionObject *f, const Value *, const const Value *index = argc > 1 ? &argv[1] : &undef; ScopedPropertyKey name(scope, index->toPropertyKey(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return false; return Encode(o->hasProperty(name)); @@ -267,7 +234,7 @@ ReturnedValue Reflect::method_set(const FunctionObject *f, const Value *, const ScopedValue receiver(scope, argc >3 ? argv[3] : argv[0]); ScopedPropertyKey propertyKey(scope, index->toPropertyKey(scope.engine)); - if (scope.engine->hasException) + if (scope.hasException()) return false; bool result = o->put(propertyKey, val, receiver); return Encode(result); diff --git a/src/qml/jsruntime/qv4reflect_p.h b/src/qml/jsruntime/qv4reflect_p.h index d480e1d914..40e1874686 100644 --- a/src/qml/jsruntime/qv4reflect_p.h +++ b/src/qml/jsruntime/qv4reflect_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4REFLECT_H #define QV4REFLECT_H diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp index 4ed1dbd5aa..9c48199157 100644 --- a/src/qml/jsruntime/qv4regexp.cpp +++ b/src/qml/jsruntime/qv4regexp.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4regexp_p.h" #include "qv4engine_p.h" @@ -45,19 +9,23 @@ using namespace QV4; +#if ENABLE(YARR_JIT) +static constexpr quint8 RegexpJitThreshold = 5; +#endif + static JSC::RegExpFlags jscFlags(uint flags) { JSC::RegExpFlags jscFlags = JSC::NoFlags; if (flags & CompiledData::RegExp::RegExp_Global) - jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagGlobal); + jscFlags = static_cast<JSC::RegExpFlags>(jscFlags | JSC::FlagGlobal); if (flags & CompiledData::RegExp::RegExp_IgnoreCase) - jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagIgnoreCase); + jscFlags = static_cast<JSC::RegExpFlags>(jscFlags | JSC::FlagIgnoreCase); if (flags & CompiledData::RegExp::RegExp_Multiline) - jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagMultiline); + jscFlags = static_cast<JSC::RegExpFlags>(jscFlags | JSC::FlagMultiline); if (flags & CompiledData::RegExp::RegExp_Unicode) - jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagUnicode); + jscFlags = static_cast<JSC::RegExpFlags>(jscFlags | JSC::FlagUnicode); if (flags & CompiledData::RegExp::RegExp_Sticky) - jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagSticky); + jscFlags = static_cast<JSC::RegExpFlags>(jscFlags | JSC::FlagSticky); return jscFlags; } @@ -73,20 +41,65 @@ DEFINE_MANAGED_VTABLE(RegExp); uint RegExp::match(const QString &string, int start, uint *matchOffsets) { - static const uint offsetJITFail = std::numeric_limits<unsigned>::max() - 1; - if (!isValid()) return JSC::Yarr::offsetNoMatch; +#if ENABLE(YARR_JIT) + auto *priv = d(); + + auto regenerateByteCode = [](Heap::RegExp *regexp) { + JSC::Yarr::ErrorCode error = JSC::Yarr::ErrorCode::NoError; + JSC::Yarr::YarrPattern yarrPattern(WTF::String(*regexp->pattern), jscFlags(regexp->flags), + error); + + // As we successfully parsed the pattern before, we should still be able to. + Q_ASSERT(error == JSC::Yarr::ErrorCode::NoError); + + regexp->byteCode = JSC::Yarr::byteCompile( + yarrPattern, + regexp->internalClass->engine->bumperPointerAllocator).release(); + }; + + auto removeJitCode = [](Heap::RegExp *regexp) { + delete regexp->jitCode; + regexp->jitCode = nullptr; + regexp->jitFailed = true; + }; + + auto removeByteCode = [](Heap::RegExp *regexp) { + delete regexp->byteCode; + regexp->byteCode = nullptr; + }; + + if (!priv->jitCode && !priv->jitFailed && priv->internalClass->engine->canJIT() + && (string.length() > 1024 || priv->matchCount++ == RegexpJitThreshold)) { + removeByteCode(priv); + + JSC::Yarr::ErrorCode error = JSC::Yarr::ErrorCode::NoError; + JSC::Yarr::YarrPattern yarrPattern( + WTF::String(*priv->pattern), jscFlags(priv->flags), error); + if (!yarrPattern.m_containsBackreferences) { + priv->jitCode = new JSC::Yarr::YarrCodeBlock; + JSC::VM *vm = static_cast<JSC::VM *>(priv->internalClass->engine); + JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, vm, *priv->jitCode); + } + + if (!priv->hasValidJITCode()) { + removeJitCode(priv); + regenerateByteCode(priv); + } + } +#endif + WTF::String s(string); #if ENABLE(YARR_JIT) - auto *priv = d(); if (priv->hasValidJITCode()) { + static const uint offsetJITFail = std::numeric_limits<unsigned>::max() - 1; uint ret = JSC::Yarr::offsetNoMatch; #if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS) char buffer[8192]; - ret = uint(priv->jitCode->execute(s.characters16(), start, s.length(), + ret = uint(priv->jitCode->execute(s.characters16(), start, s.size(), (int*)matchOffsets, buffer, 8192).start); #else ret = uint(priv->jitCode->execute(s.characters16(), start, s.length(), @@ -95,34 +108,25 @@ uint RegExp::match(const QString &string, int start, uint *matchOffsets) if (ret != offsetJITFail) return ret; + removeJitCode(priv); // JIT failed. We need byteCode to run the interpreter. - if (!priv->byteCode) { - JSC::Yarr::ErrorCode error = JSC::Yarr::ErrorCode::NoError; - JSC::Yarr::YarrPattern yarrPattern(WTF::String(*priv->pattern), jscFlags(priv->flags), - error); - - // As we successfully parsed the pattern before, we should still be able to. - Q_ASSERT(error == JSC::Yarr::ErrorCode::NoError); - - priv->byteCode = JSC::Yarr::byteCompile( - yarrPattern, - priv->internalClass->engine->bumperPointerAllocator).release(); - } + Q_ASSERT(!priv->byteCode); + regenerateByteCode(priv); } #endif // ENABLE(YARR_JIT) - return JSC::Yarr::interpret(byteCode(), s.characters16(), string.length(), start, matchOffsets); + return JSC::Yarr::interpret(byteCode(), s.characters16(), string.size(), start, matchOffsets); } QString RegExp::getSubstitution(const QString &matched, const QString &str, int position, const Value *captures, int nCaptures, const QString &replacement) { QString result; - int matchedLength = matched.length(); - Q_ASSERT(position >= 0 && position <= str.length()); + int matchedLength = matched.size(); + Q_ASSERT(position >= 0 && position <= str.size()); int tailPos = position + matchedLength; int seenDollar = -1; - for (int i = 0; i < replacement.length(); ++i) { + for (int i = 0; i < replacement.size(); ++i) { QChar ch = replacement.at(i); if (seenDollar >= 0) { if (ch.unicode() == '$') { @@ -135,7 +139,7 @@ QString RegExp::getSubstitution(const QString &matched, const QString &str, int result += str.mid(tailPos); } else if (ch.unicode() >= '0' && ch.unicode() <= '9') { int n = ch.unicode() - '0'; - if (i + 1 < replacement.length()) { + if (i + 1 < replacement.size()) { ch = replacement.at(i + 1); if (ch.unicode() >= '0' && ch.unicode() <= '9') { n = n*10 + (ch.unicode() - '0'); @@ -212,25 +216,15 @@ void Heap::RegExp::init(ExecutionEngine *engine, const QString &pattern, uint fl this->flags = flags; valid = false; + jitFailed = false; + matchCount = 0; JSC::Yarr::ErrorCode error = JSC::Yarr::ErrorCode::NoError; JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), jscFlags(flags), error); if (error != JSC::Yarr::ErrorCode::NoError) return; subPatternCount = yarrPattern.m_numSubpatterns; -#if ENABLE(YARR_JIT) - if (!yarrPattern.m_containsBackreferences && engine->canJIT()) { - jitCode = new JSC::Yarr::YarrCodeBlock; - JSC::VM *vm = static_cast<JSC::VM *>(engine); - JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, vm, *jitCode); - } -#else - Q_UNUSED(engine) -#endif - if (hasValidJITCode()) { - valid = true; - return; - } + Q_UNUSED(engine); byteCode = JSC::Yarr::byteCompile(yarrPattern, internalClass->engine->bumperPointerAllocator).release(); if (byteCode) valid = true; diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h index 6afb10ea95..8a178dd2f6 100644 --- a/src/qml/jsruntime/qv4regexp_p.h +++ b/src/qml/jsruntime/qv4regexp_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4REGEXP_H #define QV4REGEXP_H @@ -102,11 +66,13 @@ struct RegExp : Base { int subPatternCount; uint flags; bool valid; + bool jitFailed; + quint8 matchCount; QString flagsAsString() const; int captureCount() const { return subPatternCount + 1; } }; -Q_STATIC_ASSERT(std::is_trivial< RegExp >::value); +Q_STATIC_ASSERT(std::is_trivial_v<RegExp>); } @@ -164,7 +130,7 @@ inline RegExpCacheKey::RegExpCacheKey(const RegExp::Data *re) , flags(re->flags) {} -inline uint qHash(const RegExpCacheKey& key, uint seed = 0) Q_DECL_NOTHROW +inline size_t qHash(const RegExpCacheKey& key, size_t seed = 0) noexcept { return qHash(key.pattern, seed); } class RegExpCache : public QHash<RegExpCacheKey, WeakValue> diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index c1a42c4afa..144cd39bcd 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -1,46 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4regexpobject_p.h" -#include "qv4objectproto_p.h" #include "qv4regexp_p.h" -#include "qv4stringobject_p.h" #include <private/qv4mm_p.h> #include "qv4scopedvalue_p.h" #include "qv4jscall_p.h" @@ -49,7 +11,6 @@ #include "private/qlocale_tools_p.h" #include <QtCore/QDebug> -#include <QtCore/qregexp.h> #if QT_CONFIG(regularexpression) #include <QtCore/qregularexpression.h> #endif @@ -60,8 +21,6 @@ QT_BEGIN_NAMESPACE -Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax); - using namespace QV4; DEFINE_OBJECT_VTABLE(RegExpObject); @@ -84,57 +43,40 @@ void Heap::RegExpObject::init(QV4::RegExp *value) o->initProperties(); } -// 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. -void Heap::RegExpObject::init(const QRegExp &re) -{ - Object::init(); - - // 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; - } +static QString minimalPattern(const QString &pattern) +{ + QString ecmaPattern; + int len = pattern.size(); + 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; } - - Scope scope(internalClass->engine); - Scoped<QV4::RegExpObject> o(scope, this); - - uint flags = (re.caseSensitivity() == Qt::CaseInsensitive ? CompiledData::RegExp::RegExp_IgnoreCase : CompiledData::RegExp::RegExp_NoFlags); - o->d()->value.set(scope.engine, QV4::RegExp::create(scope.engine, pattern, flags)); - - o->initProperties(); + return ecmaPattern; } #if QT_CONFIG(regularexpression) @@ -148,10 +90,16 @@ void Heap::RegExpObject::init(const QRegularExpression &re) Scope scope(internalClass->engine); Scoped<QV4::RegExpObject> o(scope, this); - const uint flags = (re.patternOptions() & QRegularExpression::CaseInsensitiveOption) - ? CompiledData::RegExp::RegExp_IgnoreCase - : CompiledData::RegExp::RegExp_NoFlags; - o->d()->value.set(scope.engine, QV4::RegExp::create(scope.engine, re.pattern(), flags)); + QRegularExpression::PatternOptions options = re.patternOptions(); + uint flags = (options & QRegularExpression::CaseInsensitiveOption) + ? CompiledData::RegExp::RegExp_IgnoreCase + : CompiledData::RegExp::RegExp_NoFlags; + if (options & QRegularExpression::MultilineOption) + flags |= CompiledData::RegExp::RegExp_Multiline; + QString pattern = re.pattern(); + if (options & QRegularExpression::InvertedGreedinessOption) + pattern = minimalPattern(pattern); + o->d()->value.set(scope.engine, QV4::RegExp::create(scope.engine, pattern, flags)); o->initProperties(); } #endif @@ -163,26 +111,18 @@ void RegExpObject::initProperties() Q_ASSERT(value()); } -// 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()->flags & CompiledData::RegExp::RegExp_IgnoreCase) ? Qt::CaseInsensitive : Qt::CaseSensitive; - return QRegExp(*value()->pattern, caseSensitivity, QRegExp::RegExp2); -} - #if QT_CONFIG(regularexpression) // Converts a JS RegExp to a QRegularExpression. // The conversion is not 100% exact since ECMA regexp and QRegularExpression // have different semantics/flags, but we try to do our best. QRegularExpression RegExpObject::toQRegularExpression() const { - QRegularExpression::PatternOptions caseSensitivity - = (value()->flags & CompiledData::RegExp::RegExp_IgnoreCase) - ? QRegularExpression::CaseInsensitiveOption - : QRegularExpression::NoPatternOption; - return QRegularExpression(*value()->pattern, caseSensitivity); + QRegularExpression::PatternOptions options = QRegularExpression::NoPatternOption; + if (value()->flags & CompiledData::RegExp::RegExp_IgnoreCase) + options |= QRegularExpression::CaseInsensitiveOption; + if (value()->flags & CompiledData::RegExp::RegExp_Multiline) + options |= QRegularExpression::MultilineOption; + return QRegularExpression(*value()->pattern, options); } #endif @@ -193,7 +133,7 @@ QString RegExpObject::toString() const p = QStringLiteral("(?:)"); } else { // escape certain parts, see ch. 15.10.4 - p.replace('/', QLatin1String("\\/")); + p.replace(u'/', QLatin1String("\\/")); } return p; } @@ -204,7 +144,7 @@ ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine, const String *s Scope scope(engine); int offset = (global() || sticky()) ? lastIndex() : 0; - if (offset < 0 || offset > s.length()) { + if (offset < 0 || offset > s.size()) { setLastIndex(0); RETURN_RESULT(Encode::null()); } @@ -228,7 +168,7 @@ ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine, const String *s int len = value()->captureCount(); array->arrayReserve(len); ScopedValue v(scope); - int strlen = s.length(); + int strlen = s.size(); for (int i = 0; i < len; ++i) { int start = matchOffsets[i * 2]; int end = matchOffsets[i * 2 + 1]; @@ -255,9 +195,9 @@ ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine, const String *s DEFINE_OBJECT_VTABLE(RegExpCtor); -void Heap::RegExpCtor::init(QV4::ExecutionContext *scope) +void Heap::RegExpCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("RegExp")); + Heap::FunctionObject::init(engine, QStringLiteral("RegExp")); clearLastMatch(); } @@ -290,7 +230,7 @@ uint parseFlags(Scope &scope, const QV4::Value *f) if (scope.hasException()) return flags; QString str = s->toQString(); - for (int i = 0; i < str.length(); ++i) { + for (int i = 0; i < str.size(); ++i) { if (str.at(i) == QLatin1Char('g') && !(flags & CompiledData::RegExp::RegExp_Global)) { flags |= CompiledData::RegExp::RegExp_Global; } else if (str.at(i) == QLatin1Char('i') && !(flags & CompiledData::RegExp::RegExp_IgnoreCase)) { @@ -440,7 +380,7 @@ ReturnedValue RegExpPrototype::execFirstMatch(const FunctionObject *b, const Val QString s = str->toQString(); int offset = r->lastIndex(); - if (offset < 0 || offset > s.length()) { + if (offset < 0 || offset > s.size()) { r->setLastIndex(0); RETURN_RESULT(Encode::null()); } @@ -481,6 +421,8 @@ ReturnedValue RegExpPrototype::exec(ExecutionEngine *engine, const Object *o, co ScopedFunctionObject exec(scope, o->get(key)); if (exec) { ScopedValue result(scope, exec->call(o, s, 1)); + if (scope.hasException()) + RETURN_UNDEFINED(); if (!result->isNull() && !result->isObject()) return scope.engine->throwTypeError(); return result->asReturnedValue(); @@ -498,7 +440,7 @@ ReturnedValue RegExpPrototype::method_exec(const FunctionObject *b, const Value if (!r) return scope.engine->throwTypeError(); - ScopedValue arg(scope, argc ? argv[0]: Value::undefinedValue()); + ScopedValue arg(scope, argc ? argv[0] : Value::undefinedValue()); ScopedString str(scope, arg->toString(scope.engine)); if (scope.hasException()) RETURN_UNDEFINED(); @@ -574,7 +516,7 @@ ReturnedValue RegExpPrototype::method_get_ignoreCase(const FunctionObject *f, co static int advanceStringIndex(int index, const QString &str, bool unicode) { if (unicode) { - if (index < str.length() - 1 && + if (index < str.size() - 1 && str.at(index).isHighSurrogate() && str.at(index + 1).isLowSurrogate()) ++index; @@ -663,7 +605,7 @@ ReturnedValue RegExpPrototype::method_replace(const FunctionObject *f, const Val if (scope.hasException()) return Encode::undefined(); - int lengthS = s->toQString().length(); + int lengthS = s->toQString().size(); ScopedString replaceValue(scope); ScopedFunctionObject replaceFunction(scope, (argc > 1 ? argv[1] : Value::undefinedValue())); @@ -715,7 +657,7 @@ ReturnedValue RegExpPrototype::method_replace(const FunctionObject *f, const Val if (scope.hasException()) return Encode::undefined(); QString m = matchString->toQString(); - int matchLength = m.length(); + int matchLength = m.size(); v = resultObject->get(scope.engine->id_index()); int position = v->toInt32(); position = qMax(qMin(position, lengthS), 0); @@ -724,19 +666,21 @@ ReturnedValue RegExpPrototype::method_replace(const FunctionObject *f, const Val int n = 1; Scope innerScope(scope.engine); - JSCallData cData(scope, nCaptures + 3); + JSCallArguments cData(scope, nCaptures + 3); while (n <= nCaptures) { v = resultObject->get(PropertyKey::fromArrayIndex(n)); if (!v->isUndefined()) - cData->args[n] = v->toString(scope.engine); + cData.args[n] = v->toString(scope.engine); ++n; } QString replacement; if (functionalReplace) { - cData->args[0] = matchString; - cData->args[nCaptures + 1] = Encode(position); - cData->args[nCaptures + 2] = s; + cData.args[0] = matchString; + cData.args[nCaptures + 1] = Encode(position); + cData.args[nCaptures + 2] = s; ScopedValue replValue(scope, replaceFunction->call(cData)); + if (scope.hasException()) + return Encode::undefined(); replacement = replValue->toQString(); } else { replacement = RegExp::getSubstitution(matchString->toQString(), s->toQString(), position, cData.args, nCaptures, replaceValue->toQString()); @@ -744,12 +688,12 @@ ReturnedValue RegExpPrototype::method_replace(const FunctionObject *f, const Val if (scope.hasException()) return Encode::undefined(); if (position >= nextSourcePosition) { - accumulatedResult += s->toQString().midRef(nextSourcePosition, position - nextSourcePosition) + replacement; + accumulatedResult += QStringView{s->toQString()}.mid(nextSourcePosition, position - nextSourcePosition) + replacement; nextSourcePosition = position + matchLength; } } if (nextSourcePosition < lengthS) { - accumulatedResult += s->toQString().midRef(nextSourcePosition); + accumulatedResult += QStringView{s->toQString()}.mid(nextSourcePosition); } return scope.engine->newString(accumulatedResult)->asReturnedValue(); } @@ -840,7 +784,7 @@ ReturnedValue RegExpPrototype::method_split(const FunctionObject *f, const Value return A->asReturnedValue(); QString S = s->toQString(); - int size = S.length(); + int size = S.size(); if (size == 0) { ScopedValue z(scope, exec(scope.engine, splitter, s)); if (z->isNull()) @@ -961,8 +905,8 @@ ReturnedValue RegExpPrototype::method_compile(const FunctionObject *b, const Val return scope.engine->throwTypeError(); Scoped<RegExpObject> re(scope, scope.engine->regExpCtor()->callAsConstructor(argv, argc)); - - r->d()->value.set(scope.engine, re->value()); + if (re) // Otherwise the regexp constructor should have thrown an exception + r->d()->value.set(scope.engine, re->value()); return Encode::undefined(); } diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index b94889e9f0..179a01fb45 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4REGEXPOBJECT_H #define QV4REGEXPOBJECT_H @@ -50,21 +14,13 @@ // We mean it. // -#include "qv4runtime_p.h" -#include "qv4engine_p.h" -#include "qv4context_p.h" -#include "qv4functionobject_p.h" -#include "qv4string_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> +#include <private/qv4context_p.h> +#include <private/qv4engine_p.h> +#include <private/qv4functionobject_p.h> +#include <private/qv4managed_p.h> + +#include <QtCore/qhash.h> +#include <QtCore/qstring.h> QT_BEGIN_NAMESPACE @@ -80,7 +36,6 @@ DECLARE_HEAP_OBJECT(RegExpObject, Object) { void init(); void init(QV4::RegExp *value); - void init(const QRegExp &re); #if QT_CONFIG(regularexpression) void init(const QRegularExpression &re); #endif @@ -93,15 +48,15 @@ DECLARE_HEAP_OBJECT(RegExpObject, Object) { Member(class, NoMark, int, lastMatchEnd) DECLARE_HEAP_OBJECT(RegExpCtor, FunctionObject) { - DECLARE_MARKOBJECTS(RegExpCtor); + DECLARE_MARKOBJECTS(RegExpCtor) - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); void clearLastMatch(); }; } -struct Q_QML_PRIVATE_EXPORT RegExpObject: Object { +struct Q_QML_EXPORT RegExpObject: Object { V4_OBJECT2(RegExpObject, Object) Q_MANAGED_TYPE(RegExpObject) V4_INTERNALCLASS(RegExpObject) @@ -140,7 +95,6 @@ struct Q_QML_PRIVATE_EXPORT RegExpObject: Object { return setProperty(Index_LastIndex, Value::fromInt32(index)); } - QRegExp toQRegExp() const; #if QT_CONFIG(regularexpression) QRegularExpression toQRegularExpression() const; #endif @@ -152,11 +106,19 @@ struct Q_QML_PRIVATE_EXPORT RegExpObject: Object { return s->toQString(); } - Heap::RegExp *value() const { return d()->value; } - uint flags() const { return d()->value->flags; } - bool global() const { return d()->value->global(); } - bool sticky() const { return d()->value->sticky(); } - bool unicode() const { return d()->value->unicode(); } + // We cannot name Heap::RegExp here since we don't want to include qv4regexp_p.h but we still + // want to keep the methods inline. We shift the requirement to name the type to the caller by + // making it a template. + template<typename RegExp = Heap::RegExp> + RegExp *value() const { return d()->value; } + template<typename RegExp = Heap::RegExp> + uint flags() const { return value<RegExp>()->flags; } + template<typename RegExp = Heap::RegExp> + bool global() const { return value<RegExp>()->global(); } + template<typename RegExp = Heap::RegExp> + bool sticky() const { return value<RegExp>()->sticky(); } + template<typename RegExp = Heap::RegExp> + bool unicode() const { return value<RegExp>()->unicode(); } ReturnedValue builtinExec(ExecutionEngine *engine, const String *s); }; diff --git a/src/qml/jsruntime/qv4resolvedtypereference.cpp b/src/qml/jsruntime/qv4resolvedtypereference.cpp new file mode 100644 index 0000000000..7dcf2cd0b8 --- /dev/null +++ b/src/qml/jsruntime/qv4resolvedtypereference.cpp @@ -0,0 +1,98 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qv4resolvedtypereference_p.h" + +#include <QtQml/private/qqmlengine_p.h> +#include <QtQml/qqmlpropertymap.h> +#include <QtCore/qcryptographichash.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +template <typename T> +bool qtTypeInherits(const QMetaObject *mo) { + while (mo) { + if (mo == &T::staticMetaObject) + return true; + mo = mo->superClass(); + } + return false; +} + +void ResolvedTypeReference::doDynamicTypeCheck() +{ + const QMetaObject *mo = nullptr; + if (m_typePropertyCache) + mo = m_typePropertyCache->firstCppMetaObject(); + else if (m_type.isValid()) + mo = m_type.metaObject(); + else if (m_compilationUnit) + mo = m_compilationUnit->rootPropertyCache()->firstCppMetaObject(); + m_isFullyDynamicType = qtTypeInherits<QQmlPropertyMap>(mo); +} + +/*! +Returns the property cache, if one alread exists. The cache is not referenced. +*/ +QQmlPropertyCache::ConstPtr ResolvedTypeReference::propertyCache() const +{ + if (m_type.isValid()) + return m_typePropertyCache; + else + return m_compilationUnit->rootPropertyCache(); +} + +/*! +Returns the property cache, creating one if it doesn't already exist. The cache is not referenced. +*/ +QQmlPropertyCache::ConstPtr ResolvedTypeReference::createPropertyCache() +{ + if (m_typePropertyCache) { + return m_typePropertyCache; + } else if (m_type.isValid()) { + const QMetaObject *metaObject = m_type.metaObject(); + if (!metaObject) // value type of non-Q_GADGET base with extension + metaObject = m_type.extensionMetaObject(); + if (metaObject) + m_typePropertyCache = QQmlMetaType::propertyCache(metaObject, m_version); + return m_typePropertyCache; + } else { + Q_ASSERT(m_compilationUnit); + return m_compilationUnit->rootPropertyCache(); + } +} + +bool ResolvedTypeReference::addToHash( + QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums) +{ + if (m_type.isInlineComponentType()) { + + // A reference to an inline component in the same file will have + // - no compilation unit since we cannot resolve the compilation unit before it's built. + // - a property cache since we've assigned one in buildMetaObjectsIncrementally(). + // - a QQmlType that says it's an inline component. + // We don't have to add such a thing to the hash since if it changes, the QML document + // itself changes, leading to a new timestamp, which is checked before the checksum. + if (!m_compilationUnit) + return !m_typePropertyCache.isNull(); + + } else if (m_type.isValid()) { + bool ok = false; + if (QQmlPropertyCache::ConstPtr propertyCache = createPropertyCache()) + hash->addData(propertyCache->checksum(checksums, &ok)); + else + Q_ASSERT(m_type.module() == QLatin1String("QML")); // a builtin without metaobject + return ok; + } + if (!m_compilationUnit) + return false; + hash->addData({m_compilationUnit->unitData()->md5Checksum, + sizeof(m_compilationUnit->unitData()->md5Checksum)}); + return true; +} + +} // namespace QV4 + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4resolvedtypereference_p.h b/src/qml/jsruntime/qv4resolvedtypereference_p.h new file mode 100644 index 0000000000..4aaafdd71c --- /dev/null +++ b/src/qml/jsruntime/qv4resolvedtypereference_p.h @@ -0,0 +1,113 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QV4RESOLVEDTYPEREFERNCE_P_H +#define QV4RESOLVEDTYPEREFERNCE_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 <QtQml/private/qtqmlglobal_p.h> +#include <QtQml/private/qqmlrefcount_p.h> +#include <QtQml/private/qqmlpropertycache_p.h> +#include <QtQml/private/qqmltype_p.h> +#include <QtQml/private/qv4compileddata_p.h> + +QT_BEGIN_NAMESPACE + +class QCryptographicHash; +namespace QV4 { + +class ResolvedTypeReference +{ + Q_DISABLE_COPY_MOVE(ResolvedTypeReference) +public: + ResolvedTypeReference() = default; + ~ResolvedTypeReference() + { + if (m_stronglyReferencesCompilationUnit && m_compilationUnit) + m_compilationUnit->release(); + } + + QQmlPropertyCache::ConstPtr propertyCache() const; + QQmlPropertyCache::ConstPtr createPropertyCache(); + bool addToHash(QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums); + + void doDynamicTypeCheck(); + + QQmlType type() const { return m_type; } + void setType(QQmlType type) { m_type = std::move(type); } + + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit() + { + return m_compilationUnit; + } + + void setCompilationUnit(QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit) + { + if (m_compilationUnit == unit.data()) + return; + if (m_stronglyReferencesCompilationUnit) { + if (m_compilationUnit) + m_compilationUnit->release(); + m_compilationUnit = unit.take(); + } else { + m_compilationUnit = unit.data(); + } + } + + bool referencesCompilationUnit() const { return m_stronglyReferencesCompilationUnit; } + void setReferencesCompilationUnit(bool doReference) + { + if (doReference == m_stronglyReferencesCompilationUnit) + return; + m_stronglyReferencesCompilationUnit = doReference; + if (!m_compilationUnit) + return; + if (doReference) { + m_compilationUnit->addref(); + } else if (m_compilationUnit->count() == 1) { + m_compilationUnit->release(); + m_compilationUnit = nullptr; + } else { + m_compilationUnit->release(); + } + } + + QQmlPropertyCache::ConstPtr typePropertyCache() const { return m_typePropertyCache; } + void setTypePropertyCache(QQmlPropertyCache::ConstPtr cache) + { + m_typePropertyCache = std::move(cache); + } + + QTypeRevision version() const { return m_version; } + void setVersion(QTypeRevision version) { m_version = version; } + + bool isFullyDynamicType() const { return m_isFullyDynamicType; } + void setFullyDynamicType(bool fullyDynamic) { m_isFullyDynamicType = fullyDynamic; } + +private: + QQmlType m_type; + QQmlPropertyCache::ConstPtr m_typePropertyCache; + QV4::CompiledData::CompilationUnit *m_compilationUnit = nullptr; + + QTypeRevision m_version = QTypeRevision::zero(); + // Types such as QQmlPropertyMap can add properties dynamically at run-time and + // therefore cannot have a property cache installed when instantiated. + bool m_isFullyDynamicType = false; + bool m_stronglyReferencesCompilationUnit = true; +}; + +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4RESOLVEDTYPEREFERNCE_P_H diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 21a0017728..b5c497be49 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1,83 +1,48 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4global_p.h" +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + #include "qv4runtime_p.h" -#include "qv4engine_p.h" -#include "qv4object_p.h" -#include "qv4objectproto_p.h" -#include "qv4globalobject_p.h" -#include "qv4stringobject_p.h" -#include "qv4argumentsobject_p.h" -#include "qv4objectiterator_p.h" -#include "qv4dateobject_p.h" -#include "qv4lookup_p.h" -#include "qv4function_p.h" -#include "qv4numberobject_p.h" -#include "qv4regexp_p.h" -#include "qv4regexpobject_p.h" -#include "private/qlocale_tools_p.h" -#include "qv4scopedvalue_p.h" -#include "qv4jscall_p.h" -#include <private/qv4qmlcontext_p.h> -#include <private/qqmltypewrapper_p.h> + #include <private/qqmlengine_p.h> #include <private/qqmljavascriptexpression_p.h> #include <private/qqmljsast_p.h> -#include "qv4qobjectwrapper_p.h" -#include "qv4symbol_p.h" -#include "qv4generatorobject_p.h" - -#include <QtCore/QDebug> -#include <cassert> -#include <cstdio> -#include <stdlib.h> +#include <private/qqmltypewrapper_p.h> +#include <private/qqmlvaluetypewrapper_p.h> +#include <private/qv4argumentsobject_p.h> +#include <private/qv4engine_p.h> +#include <private/qv4function_p.h> +#include <private/qv4generatorobject_p.h> +#include <private/qv4global_p.h> +#include <private/qv4globalobject_p.h> +#include <private/qv4jscall_p.h> +#include <private/qv4lookup_p.h> +#include <private/qv4math_p.h> +#include <private/qv4object_p.h> +#include <private/qv4qmlcontext_p.h> +#include <private/qv4qobjectwrapper_p.h> +#include <private/qv4regexp_p.h> +#include <private/qv4regexpobject_p.h> +#include <private/qv4scopedvalue_p.h> +#include <private/qv4stackframe_p.h> +#include <private/qv4symbol_p.h> #include <wtf/MathExtras.h> +#include <QtCore/private/qlocale_tools_p.h> +#include <QtCore/qdebug.h> + #ifdef QV4_COUNT_RUNTIME_FUNCTIONS -# include <QtCore/QBuffer> -# include <QtCore/QDebug> +# include <QtCore/qbuffer.h> #endif // QV4_COUNT_RUNTIME_FUNCTIONS +#include <cassert> +#include <cstdio> +#include <stdlib.h> + QT_BEGIN_NAMESPACE +Q_STATIC_LOGGING_CATEGORY(lcCoercingTypeAssertion, "qt.qml.coercingTypeAssertion"); + namespace QV4 { #ifdef QV4_COUNT_RUNTIME_FUNCTIONS @@ -179,7 +144,7 @@ struct RuntimeCounters::Data { } std::sort(lines.begin(), lines.end(), Line::less); outs << lines.size() << " counters:" << endl; - for (const Line &line : qAsConst(lines)) + for (const Line &line : std::as_const(lines)) outs << qSetFieldWidth(10) << line.count << qSetFieldWidth(0) << " | " << line.func << " | " << pretty(line.tag1) @@ -252,7 +217,7 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) *result = qdtoa(num, &decpt, &sign); if (decpt <= ecma_shortest_low || decpt > ecma_shortest_high) { - if (result->length() > 1) + if (result->size() > 1) result->insert(1, dot); result->append(QLatin1Char('e')); if (decpt > 0) @@ -260,10 +225,10 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) result->append(QString::number(decpt - 1)); } else if (decpt <= 0) { result->prepend(QLatin1String("0.") + QString(-decpt, zero)); - } else if (decpt < result->length()) { + } else if (decpt < result->size()) { result->insert(decpt, dot); } else { - result->append(QString(decpt - result->length(), zero)); + result->append(QString(decpt - result->size(), zero)); } if (sign && num) @@ -319,7 +284,7 @@ ReturnedValue Runtime::Closure::call(ExecutionEngine *engine, int functionId) QV4::Function *clos = engine->currentStackFrame->v4Function->executableCompilationUnit() ->runtimeFunctions[functionId]; Q_ASSERT(clos); - ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context); + ExecutionContext *current = engine->currentContext(); if (clos->isGenerator()) return GeneratorFunction::create(current, clos)->asReturnedValue(); return FunctionObject::createScriptFunction(current, clos)->asReturnedValue(); @@ -329,7 +294,7 @@ Bool Runtime::DeleteProperty_NoThrow::call(ExecutionEngine *engine, const Value { Scope scope(engine); ScopedObject o(scope, base.toObject(engine)); - if (scope.engine->hasException) + if (scope.hasException()) return Encode::undefined(); Q_ASSERT(o); @@ -354,7 +319,7 @@ Bool Runtime::DeleteName_NoThrow::call(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).deleteProperty(name); + return engine->currentContext()->deleteProperty(name); } ReturnedValue Runtime::DeleteName::call(ExecutionEngine *engine, Function *function, int name) @@ -368,7 +333,7 @@ ReturnedValue Runtime::DeleteName::call(ExecutionEngine *engine, Function *funct } } -QV4::ReturnedValue Runtime::Instanceof::call(ExecutionEngine *engine, const Value &lval, const Value &rval) +static QV4::ReturnedValue doInstanceof(ExecutionEngine *engine, const Value &lval, const Value &rval) { // 11.8.6, 5: rval must be an Object const Object *rhs = rval.as<Object>(); @@ -384,13 +349,75 @@ QV4::ReturnedValue Runtime::Instanceof::call(ExecutionEngine *engine, const Valu Scope scope(engine); ScopedValue hasInstance(scope, rhs->get(engine->symbol_hasInstance())); if (hasInstance->isUndefined()) - return rhs->instanceOf(lval); + return Encode(rhs->instanceOf(lval)); + FunctionObject *fHasInstance = hasInstance->as<FunctionObject>(); if (!fHasInstance) return engine->throwTypeError(); - ScopedValue result(scope, fHasInstance->call(&rval, &lval, 1)); - return Encode(result->toBoolean()); + return Encode(fHasInstance->call(&rval, &lval, 1)); +} + +QV4::ReturnedValue Runtime::Instanceof::call(ExecutionEngine *engine, const Value &lval, const Value &rval) +{ + Scope scope(engine); + ScopedValue result(scope, doInstanceof(engine, lval, rval)); + return scope.hasException() ? Encode::undefined() : Encode(result->toBoolean()); +} + +QV4::ReturnedValue Runtime::As::call(ExecutionEngine *engine, const Value &lval, const Value &rval) +{ + Scope scope(engine); + ScopedValue result(scope, doInstanceof(engine, lval, rval)); + + if (scope.hasException()) { + // "foo instanceof valueType" must not throw an exception. + // So this can only be an object type. + engine->catchException(); + return Encode::null(); + } + + if (result->toBoolean()) + return lval.asReturnedValue(); + else if (result->isBoolean()) + return Encode::null(); + + if (engine->callingQmlContext()->valueTypesAreAssertable()) + return Encode::undefined(); + + // Try to convert the value type + Scoped<QQmlTypeWrapper> typeWrapper(scope, rval); + if (!typeWrapper) + return Encode::undefined(); + + const auto *stackFrame = engine->currentStackFrame; + if (lval.as<QQmlValueTypeWrapper>()) { + qCWarning(lcCoercingTypeAssertion).nospace().noquote() + << stackFrame->source() << ':' << stackFrame->lineNumber() << ':' + << " Coercing between incompatible value types mistakenly yields null rather than" + << " undefined. Add 'pragma ValueTypeBehavior: Assertable' to prevent this."; + return Encode::null(); + } + + if (lval.as<QV4::QObjectWrapper>()) { + qCWarning(lcCoercingTypeAssertion).nospace().noquote() + << stackFrame->source() << ':' << stackFrame->lineNumber() << ':' + << " Coercing from instances of object types to value types mistakenly yields null" + << " rather than undefined. Add 'pragma ValueTypeBehavior: Assertable' to prevent" + << " this."; + return Encode::null(); + } + + result = coerce(engine, lval, typeWrapper->d()->type(), false); + if (result->isUndefined()) + return Encode::undefined(); + + qCWarning(lcCoercingTypeAssertion).nospace().noquote() + << stackFrame->source() << ':' << stackFrame->lineNumber() << ':' + << " Coercing a value to " << typeWrapper->toQStringNoThrow() + << " using a type assertion. This behavior is deprecated." + << " Add 'pragma ValueTypeBehavior: Assertable' to prevent it."; + return result->asReturnedValue(); } QV4::ReturnedValue Runtime::In::call(ExecutionEngine *engine, const Value &left, const Value &right) @@ -408,7 +435,16 @@ QV4::ReturnedValue Runtime::In::call(ExecutionEngine *engine, const Value &left, double RuntimeHelpers::stringToNumber(const QString &string) { - const QStringRef s = QStringRef(&string).trimmed(); + // The actual maximum valid length is certainly shorter, but due to the sheer number of + // different number formatting variants, we rather err on the side of caution here. + // For example, you can have up to 772 valid decimal digits left of the dot, as stated in the + // libdoubleconversion sources. The same maximum value would be represented by roughly 3.5 times + // as many binary digits. + const int excessiveLength = 16 * 1024; + if (string.size() > excessiveLength) + return qQNaN(); + + const QStringView s = QStringView(string).trimmed(); if (s.startsWith(QLatin1Char('0'))) { int base = -1; if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X"))) @@ -506,6 +542,8 @@ ReturnedValue RuntimeHelpers::ordinaryToPrimitive(ExecutionEngine *engine, const ScopedValue conv(scope, object->get(meth1)); if (FunctionObject *o = conv->as<FunctionObject>()) { result = o->call(object, nullptr, 0); + if (engine->hasException) + return Encode::undefined(); if (result->isPrimitive()) return result->asReturnedValue(); } @@ -516,6 +554,8 @@ ReturnedValue RuntimeHelpers::ordinaryToPrimitive(ExecutionEngine *engine, const conv = object->get(meth2); if (FunctionObject *o = conv->as<FunctionObject>()) { result = o->call(object, nullptr, 0); + if (engine->hasException) + return Encode::undefined(); if (result->isPrimitive()) return result->asReturnedValue(); } @@ -577,7 +617,7 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, Value val goto redo; } case Value::Integer_Type: - return RuntimeHelpers::stringFromNumber(engine, value.int_32()); + return engine->newString(QString::number(value.int_32())); default: // double return RuntimeHelpers::stringFromNumber(engine, value.doubleValue()); } // switch @@ -651,7 +691,7 @@ static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engin ScopedObject o(scope, object); if (!o) { if (const String *str = object.as<String>()) { - if (idx >= (uint)str->toQString().length()) { + if (idx >= (uint)str->toQString().size()) { return Encode::undefined(); } const QString s = str->toQString().mid(idx, 1); @@ -789,8 +829,10 @@ ReturnedValue Runtime::GetIterator::call(ExecutionEngine *engine, const Value &i ScopedFunctionObject f(scope, o->get(engine->symbol_iterator())); if (!f) return engine->throwTypeError(); - JSCallData cData(scope, 0, nullptr, o); + JSCallData cData(o, nullptr, 0); ScopedObject it(scope, f->call(cData)); + if (engine->hasException) + return Encode::undefined(); if (!it) return engine->throwTypeError(); return it->asReturnedValue(); @@ -810,7 +852,7 @@ ReturnedValue Runtime::IteratorNext::call(ExecutionEngine *engine, const Value & engine->throwTypeError(); return Encode(true); } - JSCallData cData(scope, 0, nullptr, &iterator); + JSCallData cData(&iterator, nullptr, 0); ScopedObject o(scope, f->call(cData)); if (scope.hasException()) return Encode(true); @@ -853,6 +895,8 @@ ReturnedValue Runtime::IteratorNextForYieldStar::call(ExecutionEngine *engine, c engine->hasException = false; ScopedValue ret(scope, static_cast<const Object &>(iterator).get(engine->id_return())); + if (engine->hasException) + return Encode(true); if (ret->isUndefined()) { // propagate return() return Encode::undefined(); @@ -867,14 +911,13 @@ ReturnedValue Runtime::IteratorNextForYieldStar::call(ExecutionEngine *engine, c ScopedValue t(scope, static_cast<const Object &>(iterator).get(engine->id_throw())); if (engine->hasException) - return Encode::undefined(); + return Encode(true); if (t->isUndefined()) { // no throw method on the iterator - ScopedValue done(scope, Encode(false)); - IteratorClose::call(engine, iterator, done); - if (engine->hasException) - return Encode::undefined(); - return engine->throwTypeError(); + IteratorClose::call(engine, iterator); + if (!engine->hasException) + engine->throwTypeError(); + return Encode(true); } f = t->as<FunctionObject>(); arg = exceptionValue; @@ -885,14 +928,18 @@ ReturnedValue Runtime::IteratorNextForYieldStar::call(ExecutionEngine *engine, c f = next->as<FunctionObject>(); } - if (!f) - return engine->throwTypeError(); + if (!f) { + engine->throwTypeError(); + return Encode(true); + } ScopedObject o(scope, f->call(&iterator, arg, 1)); if (scope.hasException()) return Encode(true); - if (!o) - return engine->throwTypeError(); + if (!o) { + engine->throwTypeError(); + return Encode(true); + } ScopedValue d(scope, o->get(engine->id_done())); if (scope.hasException()) @@ -900,18 +947,15 @@ ReturnedValue Runtime::IteratorNextForYieldStar::call(ExecutionEngine *engine, c bool done = d->toBoolean(); if (done) { *object = o->get(engine->id_value()); - return returnCalled ? Encode::undefined() : Encode(true); + return (returnCalled && !engine->hasException) ? Encode::undefined() : Encode(true); } *object = o; return Encode(false); } -ReturnedValue Runtime::IteratorClose::call(ExecutionEngine *engine, const Value &iterator, const Value &done) +ReturnedValue Runtime::IteratorClose::call(ExecutionEngine *engine, const Value &iterator) { Q_ASSERT(iterator.isObject()); - Q_ASSERT(done.isBoolean()); - if (done.booleanValue()) - return Encode::undefined(); Scope scope(engine); ScopedValue e(scope); @@ -972,7 +1016,7 @@ void Runtime::StoreNameSloppy::call(ExecutionEngine *engine, int nameIndex, cons { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - ExecutionContext::Error e = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).setProperty(name, value); + ExecutionContext::Error e = engine->currentContext()->setProperty(name, value); if (e == ExecutionContext::RangeError) engine->globalObject->put(name, value); @@ -982,7 +1026,7 @@ void Runtime::StoreNameStrict::call(ExecutionEngine *engine, int nameIndex, cons { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - ExecutionContext::Error e = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).setProperty(name, value); + ExecutionContext::Error e = engine->currentContext()->setProperty(name, value); if (e == ExecutionContext::TypeError) engine->throwTypeError(); else if (e == ExecutionContext::RangeError) @@ -1013,32 +1057,50 @@ ReturnedValue Runtime::LoadName::call(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name); + return engine->currentContext()->getProperty(name); } static Object *getSuperBase(Scope &scope) { - if (scope.engine->currentStackFrame->jsFrame->thisObject.isEmpty()) { - scope.engine->throwReferenceError(QStringLiteral("Missing call to super()."), QString(), 0, 0); - return nullptr; + Scoped<JavaScriptFunctionObject> f(scope); + ScopedObject homeObject(scope); + if (scope.engine->currentStackFrame->isJSTypesFrame()) { + JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>( + scope.engine->currentStackFrame); + + if (frame->jsFrame->thisObject.isEmpty()) { + scope.engine->throwReferenceError( + QStringLiteral("Missing call to super()."), QString(), 0, 0); + return nullptr; + } + + f = Value::fromStaticValue(frame->jsFrame->function); + homeObject = f->getHomeObject(); + } else { + Q_ASSERT(scope.engine->currentStackFrame->isMetaTypesFrame()); + MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>( + scope.engine->currentStackFrame); + if (frame->thisObject() == nullptr) { + scope.engine->throwReferenceError( + QStringLiteral("Missing call to super()."), QString(), 0, 0); + return nullptr; + } } - ScopedFunctionObject f( - scope, Value::fromStaticValue(scope.engine->currentStackFrame->jsFrame->function)); - ScopedObject homeObject(scope, f->getHomeObject()); if (!homeObject) { - ScopedContext ctx(scope, static_cast<ExecutionContext *>(&scope.engine->currentStackFrame->jsFrame->context)); + ScopedContext ctx(scope, scope.engine->currentContext()); Q_ASSERT(ctx); while (ctx) { if (CallContext *c = ctx->asCallContext()) { f = c->d()->function; QV4::Function *fn = f->function(); - if (fn && !fn->isArrowFunction() && !fn->isEval) + if (fn && !fn->isArrowFunction() && fn->kind != Function::Eval) break; } ctx = ctx->d()->outer; } - homeObject = f->getHomeObject(); + if (f) + homeObject = f->getHomeObject(); } if (!homeObject) { scope.engine->throwTypeError(); @@ -1062,8 +1124,18 @@ ReturnedValue Runtime::LoadSuperProperty::call(ExecutionEngine *engine, const Va ScopedPropertyKey key(scope, property.toPropertyKey(engine)); if (engine->hasException) return Encode::undefined(); - return base->get( - key, &(engine->currentStackFrame->jsFrame->thisObject.asValue<Value>())); + + if (scope.engine->currentStackFrame->isJSTypesFrame()) { + JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>( + scope.engine->currentStackFrame); + return base->get(key, &(frame->jsFrame->thisObject.asValue<Value>())); + } else { + Q_ASSERT(scope.engine->currentStackFrame->isMetaTypesFrame()); + MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>( + scope.engine->currentStackFrame); + Scoped<QObjectWrapper> wrapper(scope, QObjectWrapper::wrap(engine, frame->thisObject())); + return base->get(key, wrapper); + } } void Runtime::StoreSuperProperty::call(ExecutionEngine *engine, const Value &property, const Value &value) @@ -1075,8 +1147,20 @@ void Runtime::StoreSuperProperty::call(ExecutionEngine *engine, const Value &pro ScopedPropertyKey key(scope, property.toPropertyKey(engine)); if (engine->hasException) return; - bool result = base->put( - key, value, &(engine->currentStackFrame->jsFrame->thisObject.asValue<Value>())); + + bool result; + if (scope.engine->currentStackFrame->isJSTypesFrame()) { + JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>( + scope.engine->currentStackFrame); + result = base->put(key, value, &(frame->jsFrame->thisObject.asValue<Value>())); + } else { + Q_ASSERT(scope.engine->currentStackFrame->isMetaTypesFrame()); + MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>( + scope.engine->currentStackFrame); + Scoped<QObjectWrapper> wrapper(scope, QObjectWrapper::wrap(engine, frame->thisObject())); + result = base->put(key, value, wrapper); + } + if (!result && engine->currentStackFrame->v4Function->isStrict()) engine->throwTypeError(); } @@ -1116,14 +1200,28 @@ void Runtime::SetLookupStrict::call(Function *f, const Value &base, int index, c ReturnedValue Runtime::LoadSuperConstructor::call(ExecutionEngine *engine, const Value &t) { - if (engine->currentStackFrame->thisObject() != Value::emptyValue().asReturnedValue()) { - return engine->throwReferenceError(QStringLiteral("super() already called."), QString(), 0, 0); // ### fix line number + if (engine->currentStackFrame->isJSTypesFrame()) { + JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>(engine->currentStackFrame); + if (frame->thisObject() != Value::emptyValue().asReturnedValue()) { + // ### TODO: fix line number + return engine->throwReferenceError( + QStringLiteral("super() already called."), QString(), 0, 0); + } + } else { + Q_ASSERT(engine->currentStackFrame->isMetaTypesFrame()); + MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>(engine->currentStackFrame); + if (frame->thisObject() != nullptr) { + // ### TODO: fix line number + return engine->throwReferenceError( + QStringLiteral("super() already called."), QString(), 0, 0); + } } + const FunctionObject *f = t.as<FunctionObject>(); if (!f) return engine->throwTypeError(); Heap::Object *c = static_cast<const Object &>(t).getPrototypeOf(); - if (!c->vtable()->isFunctionObject || !static_cast<Heap::FunctionObject *>(c)->isConstructor()) + if (!c->vtable()->callAsConstructor) return engine->throwTypeError(); return c->asReturnedValue(); } @@ -1332,7 +1430,8 @@ ReturnedValue Runtime::CallGlobalLookup::call(ExecutionEngine *engine, uint inde engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString()); } - return static_cast<FunctionObject &>(function).call(&thisObject, argv, argc); + return checkedResult(engine, static_cast<FunctionObject &>(function).call( + &thisObject, argv, argc)); } ReturnedValue Runtime::CallQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index, @@ -1347,7 +1446,8 @@ ReturnedValue Runtime::CallQmlContextPropertyLookup::call(ExecutionEngine *engin engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString()); } - return static_cast<FunctionObject &>(function).call(thisObject, argv, argc); + return checkedResult(engine, static_cast<FunctionObject &>(function).call( + thisObject, argv, argc)); } ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Value *argv, int argc) @@ -1355,8 +1455,8 @@ ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Val Scope scope(engine); ScopedValue thisObject(scope); - ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context); - ScopedFunctionObject function(scope, ctx.getPropertyAndBase(engine->id_eval(), thisObject)); + ScopedFunctionObject function( + scope, engine->currentContext()->getPropertyAndBase(engine->id_eval(), thisObject)); if (engine->hasException) return Encode::undefined(); @@ -1366,7 +1466,7 @@ ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Val if (function->d() == engine->evalFunction()->d()) return static_cast<EvalFunction *>(function.getPointer())->evalCall(thisObject, argv, argc, true); - return function->call(thisObject, argv, argc); + return checkedResult(engine, function->call(thisObject, argv, argc)); } ReturnedValue Runtime::CallName::call(ExecutionEngine *engine, int nameIndex, Value *argv, int argc) @@ -1375,8 +1475,7 @@ ReturnedValue Runtime::CallName::call(ExecutionEngine *engine, int nameIndex, Va ScopedValue thisObject(scope); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context); - ScopedFunctionObject f(scope, ctx.getPropertyAndBase(name, thisObject)); + ScopedFunctionObject f(scope, engine->currentContext()->getPropertyAndBase(name, thisObject)); if (engine->hasException) return Encode::undefined(); @@ -1386,7 +1485,7 @@ ReturnedValue Runtime::CallName::call(ExecutionEngine *engine, int nameIndex, Va ->runtimeStrings[nameIndex]->toQString()); } - return f->call(thisObject, argv, argc); + return checkedResult(engine, f->call(thisObject, argv, argc)); } ReturnedValue Runtime::CallProperty::call(ExecutionEngine *engine, const Value &baseRef, int nameIndex, Value *argv, int argc) @@ -1428,7 +1527,7 @@ ReturnedValue Runtime::CallProperty::call(ExecutionEngine *engine, const Value & return engine->throwTypeError(error); } - return f->call(base, argv, argc); + return checkedResult(engine, f->call(base, argv, argc)); } ReturnedValue Runtime::CallPropertyLookup::call(ExecutionEngine *engine, const Value &base, uint index, Value *argv, int argc) @@ -1437,28 +1536,17 @@ ReturnedValue Runtime::CallPropertyLookup::call(ExecutionEngine *engine, const V // ok to have the value on the stack here Value f = Value::fromReturnedValue(l->getter(l, engine, base)); - if (!f.isFunctionObject()) - return engine->throwTypeError(); - - return static_cast<FunctionObject &>(f).call(&base, argv, argc); -} + if (Q_LIKELY(f.isFunctionObject())) + return checkedResult(engine, static_cast<FunctionObject &>(f).call(&base, argv, argc)); -ReturnedValue Runtime::CallElement::call(ExecutionEngine *engine, const Value &baseRef, const Value &index, Value *argv, int argc) -{ - const Value *base = &baseRef; - Scope scope(engine); - ScopedValue thisObject(scope, base->toObject(engine)); - base = thisObject; + if (QmlSignalHandler *handler = f.as<QmlSignalHandler>()) + return checkedResult(engine, handler->call(&base, argv, argc)); - ScopedPropertyKey str(scope, index.toPropertyKey(engine)); - if (engine->hasException) - return Encode::undefined(); - - ScopedFunctionObject f(scope, static_cast<const Object *>(base)->get(str)); - if (!f) - return engine->throwTypeError(); - - return f->call(base, argv, argc); + const QString message = QStringLiteral("Property '%1' of object %2 is not a function") + .arg(engine->currentStackFrame->v4Function->compilationUnit + ->runtimeStrings[l->nameIndex]->toQString()) + .arg(base.toQStringNoThrow()); + return engine->throwTypeError(message); } ReturnedValue Runtime::CallValue::call(ExecutionEngine *engine, const Value &func, Value *argv, int argc) @@ -1466,7 +1554,8 @@ ReturnedValue Runtime::CallValue::call(ExecutionEngine *engine, const Value &fun if (!func.isFunctionObject()) return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow())); Value undef = Value::undefinedValue(); - return static_cast<const FunctionObject &>(func).call(&undef, argv, argc); + return checkedResult(engine, static_cast<const FunctionObject &>(func).call( + &undef, argv, argc)); } ReturnedValue Runtime::CallWithReceiver::call(ExecutionEngine *engine, const Value &func, @@ -1474,7 +1563,8 @@ ReturnedValue Runtime::CallWithReceiver::call(ExecutionEngine *engine, const Val { if (!func.isFunctionObject()) return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow())); - return static_cast<const FunctionObject &>(func).call(&thisObject, argv, argc); + return checkedResult(engine, static_cast<const FunctionObject &>(func).call( + &thisObject, argv, argc)); } struct CallArgs { @@ -1501,16 +1591,21 @@ static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc) // spread element ++i; it = Runtime::GetIterator::call(scope.engine, argv[i], /* ForInIterator */ 1); - if (scope.engine->hasException) + if (scope.hasException()) return { nullptr, 0 }; while (1) { done = Runtime::IteratorNext::call(scope.engine, it, v); - if (scope.engine->hasException) + if (scope.hasException()) return { nullptr, 0 }; Q_ASSERT(done->isBoolean()); if (done->booleanValue()) break; ++argCount; + constexpr auto safetyMargin = 100; // leave some space on the stack for actual work with the elements + if (qint64(scope.engine->jsStackLimit - scope.engine->jsStackTop) < safetyMargin) { + scope.engine->throwRangeError(QLatin1String("Too many elements in array to use it with the spread operator")); + return { nullptr, 0 }; + } v = scope.alloc<Scope::Uninitialized>(); } } @@ -1528,7 +1623,8 @@ ReturnedValue Runtime::CallWithSpread::call(ExecutionEngine *engine, const Value if (engine->hasException) return Encode::undefined(); - return static_cast<const FunctionObject &>(function).call(&thisObject, arguments.argv, arguments.argc); + return checkedResult(engine, static_cast<const FunctionObject &>(function).call( + &thisObject, arguments.argv, arguments.argc)); } ReturnedValue Runtime::Construct::call(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc) @@ -1552,7 +1648,7 @@ ReturnedValue Runtime::ConstructWithSpread::call(ExecutionEngine *engine, const return static_cast<const FunctionObject &>(function).callAsConstructor(arguments.argv, arguments.argc, &newTarget); } -ReturnedValue Runtime::TailCall::call(CppStackFrame *frame, ExecutionEngine *engine) +ReturnedValue Runtime::TailCall::call(JSTypesStackFrame *frame, ExecutionEngine *engine) { // IMPORTANT! The JIT assumes that this method has the same amount (or less) arguments than // the jitted function, so it can safely do a tail call. @@ -1564,22 +1660,26 @@ ReturnedValue Runtime::TailCall::call(CppStackFrame *frame, ExecutionEngine *eng int argc = tos[StackOffsets::tailCall_argc].int_32(); Q_ASSERT(argc >= 0); - if (!function.isFunctionObject()) + const JavaScriptFunctionObject *jsfo = function.as<JavaScriptFunctionObject>(); + if (!jsfo) { + if (const FunctionObject *fo = function.as<FunctionObject>()) + return checkedResult(engine, fo->call(&thisObject, argv, argc)); return engine->throwTypeError(); + } - const FunctionObject &fo = static_cast<const FunctionObject &>(function); - if (!frame->callerCanHandleTailCall || !fo.canBeTailCalled() || engine->debugger() - || unsigned(argc) > fo.formalParameterCount()) { + if (!frame->callerCanHandleTailCall() || !jsfo->canBeTailCalled() || engine->debugger() + || unsigned(argc) > jsfo->formalParameterCount()) { // Cannot tailcall, do a normal call: - return fo.call(&thisObject, argv, argc); + return checkedResult(engine, jsfo->call(&thisObject, argv, argc)); } - memcpy(frame->jsFrame->args, argv, argc * sizeof(Value)); - frame->init(engine, fo.function(), frame->jsFrame->argValues<Value>(), argc, - frame->callerCanHandleTailCall); - frame->setupJSFrame(frame->savedStackTop, fo, fo.scope(), thisObject, Primitive::undefinedValue()); - engine->jsStackTop = frame->savedStackTop + frame->requiredJSStackFrameSize(); - frame->pendingTailCall = true; + memmove(frame->jsFrame->args, argv, argc * sizeof(Value)); + frame->init(jsfo->function(), frame->jsFrame->argValues<Value>(), argc, + frame->callerCanHandleTailCall()); + frame->setupJSFrame(frame->framePointer(), *jsfo, jsfo->scope(), thisObject, + Primitive::undefinedValue()); + engine->jsStackTop = frame->framePointer() + frame->requiredJSStackFrameSize(); + frame->setPendingTailCall(true); return Encode::undefined(); } @@ -1624,20 +1724,21 @@ QV4::ReturnedValue Runtime::TypeofName::call(ExecutionEngine *engine, int nameIn { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - ScopedValue prop(scope, static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name)); + ScopedValue prop(scope, engine->currentContext()->getProperty(name)); // typeof doesn't throw. clear any possible exception scope.engine->hasException = false; return TypeofValue::call(engine, prop); } -void Runtime::PushCallContext::call(CppStackFrame *frame) +void Runtime::PushCallContext::call(JSTypesStackFrame *frame) { frame->jsFrame->context = ExecutionContext::newCallContext(frame)->asReturnedValue(); } ReturnedValue Runtime::PushWithContext::call(ExecutionEngine *engine, const Value &acc) { - CallData *jsFrame = engine->currentStackFrame->jsFrame; + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); + CallData *jsFrame = static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame; Value &newAcc = jsFrame->accumulator.asValue<Value>(); newAcc = Value::fromHeapObject(acc.toObject(engine)); if (!engine->hasException) { @@ -1652,18 +1753,23 @@ ReturnedValue Runtime::PushWithContext::call(ExecutionEngine *engine, const Valu void Runtime::PushCatchContext::call(ExecutionEngine *engine, int blockIndex, int exceptionVarNameIndex) { + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); auto name = engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex]; - engine->currentStackFrame->jsFrame->context = ExecutionContext::newCatchContext(engine->currentStackFrame, blockIndex, name)->asReturnedValue(); + static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame->context + = ExecutionContext::newCatchContext(engine->currentStackFrame, blockIndex, name)->asReturnedValue(); } void Runtime::PushBlockContext::call(ExecutionEngine *engine, int index) { - engine->currentStackFrame->jsFrame->context = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue(); + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); + static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame->context + = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue(); } void Runtime::CloneBlockContext::call(ExecutionEngine *engine) { - auto frame = engine->currentStackFrame; + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); + auto frame = static_cast<JSTypesStackFrame *>(engine->currentStackFrame); auto context = static_cast<Heap::CallContext *>( Value::fromStaticValue(frame->jsFrame->context).m()); frame->jsFrame->context = @@ -1672,18 +1778,20 @@ void Runtime::CloneBlockContext::call(ExecutionEngine *engine) void Runtime::PushScriptContext::call(ExecutionEngine *engine, int index) { - Q_ASSERT(engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_GlobalContext || - engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_QmlContext); + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); + Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_GlobalContext || + engine->currentContext()->d()->type == Heap::ExecutionContext::Type_QmlContext); ReturnedValue c = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue(); engine->setScriptContext(c); - engine->currentStackFrame->jsFrame->context = c; + static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame->context = c; } void Runtime::PopScriptContext::call(ExecutionEngine *engine) { + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); ReturnedValue root = engine->rootContext()->asReturnedValue(); engine->setScriptContext(root); - engine->currentStackFrame->jsFrame->context = root; + static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame->context = root; } void Runtime::ThrowReferenceError::call(ExecutionEngine *engine, int nameIndex) @@ -1699,6 +1807,21 @@ void Runtime::ThrowOnNullOrUndefined::call(ExecutionEngine *engine, const Value engine->throwTypeError(); } +void Runtime::MarkCustom::call(const Value &toBeMarked) +{ + auto *h = toBeMarked.heapObject(); + if (!h) + return; + Q_ASSERT(h->internalClass); + auto engine = h->internalClass->engine; + Q_ASSERT(engine); + // runtime function is only meant to be called while gc is ongoing + Q_ASSERT(engine->isGCOngoing); + QV4::WriteBarrier::markCustom(engine, [&](QV4::MarkStack *ms) { + h->mark(ms); + }); +} + ReturnedValue Runtime::ConvertThisToObject::call(ExecutionEngine *engine, const Value &t) { if (!t.isObject()) { @@ -1715,7 +1838,7 @@ void Runtime::DeclareVar::call(ExecutionEngine *engine, Bool deletable, int name { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).createMutableBinding(name, deletable); + engine->currentContext()->createMutableBinding(name, deletable); } ReturnedValue Runtime::ArrayLiteral::call(ExecutionEngine *engine, Value *values, uint length) @@ -1768,7 +1891,7 @@ ReturnedValue Runtime::ObjectLiteral::call(ExecutionEngine *engine, int classId, arg = ObjectLiteralArgument::Value; fnName = name->asFunctionName(engine, prefix); - ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context); + ExecutionContext *current = engine->currentContext(); if (clos->isGenerator()) value = MemberGeneratorFunction::create(current, clos, o, fnName)->asReturnedValue(); else @@ -1826,7 +1949,7 @@ ReturnedValue Runtime::CreateClass::call(ExecutionEngine *engine, int classIndex ScopedObject proto(scope, engine->newObject()); proto->setPrototypeUnchecked(protoParent); - ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context); + ExecutionContext *current = engine->currentContext(); ScopedFunctionObject constructor(scope); QV4::Function *f = cls->constructorFunction != UINT_MAX ? unit->runtimeFunctions[cls->constructorFunction] : nullptr; @@ -1910,14 +2033,18 @@ QV4::ReturnedValue Runtime::CreateMappedArgumentsObject::call(ExecutionEngine *e QV4::ReturnedValue Runtime::CreateUnmappedArgumentsObject::call(ExecutionEngine *engine) { + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_StrictArgumentsObject); - return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue(); + return engine->memoryManager->allocObject<StrictArgumentsObject>( + ic, static_cast<JSTypesStackFrame *>(engine->currentStackFrame))->asReturnedValue(); } QV4::ReturnedValue Runtime::CreateRestParameter::call(ExecutionEngine *engine, int argIndex) { - const Value *values = engine->currentStackFrame->originalArguments + argIndex; - int nValues = engine->currentStackFrame->originalArgumentsCount - argIndex; + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); + JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>(engine->currentStackFrame); + const Value *values = frame->argv() + argIndex; + int nValues = frame->argc() - argIndex; if (nValues <= 0) return engine->newArrayObject(0)->asReturnedValue(); return engine->newArrayObject(values, nValues)->asReturnedValue(); @@ -2049,9 +2176,7 @@ ReturnedValue Runtime::Exp::call(const Value &base, const Value &exp) { double b = base.toNumber(); double e = exp.toNumber(); - if (qt_is_inf(e) && (b == 1 || b == -1)) - return Encode(qt_qnan()); - return Encode(pow(b,e)); + return Encode(QQmlPrivate::jsExponentiate(b, e)); } ReturnedValue Runtime::BitAnd::call(const Value &left, const Value &right) @@ -2173,35 +2298,23 @@ Bool Runtime::CompareEqual::call(const Value &left, const Value &right) Value *lhsGuard = nullptr; Value *rhsGuard = nullptr; - redo: + redo: if (lhs.asReturnedValue() == rhs.asReturnedValue()) return !lhs.isNaN(); - int lt = lhs.quickType(); - int rt = rhs.quickType(); - if (rt < lt) { - qSwap(lhs, rhs); - qSwap(lt, rt); - } + quint32 lt = lhs.quickType(); + quint32 rt = rhs.quickType(); - switch (lt) { - case QV4::Value::QT_ManagedOrUndefined: + // LHS: Check if managed + if ((lt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) { if (lhs.isUndefined()) return rhs.isNullOrUndefined(); - Q_FALLTHROUGH(); - case QV4::Value::QT_ManagedOrUndefined1: - case QV4::Value::QT_ManagedOrUndefined2: - case QV4::Value::QT_ManagedOrUndefined3: - // LHS: Managed - switch (rt) { - case QV4::Value::QT_ManagedOrUndefined: + + // RHS: Check if managed + if ((rt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) { if (rhs.isUndefined()) return false; - Q_FALLTHROUGH(); - case QV4::Value::QT_ManagedOrUndefined1: - case QV4::Value::QT_ManagedOrUndefined2: - case QV4::Value::QT_ManagedOrUndefined3: { - // RHS: Managed + Heap::Base *l = lhs.m(); Heap::Base *r = rhs.m(); Q_ASSERT(l); @@ -2211,15 +2324,18 @@ Bool Runtime::CompareEqual::call(const Value &left, const Value &right) if (l->internalClass->vtable->isStringOrSymbol) { scope.set(&rhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(rhs), PREFERREDTYPE_HINT), r->internalClass->engine); rhs = rhsGuard->asReturnedValue(); - break; + goto redo; } else { Q_ASSERT(r->internalClass->vtable->isStringOrSymbol); scope.set(&lhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT), l->internalClass->engine); lhs = lhsGuard->asReturnedValue(); - break; + goto redo; } return false; } + +lhs_managed_and_rhs_not: + switch (rt) { case QV4::Value::QT_Empty: Q_UNREACHABLE(); case QV4::Value::QT_Null: @@ -2227,7 +2343,7 @@ Bool Runtime::CompareEqual::call(const Value &left, const Value &right) case QV4::Value::QT_Bool: case QV4::Value::QT_Int: rhs = Value::fromDouble(rhs.int_32()); - // fall through + Q_FALLTHROUGH(); default: // double if (lhs.m()->internalClass->vtable->isStringOrSymbol) { return lhs.m()->internalClass->vtable->isString ? (RuntimeHelpers::toNumber(lhs) == rhs.doubleValue()) : false; @@ -2237,6 +2353,15 @@ Bool Runtime::CompareEqual::call(const Value &left, const Value &right) } } goto redo; + } else if ((rt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) { + if (rhs.isUndefined()) + return lhs.isNull(); // Can't be undefined + qSwap(lhs, rhs); + qSwap(lt, rt); + goto lhs_managed_and_rhs_not; + } + + switch (lt) { case QV4::Value::QT_Empty: Q_UNREACHABLE(); case QV4::Value::QT_Null: @@ -2244,13 +2369,10 @@ Bool Runtime::CompareEqual::call(const Value &left, const Value &right) case QV4::Value::QT_Bool: case QV4::Value::QT_Int: switch (rt) { - case QV4::Value::QT_ManagedOrUndefined: - case QV4::Value::QT_ManagedOrUndefined1: - case QV4::Value::QT_ManagedOrUndefined2: - case QV4::Value::QT_ManagedOrUndefined3: case QV4::Value::QT_Empty: - case QV4::Value::QT_Null: Q_UNREACHABLE(); + case QV4::Value::QT_Null: + return false; case QV4::Value::QT_Bool: case QV4::Value::QT_Int: return lhs.int_32() == rhs.int_32(); @@ -2258,8 +2380,17 @@ Bool Runtime::CompareEqual::call(const Value &left, const Value &right) return lhs.int_32() == rhs.doubleValue(); } default: // double - Q_ASSERT(rhs.isDouble()); - return lhs.doubleValue() == rhs.doubleValue(); + switch (rt) { + case QV4::Value::QT_Empty: + Q_UNREACHABLE(); + case QV4::Value::QT_Null: + return false; + case QV4::Value::QT_Bool: + case QV4::Value::QT_Int: + return lhs.doubleValue() == rhs.int_32(); + default: // double + return lhs.doubleValue() == rhs.doubleValue(); + } } } @@ -2331,7 +2462,6 @@ QHash<const void *, const char *> Runtime::symbolTable() {symbol<CallName>(), "CallName" }, {symbol<CallProperty>(), "CallProperty" }, {symbol<CallPropertyLookup>(), "CallPropertyLookup" }, - {symbol<CallElement>(), "CallElement" }, {symbol<CallValue>(), "CallValue" }, {symbol<CallWithReceiver>(), "CallWithReceiver" }, {symbol<CallPossiblyDirectEval>(), "CallPossiblyDirectEval" }, @@ -2378,6 +2508,8 @@ QHash<const void *, const char *> Runtime::symbolTable() {symbol<Closure>(), "Closure" }, + {symbol<MarkCustom>(), "MarkCustom"}, + {symbol<ConvertThisToObject>(), "ConvertThisToObject" }, {symbol<DeclareVar>(), "DeclareVar" }, {symbol<CreateMappedArgumentsObject>(), "CreateMappedArgumentsObject" }, @@ -2401,6 +2533,7 @@ QHash<const void *, const char *> Runtime::symbolTable() {symbol<UMinus>(), "UMinus" }, {symbol<Instanceof>(), "Instanceof" }, + {symbol<As>(), "As" }, {symbol<In>(), "In" }, {symbol<Add>(), "Add" }, {symbol<Sub>(), "Sub" }, diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index cb89b7e6c0..a012728cd9 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QMLJS_RUNTIME_H #define QMLJS_RUNTIME_H @@ -52,7 +16,6 @@ #include "qv4global_p.h" #include "qv4value_p.h" -#include "qv4math_p.h" #include "qv4runtimeapi_p.h" #include <QtCore/qnumeric.h> @@ -95,7 +58,7 @@ enum TypeHint { STRING_HINT }; -struct Q_QML_PRIVATE_EXPORT RuntimeHelpers { +struct Q_QML_EXPORT RuntimeHelpers { static ReturnedValue objectDefaultValue(const Object *object, int typeHint); static ReturnedValue toPrimitive(const Value &value, TypeHint typeHint); static ReturnedValue ordinaryToPrimitive(ExecutionEngine *engine, const Object *object, String *typeHint); diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index d155187e48..e4a8c09370 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4RUNTIMEAPI_P_H #define QV4RUNTIMEAPI_P_H @@ -59,7 +23,7 @@ namespace QV4 { typedef uint Bool; -struct Q_QML_PRIVATE_EXPORT Runtime { +struct Q_QML_EXPORT Runtime { typedef ReturnedValue (*UnaryOperation)(const Value &value); typedef ReturnedValue (*BinaryOperation)(const Value &left, const Value &right); typedef ReturnedValue (*BinaryOperationContext)(ExecutionEngine *, const Value &left, const Value &right); @@ -79,418 +43,424 @@ struct Q_QML_PRIVATE_EXPORT Runtime { static constexpr bool lastArgumentIsOutputValue = out == LastArgumentIsOutputValue::Yes; }; using PureMethod = Method<Throws::No, ChangesContext::No, Pure::Yes>; - using IteratorMethod = Method<Throws::Yes, ChangesContext::No, Pure::No, + using IteratorMethod = Method<Throws::No, ChangesContext::No, Pure::No, LastArgumentIsOutputValue::Yes>; /* call */ - struct Q_QML_PRIVATE_EXPORT CallGlobalLookup : Method<Throws::Yes> + struct Q_QML_EXPORT CallGlobalLookup : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, uint, Value[], int); }; - struct Q_QML_PRIVATE_EXPORT CallQmlContextPropertyLookup : Method<Throws::Yes> + struct Q_QML_EXPORT CallQmlContextPropertyLookup : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, uint, Value[], int); }; - struct Q_QML_PRIVATE_EXPORT CallName : Method<Throws::Yes> + struct Q_QML_EXPORT CallName : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, int, Value[], int); }; - struct Q_QML_PRIVATE_EXPORT CallProperty : Method<Throws::Yes> + struct Q_QML_EXPORT CallProperty : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &, int, Value[], int); }; - struct Q_QML_PRIVATE_EXPORT CallPropertyLookup : Method<Throws::Yes> + struct Q_QML_EXPORT CallPropertyLookup : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &, uint, Value[], int); }; - struct Q_QML_PRIVATE_EXPORT CallElement : Method<Throws::Yes> - { - static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int); - }; - struct Q_QML_PRIVATE_EXPORT CallValue : Method<Throws::Yes> + struct Q_QML_EXPORT CallValue : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &, Value[], int); }; - struct Q_QML_PRIVATE_EXPORT CallWithReceiver : Method<Throws::Yes> + struct Q_QML_EXPORT CallWithReceiver : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int); }; - struct Q_QML_PRIVATE_EXPORT CallPossiblyDirectEval : Method<Throws::Yes> + struct Q_QML_EXPORT CallPossiblyDirectEval : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, Value[], int); }; - struct Q_QML_PRIVATE_EXPORT CallWithSpread : Method<Throws::Yes> + struct Q_QML_EXPORT CallWithSpread : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int); }; - struct Q_QML_PRIVATE_EXPORT TailCall : Method<Throws::Yes> + struct Q_QML_EXPORT TailCall : Method<Throws::Yes> { - static ReturnedValue call(CppStackFrame *, ExecutionEngine *); + static ReturnedValue call(JSTypesStackFrame *, ExecutionEngine *engine); }; /* construct */ - struct Q_QML_PRIVATE_EXPORT Construct : Method<Throws::Yes> + struct Q_QML_EXPORT Construct : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int); }; - struct Q_QML_PRIVATE_EXPORT ConstructWithSpread : Method<Throws::Yes> + struct Q_QML_EXPORT ConstructWithSpread : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value[], int); }; /* load & store */ - struct Q_QML_PRIVATE_EXPORT StoreNameStrict : Method<Throws::Yes> + struct Q_QML_EXPORT StoreNameStrict : Method<Throws::Yes> { static void call(ExecutionEngine *, int, const Value &); }; - struct Q_QML_PRIVATE_EXPORT StoreNameSloppy : Method<Throws::Yes> + struct Q_QML_EXPORT StoreNameSloppy : Method<Throws::Yes> { static void call(ExecutionEngine *, int, const Value &); }; - struct Q_QML_PRIVATE_EXPORT StoreProperty : Method<Throws::Yes> + struct Q_QML_EXPORT StoreProperty : Method<Throws::Yes> { static void call(ExecutionEngine *, const Value &, int, const Value &); }; - struct Q_QML_PRIVATE_EXPORT StoreElement : Method<Throws::Yes> + struct Q_QML_EXPORT StoreElement : Method<Throws::Yes> { static void call(ExecutionEngine *, const Value &, const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT LoadProperty : Method<Throws::Yes> + struct Q_QML_EXPORT LoadProperty : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &, int); }; - struct Q_QML_PRIVATE_EXPORT LoadName : Method<Throws::Yes> + struct Q_QML_EXPORT LoadName : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, int); }; - struct Q_QML_PRIVATE_EXPORT LoadElement : Method<Throws::Yes> + struct Q_QML_EXPORT LoadElement : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT LoadSuperProperty : Method<Throws::Yes> + struct Q_QML_EXPORT LoadSuperProperty : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &); }; - struct Q_QML_PRIVATE_EXPORT StoreSuperProperty : Method<Throws::Yes> + struct Q_QML_EXPORT StoreSuperProperty : Method<Throws::Yes> { static void call(ExecutionEngine *, const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT LoadSuperConstructor : Method<Throws::Yes> + struct Q_QML_EXPORT LoadSuperConstructor : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &); }; - struct Q_QML_PRIVATE_EXPORT LoadGlobalLookup : Method<Throws::Yes> + struct Q_QML_EXPORT LoadGlobalLookup : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, Function *, int); }; - struct Q_QML_PRIVATE_EXPORT LoadQmlContextPropertyLookup : Method<Throws::Yes> + struct Q_QML_EXPORT LoadQmlContextPropertyLookup : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, uint); }; - struct Q_QML_PRIVATE_EXPORT GetLookup : Method<Throws::Yes> + struct Q_QML_EXPORT GetLookup : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, Function *, const Value &, int); }; - struct Q_QML_PRIVATE_EXPORT SetLookupStrict : Method<Throws::Yes> + struct Q_QML_EXPORT SetLookupStrict : Method<Throws::Yes> { static void call(Function *, const Value &, int, const Value &); }; - struct Q_QML_PRIVATE_EXPORT SetLookupSloppy : Method<Throws::Yes> + struct Q_QML_EXPORT SetLookupSloppy : Method<Throws::Yes> { static void call(Function *, const Value &, int, const Value &); }; /* typeof */ - struct Q_QML_PRIVATE_EXPORT TypeofValue : PureMethod + struct Q_QML_EXPORT TypeofValue : PureMethod { static ReturnedValue call(ExecutionEngine *, const Value &); }; - struct Q_QML_PRIVATE_EXPORT TypeofName : Method<Throws::No> + struct Q_QML_EXPORT TypeofName : Method<Throws::No> { static ReturnedValue call(ExecutionEngine *, int); }; /* delete */ - struct Q_QML_PRIVATE_EXPORT DeleteProperty_NoThrow : Method<Throws::No> + struct Q_QML_EXPORT DeleteProperty_NoThrow : Method<Throws::No> { static Bool call(ExecutionEngine *, const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT DeleteProperty : Method<Throws::Yes> + struct Q_QML_EXPORT DeleteProperty : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, Function *, const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT DeleteName_NoThrow : Method<Throws::No> + struct Q_QML_EXPORT DeleteName_NoThrow : Method<Throws::No> { static Bool call(ExecutionEngine *, int); }; - struct Q_QML_PRIVATE_EXPORT DeleteName : Method<Throws::Yes> + struct Q_QML_EXPORT DeleteName : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, Function *, int); }; /* exceptions & scopes */ - struct Q_QML_PRIVATE_EXPORT ThrowException : Method<Throws::Yes> + struct Q_QML_EXPORT ThrowException : Method<Throws::Yes> { static void call(ExecutionEngine *, const Value &); }; - struct Q_QML_PRIVATE_EXPORT PushCallContext : Method<Throws::No, ChangesContext::Yes> + struct Q_QML_EXPORT PushCallContext : Method<Throws::No, ChangesContext::Yes> { - static void call(CppStackFrame *); + static void call(JSTypesStackFrame *); }; - struct Q_QML_PRIVATE_EXPORT PushWithContext : Method<Throws::Yes, ChangesContext::Yes> + struct Q_QML_EXPORT PushWithContext : Method<Throws::Yes, ChangesContext::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &); }; - struct Q_QML_PRIVATE_EXPORT PushCatchContext : Method<Throws::No, ChangesContext::Yes> + struct Q_QML_EXPORT PushCatchContext : Method<Throws::No, ChangesContext::Yes> { static void call(ExecutionEngine *, int, int); }; - struct Q_QML_PRIVATE_EXPORT PushBlockContext : Method<Throws::No, ChangesContext::Yes> + struct Q_QML_EXPORT PushBlockContext : Method<Throws::No, ChangesContext::Yes> { static void call(ExecutionEngine *, int); }; - struct Q_QML_PRIVATE_EXPORT CloneBlockContext : Method<Throws::No, ChangesContext::Yes> + struct Q_QML_EXPORT CloneBlockContext : Method<Throws::No, ChangesContext::Yes> { static void call(ExecutionEngine *); }; - struct Q_QML_PRIVATE_EXPORT PushScriptContext : Method<Throws::No, ChangesContext::Yes> + struct Q_QML_EXPORT PushScriptContext : Method<Throws::No, ChangesContext::Yes> { static void call(ExecutionEngine *, int); }; - struct Q_QML_PRIVATE_EXPORT PopScriptContext : Method<Throws::No, ChangesContext::Yes> + struct Q_QML_EXPORT PopScriptContext : Method<Throws::No, ChangesContext::Yes> { static void call(ExecutionEngine *); }; - struct Q_QML_PRIVATE_EXPORT ThrowReferenceError : Method<Throws::Yes> + struct Q_QML_EXPORT ThrowReferenceError : Method<Throws::Yes> { static void call(ExecutionEngine *, int); }; - struct Q_QML_PRIVATE_EXPORT ThrowOnNullOrUndefined : Method<Throws::Yes> + struct Q_QML_EXPORT ThrowOnNullOrUndefined : Method<Throws::Yes> { static void call(ExecutionEngine *, const Value &); }; + /* garbage collection */ + struct Q_QML_EXPORT MarkCustom : PureMethod + { + static void call(const Value &toBeMarked); + }; + /* closures */ - struct Q_QML_PRIVATE_EXPORT Closure : Method<Throws::No> + struct Q_QML_EXPORT Closure : Method<Throws::No> { static ReturnedValue call(ExecutionEngine *, int); }; /* Function header */ - struct Q_QML_PRIVATE_EXPORT ConvertThisToObject : Method<Throws::Yes> + struct Q_QML_EXPORT ConvertThisToObject : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &); }; - struct Q_QML_PRIVATE_EXPORT DeclareVar : Method<Throws::Yes> + struct Q_QML_EXPORT DeclareVar : Method<Throws::Yes> { static void call(ExecutionEngine *, Bool, int); }; - struct Q_QML_PRIVATE_EXPORT CreateMappedArgumentsObject : Method<Throws::No> + struct Q_QML_EXPORT CreateMappedArgumentsObject : Method<Throws::No> { static ReturnedValue call(ExecutionEngine *); }; - struct Q_QML_PRIVATE_EXPORT CreateUnmappedArgumentsObject : Method<Throws::No> + struct Q_QML_EXPORT CreateUnmappedArgumentsObject : Method<Throws::No> { static ReturnedValue call(ExecutionEngine *); }; - struct Q_QML_PRIVATE_EXPORT CreateRestParameter : PureMethod + struct Q_QML_EXPORT CreateRestParameter : PureMethod { static ReturnedValue call(ExecutionEngine *, int); }; /* literals */ - struct Q_QML_PRIVATE_EXPORT ArrayLiteral : Method<Throws::Yes> + struct Q_QML_EXPORT ArrayLiteral : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, Value[], uint); }; - struct Q_QML_PRIVATE_EXPORT ObjectLiteral : Method<Throws::Yes> + struct Q_QML_EXPORT ObjectLiteral : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, int, Value[], int); }; - struct Q_QML_PRIVATE_EXPORT CreateClass : Method<Throws::Yes> + struct Q_QML_EXPORT CreateClass : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, int, const Value &, Value[]); }; /* for-in, for-of and array destructuring */ - struct Q_QML_PRIVATE_EXPORT GetIterator : Method<Throws::Yes> + struct Q_QML_EXPORT GetIterator : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &, int); }; - struct Q_QML_PRIVATE_EXPORT IteratorNext : IteratorMethod + struct Q_QML_EXPORT IteratorNext : IteratorMethod { static ReturnedValue call(ExecutionEngine *, const Value &, Value *); }; - struct Q_QML_PRIVATE_EXPORT IteratorNextForYieldStar : IteratorMethod + struct Q_QML_EXPORT IteratorNextForYieldStar : IteratorMethod { static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, Value *); }; - struct Q_QML_PRIVATE_EXPORT IteratorClose : Method<Throws::Yes> + struct Q_QML_EXPORT IteratorClose : Method<Throws::No> { - static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); + static ReturnedValue call(ExecutionEngine *, const Value &); }; - struct Q_QML_PRIVATE_EXPORT DestructureRestElement : Method<Throws::Yes> + struct Q_QML_EXPORT DestructureRestElement : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &); }; /* conversions */ - struct Q_QML_PRIVATE_EXPORT ToObject : Method<Throws::Yes> + struct Q_QML_EXPORT ToObject : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &); }; - struct Q_QML_PRIVATE_EXPORT ToBoolean : PureMethod + struct Q_QML_EXPORT ToBoolean : PureMethod { static Bool call(const Value &); }; - struct Q_QML_PRIVATE_EXPORT ToNumber : Method<Throws::Yes> + struct Q_QML_EXPORT ToNumber : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &); }; /* unary operators */ - struct Q_QML_PRIVATE_EXPORT UMinus : Method<Throws::Yes> + struct Q_QML_EXPORT UMinus : Method<Throws::Yes> { static ReturnedValue call(const Value &); }; /* binary operators */ - struct Q_QML_PRIVATE_EXPORT Instanceof : Method<Throws::Yes> + struct Q_QML_EXPORT Instanceof : Method<Throws::Yes> + { + static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); + }; + struct Q_QML_EXPORT As : Method<Throws::No> { static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT In : Method<Throws::Yes> + struct Q_QML_EXPORT In : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT Add : Method<Throws::Yes> + struct Q_QML_EXPORT Add : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT Sub : Method<Throws::Yes> + struct Q_QML_EXPORT Sub : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT Mul : Method<Throws::Yes> + struct Q_QML_EXPORT Mul : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT Div : Method<Throws::Yes> + struct Q_QML_EXPORT Div : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT Mod : Method<Throws::Yes> + struct Q_QML_EXPORT Mod : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT Exp : Method<Throws::Yes> + struct Q_QML_EXPORT Exp : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT BitAnd : Method<Throws::Yes> + struct Q_QML_EXPORT BitAnd : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT BitOr : Method<Throws::Yes> + struct Q_QML_EXPORT BitOr : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT BitXor : Method<Throws::Yes> + struct Q_QML_EXPORT BitXor : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT Shl : Method<Throws::Yes> + struct Q_QML_EXPORT Shl : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT Shr : Method<Throws::Yes> + struct Q_QML_EXPORT Shr : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT UShr : Method<Throws::Yes> + struct Q_QML_EXPORT UShr : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT GreaterThan : Method<Throws::Yes> + struct Q_QML_EXPORT GreaterThan : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT LessThan : Method<Throws::Yes> + struct Q_QML_EXPORT LessThan : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT GreaterEqual : Method<Throws::Yes> + struct Q_QML_EXPORT GreaterEqual : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT LessEqual : Method<Throws::Yes> + struct Q_QML_EXPORT LessEqual : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT Equal : Method<Throws::Yes> + struct Q_QML_EXPORT Equal : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT NotEqual : Method<Throws::Yes> + struct Q_QML_EXPORT NotEqual : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT StrictEqual : Method<Throws::Yes> + struct Q_QML_EXPORT StrictEqual : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT StrictNotEqual : Method<Throws::Yes> + struct Q_QML_EXPORT StrictNotEqual : Method<Throws::Yes> { static ReturnedValue call(const Value &, const Value &); }; /* comparisons */ - struct Q_QML_PRIVATE_EXPORT CompareGreaterThan : Method<Throws::Yes> + struct Q_QML_EXPORT CompareGreaterThan : Method<Throws::Yes> { static Bool call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT CompareLessThan : Method<Throws::Yes> + struct Q_QML_EXPORT CompareLessThan : Method<Throws::Yes> { static Bool call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT CompareGreaterEqual : Method<Throws::Yes> + struct Q_QML_EXPORT CompareGreaterEqual : Method<Throws::Yes> { static Bool call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT CompareLessEqual : Method<Throws::Yes> + struct Q_QML_EXPORT CompareLessEqual : Method<Throws::Yes> { static Bool call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT CompareEqual : Method<Throws::Yes> + struct Q_QML_EXPORT CompareEqual : Method<Throws::Yes> { static Bool call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT CompareNotEqual : Method<Throws::Yes> + struct Q_QML_EXPORT CompareNotEqual : Method<Throws::Yes> { static Bool call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT CompareStrictEqual : Method<Throws::Yes> + struct Q_QML_EXPORT CompareStrictEqual : Method<Throws::Yes> { static Bool call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT CompareStrictNotEqual : Method<Throws::Yes> + struct Q_QML_EXPORT CompareStrictNotEqual : Method<Throws::Yes> { static Bool call(const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT CompareInstanceof : Method<Throws::Yes> + struct Q_QML_EXPORT CompareInstanceof : Method<Throws::Yes> { static Bool call(ExecutionEngine *, const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT CompareIn : Method<Throws::Yes> + struct Q_QML_EXPORT CompareIn : Method<Throws::Yes> { static Bool call(ExecutionEngine *, const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT RegexpLiteral : PureMethod + struct Q_QML_EXPORT RegexpLiteral : PureMethod { static ReturnedValue call(ExecutionEngine *, int); }; - struct Q_QML_PRIVATE_EXPORT GetTemplateObject : PureMethod + struct Q_QML_EXPORT GetTemplateObject : PureMethod { static ReturnedValue call(Function *, int); }; diff --git a/src/qml/jsruntime/qv4runtimecodegen.cpp b/src/qml/jsruntime/qv4runtimecodegen.cpp index 162d75db63..c7b05aeffe 100644 --- a/src/qml/jsruntime/qv4runtimecodegen.cpp +++ b/src/qml/jsruntime/qv4runtimecodegen.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4engine_p.h" #include "qv4runtimecodegen_p.h" @@ -67,7 +31,7 @@ void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName, _module->rootContext = _module->functions.at(index); } -void RuntimeCodegen::throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) +void RuntimeCodegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) { if (hasError()) return; @@ -76,7 +40,7 @@ void RuntimeCodegen::throwSyntaxError(const AST::SourceLocation &loc, const QStr engine->throwSyntaxError(detail, _module->fileName, loc.startLine, loc.startColumn); } -void RuntimeCodegen::throwReferenceError(const AST::SourceLocation &loc, const QString &detail) +void RuntimeCodegen::throwReferenceError(const SourceLocation &loc, const QString &detail) { if (hasError()) return; diff --git a/src/qml/jsruntime/qv4runtimecodegen_p.h b/src/qml/jsruntime/qv4runtimecodegen_p.h index 71aaf1fb55..35058be2cb 100644 --- a/src/qml/jsruntime/qv4runtimecodegen_p.h +++ b/src/qml/jsruntime/qv4runtimecodegen_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4RUNTIMECODEGEN_P_H #define QV4RUNTIMECODEGEN_P_H @@ -69,8 +33,8 @@ public: QQmlJS::AST::FunctionExpression *ast, Compiler::Module *module); - void throwSyntaxError(const QQmlJS::AST::SourceLocation &loc, const QString &detail) override; - void throwReferenceError(const QQmlJS::AST::SourceLocation &loc, const QString &detail) override; + void throwSyntaxError(const QQmlJS::SourceLocation &loc, const QString &detail) override; + void throwReferenceError(const QQmlJS::SourceLocation &loc, const QString &detail) override; private: ExecutionEngine *engine; diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index e4aceef3ee..ddd312b893 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4SCOPEDVALUE_P_H #define QV4SCOPEDVALUE_P_H @@ -68,9 +32,14 @@ namespace QV4 { struct ScopedValue; +inline bool hasExceptionOrIsInterrupted(ExecutionEngine *engine) +{ + return engine->hasException || engine->isInterrupted.loadRelaxed(); +} + #define CHECK_EXCEPTION() \ do { \ - if (scope.hasException()) { \ + if (hasExceptionOrIsInterrupted(scope.engine)) { \ return QV4::Encode::undefined(); \ } \ } while (false) @@ -124,6 +93,10 @@ struct Scope { /* Be careful when using Uninitialized, the stack has to be fully initialized before calling into the memory manager again */ Uninitialized }; + + template <AllocMode mode = Undefined> + Value *alloc(qint64 nValues) const = delete; // use safeForAllocLength + template <AllocMode mode = Undefined> QML_NEARLY_ALWAYS_INLINE Value *alloc(int nValues) const { @@ -172,6 +145,9 @@ private: struct ScopedValue { + ScopedValue(const ScopedValue &) = default; + ScopedValue(ScopedValue &&) = default; + ScopedValue(const Scope &scope) { ptr = scope.alloc<Scope::Uninitialized>(); @@ -381,11 +357,6 @@ struct Scoped return *this; } - Scoped<T> &operator=(const Scoped<T> &other) { - *ptr = *other.ptr; - return *this; - } - Scoped<T> &operator=(T *t) { setPointer(t); return *this; @@ -446,7 +417,7 @@ struct ScopedProperty { ScopedProperty(Scope &scope) { - property = reinterpret_cast<Property*>(scope.alloc(sizeof(Property) / sizeof(Value))); + property = reinterpret_cast<Property*>(scope.alloc(int(sizeof(Property) / sizeof(Value)))); } Property *operator->() { return property; } diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 2fab9e4b7b..894426ce3b 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -1,57 +1,19 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4script_p.h" #include <private/qv4mm_p.h> -#include "qv4functionobject_p.h" #include "qv4function_p.h" #include "qv4context_p.h" #include "qv4debugging_p.h" -#include "qv4profiling_p.h" #include "qv4scopedvalue_p.h" -#include "qv4jscall_p.h" #include <private/qqmljsengine_p.h> #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> #include <private/qqmlengine_p.h> +#include <private/qqmlsourcecoordinate_p.h> #include <private/qv4profiling_p.h> #include <qv4runtimecodegen_p.h> @@ -64,14 +26,15 @@ using namespace QQmlJS; Script::Script(ExecutionEngine *v4, QmlContext *qml, const QQmlRefPointer<ExecutableCompilationUnit> &compilationUnit) : line(1), column(0), context(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false) - , compilationUnit(compilationUnit), vmFunction(nullptr), parseAsBinding(true) + , compilationUnit(compilationUnit), parseAsBinding(true) { if (qml) qmlContext.set(v4, *qml); parsed = true; - vmFunction = compilationUnit ? compilationUnit->linkToEngine(v4) : nullptr; + vmFunction.set(v4, + compilationUnit ? compilationUnit->rootFunction() : nullptr); } Script::~Script() @@ -83,19 +46,17 @@ void Script::parse() if (parsed) return; - using namespace QV4::Compiler; - parsed = true; ExecutionEngine *v4 = context->engine(); Scope valueScope(v4); - Module module(v4->debugger() != nullptr); + QV4::Compiler::Module module(v4->debugger() != nullptr); if (sourceCode.startsWith(QLatin1String("function("))) { static const int snippetLength = 70; qWarning() << "Warning: Using function expressions as statements in scripts is not compliant with the ECMAScript specification:\n" - << (sourceCode.leftRef(snippetLength) + QLatin1String("...")) + << (QStringView{sourceCode}.left(snippetLength) + QLatin1String("...")) << "\nThis will throw a syntax error in Qt 5.12. If you want a function expression, surround it by parentheses."; } @@ -109,10 +70,10 @@ void Script::parse() const auto diagnosticMessages = parser.diagnosticMessages(); for (const DiagnosticMessage &m : diagnosticMessages) { if (m.isError()) { - valueScope.engine->throwSyntaxError(m.message, sourceFile, m.line, m.column); + valueScope.engine->throwSyntaxError(m.message, sourceFile, m.loc.startLine, m.loc.startColumn); return; } else { - qWarning() << sourceFile << ':' << m.line << ':' << m.column + qWarning() << sourceFile << ':' << m.loc.startLine << ':' << m.loc.startColumn << ": warning: " << m.message; } } @@ -134,8 +95,8 @@ void Script::parse() if (v4->hasException) return; - compilationUnit = QV4::ExecutableCompilationUnit::create(cg.generateCompilationUnit()); - vmFunction = compilationUnit->linkToEngine(v4); + compilationUnit = v4->insertCompilationUnit(cg.generateCompilationUnit()); + vmFunction.set(v4, compilationUnit->rootFunction()); } if (!vmFunction) { @@ -173,7 +134,7 @@ Function *Script::function() return vmFunction; } -QV4::CompiledData::CompilationUnit Script::precompile( +QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile( QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator, const QString &fileName, const QString &finalUrl, const QString &source, QList<QQmlError> *reportedErrors, @@ -209,8 +170,8 @@ QV4::CompiledData::CompilationUnit Script::precompile( const auto v4Error = cg.error(); QQmlError error; error.setUrl(cg.url()); - error.setLine(v4Error.line); - error.setColumn(v4Error.column); + error.setLine(qmlConvertSourceCoordinate<quint32, int>(v4Error.loc.startLine)); + error.setColumn(qmlConvertSourceCoordinate<quint32, int>(v4Error.loc.startColumn)); error.setDescription(v4Error.message); reportedErrors->append(error); } @@ -226,10 +187,20 @@ Script *Script::createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlCo error->clear(); QQmlMetaType::CachedUnitLookupError cacheError = QQmlMetaType::CachedUnitLookupError::NoError; - if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(originalUrl, &cacheError)) { + const ExecutionEngine::DiskCacheOptions options = engine->diskCacheOptions(); + if (const QQmlPrivate::CachedQmlUnit *cachedUnit + = (options & ExecutionEngine::DiskCache::Aot) + ? QQmlMetaType::findCachedCompilationUnit( + originalUrl, + (options & ExecutionEngine::DiskCache::AotByteCode) + ? QQmlMetaType::AcceptUntyped + : QQmlMetaType::RequireFullyTyped, + &cacheError) + : nullptr) { QQmlRefPointer<QV4::ExecutableCompilationUnit> jsUnit - = QV4::ExecutableCompilationUnit::create( - QV4::CompiledData::CompilationUnit(cachedUnit)); + = engine->insertCompilationUnit( + QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>( + cachedUnit->qmlData, cachedUnit->aotCompiledFunctions)); return new QV4::Script(engine, qmlContext, jsUnit); } diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index aecedea701..7b3dcae486 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4SCRIPT_H #define QV4SCRIPT_H @@ -72,11 +36,11 @@ struct Q_QML_EXPORT Script { Script(ExecutionContext *scope, QV4::Compiler::ContextType mode, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) : sourceFile(source), line(line), column(column), sourceCode(sourceCode) , context(scope), strictMode(false), inheritContext(false), parsed(false), contextType(mode) - , vmFunction(nullptr), parseAsBinding(false) {} + , parseAsBinding(false) {} Script(ExecutionEngine *engine, QmlContext *qml, bool parseAsBinding, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) : sourceFile(source), line(line), column(column), sourceCode(sourceCode) , context(engine->rootContext()), strictMode(false), inheritContext(true), parsed(false) - , vmFunction(nullptr), parseAsBinding(parseAsBinding) { + , parseAsBinding(parseAsBinding) { if (qml) qmlContext.set(engine, *qml); } @@ -93,7 +57,7 @@ struct Q_QML_EXPORT Script { QV4::Compiler::ContextType contextType = QV4::Compiler::ContextType::Eval; QV4::PersistentValue qmlContext; QQmlRefPointer<ExecutableCompilationUnit> compilationUnit; - Function *vmFunction; + QV4::WriteBarrier::Pointer<Function> vmFunction; bool parseAsBinding; void parse(); @@ -101,7 +65,7 @@ struct Q_QML_EXPORT Script { Function *function(); - static QV4::CompiledData::CompilationUnit precompile( + static QQmlRefPointer<QV4::CompiledData::CompilationUnit> precompile( QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator, const QString &fileName, const QString &finalUrl, const QString &source, diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 77a98247ac..8de85a86bf 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -1,43 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtQml/qqml.h> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QtCore/qsequentialiterable.h> #include "qv4sequenceobject_p.h" @@ -46,19 +10,37 @@ #include <private/qqmlengine_p.h> #include <private/qv4scopedvalue_p.h> #include <private/qv4jscall_p.h> -#include "qv4runtime_p.h" -#include "qv4objectiterator_p.h" +#include <private/qqmlmetatype_p.h> +#include <private/qqmltype_p_p.h> #include <private/qqmlvaluetypewrapper_p.h> -#if QT_CONFIG(qml_itemmodel) -#include <private/qqmlmodelindexvaluetype_p.h> -#include <QtCore/qabstractitemmodel.h> -#endif #include <algorithm> QT_BEGIN_NAMESPACE -using namespace QV4; +namespace QV4 { + +DEFINE_OBJECT_VTABLE(Sequence); + +static ReturnedValue doGetIndexed(const Sequence *s, qsizetype index) { + QV4::Scope scope(s->engine()); + + Heap::ReferenceObject::Flags flags = + Heap::ReferenceObject::EnforcesLocation; + if (s->d()->metaSequence().canSetValueAtIndex()) + flags |= Heap::ReferenceObject::CanWriteBack; + if (s->d()->valueMetaType() == QMetaType::fromType<QVariant>()) + flags |= Heap::ReferenceObject::IsVariant; + + QV4::ScopedValue v(scope, scope.engine->fromVariant( + s->at(index), s->d(), index, flags)); + if (QQmlValueTypeWrapper *ref = v->as<QQmlValueTypeWrapper>()) { + if (CppStackFrame *frame = scope.engine->currentStackFrame) + ref->d()->setLocation(frame->v4Function, frame->statementNumber()); + // No need to read the reference. at() has done that already. + } + return v->asReturnedValue(); +} // helper function to generate valid warnings if errors occur during sequence operations. static void generateWarning(QV4::ExecutionEngine *v4, const QString& description) @@ -76,773 +58,667 @@ static void generateWarning(QV4::ExecutionEngine *v4, const QString& description QQmlEnginePrivate::warning(engine, retn); } -// F(elementType, elementTypeName, sequenceType, defaultValue) -#if QT_CONFIG(qml_itemmodel) -#define FOREACH_QML_SEQUENCE_TYPE_FOR_ITEMMODEL(F) \ - F(QModelIndex, QModelIndex, QModelIndexList, QModelIndex()) \ - F(QModelIndex, QModelIndexVector, QVector<QModelIndex>, QModelIndex()) \ - F(QModelIndex, QModelIndexStdVector, std::vector<QModelIndex>, QModelIndex()) \ - F(QItemSelectionRange, QItemSelectionRange, QItemSelection, QItemSelectionRange()) -#else -#define FOREACH_QML_SEQUENCE_TYPE_FOR_ITEMMODEL(F) -#endif +struct SequenceOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator +{ + ~SequenceOwnPropertyKeyIterator() override = default; + PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override + { + const Sequence *s = static_cast<const Sequence *>(o); + + if (s->d()->isReference() && !s->loadReference()) + return PropertyKey::invalid(); + + const qsizetype size = s->size(); + if (size > 0 && qIsAtMostSizetypeLimit(arrayIndex, size - 1)) { + const uint index = arrayIndex; + ++arrayIndex; + if (attrs) + *attrs = QV4::Attr_Data; + if (pd) + pd->value = doGetIndexed(s, index); + return PropertyKey::fromArrayIndex(index); + } -#define FOREACH_QML_SEQUENCE_TYPE(F) \ - F(int, IntVector, QVector<int>, 0) \ - F(qreal, RealVector, QVector<qreal>, 0.0) \ - F(bool, BoolVector, QVector<bool>, false) \ - F(int, IntStdVector, std::vector<int>, 0) \ - F(qreal, RealStdVector, std::vector<qreal>, 0.0) \ - F(bool, BoolStdVector, std::vector<bool>, false) \ - 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(QString, StringVector, QVector<QString>, QString()) \ - F(QString, StringStdVector, std::vector<QString>, QString()) \ - F(QUrl, Url, QList<QUrl>, QUrl()) \ - F(QUrl, UrlVector, QVector<QUrl>, QUrl()) \ - F(QUrl, UrlStdVector, std::vector<QUrl>, QUrl()) \ - FOREACH_QML_SEQUENCE_TYPE_FOR_ITEMMODEL(F) + if (memberIndex == 0) { + ++memberIndex; + return o->engine()->id_length()->propertyKey(); + } -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QString &element) -{ - return engine->newString(element)->asReturnedValue(); -} + // You cannot add any own properties via the regular JavaScript interfaces. + return PropertyKey::invalid(); + } +}; -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *, int element) +void Heap::Sequence::initTypes(QMetaType listType, QMetaSequence metaSequence) { - return QV4::Encode(element); + m_listType = listType.iface(); + Q_ASSERT(m_listType); + m_metaSequence = metaSequence.iface(); + Q_ASSERT(m_metaSequence); + QV4::Scope scope(internalClass->engine); + QV4::Scoped<QV4::Sequence> o(scope, this); + o->setArrayType(Heap::ArrayData::Custom); } -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QUrl &element) +void Heap::Sequence::init(QMetaType listType, QMetaSequence metaSequence, const void *container) { - return engine->newString(element.toString())->asReturnedValue(); + ReferenceObject::init(nullptr, -1, NoFlag); + initTypes(listType, metaSequence); + m_container = listType.create(container); } -#if QT_CONFIG(qml_itemmodel) -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QModelIndex &element) +void Heap::Sequence::init( + QMetaType listType, QMetaSequence metaSequence, const void *container, + Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags) { - const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(QMetaType::QModelIndex); - return QV4::QQmlValueTypeWrapper::create(engine, QVariant(element), vtmo, QMetaType::QModelIndex); -} + ReferenceObject::init(object, propertyIndex, flags); + initTypes(listType, metaSequence); -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QItemSelectionRange &element) -{ - int metaTypeId = qMetaTypeId<QItemSelectionRange>(); - const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(metaTypeId); - return QV4::QQmlValueTypeWrapper::create(engine, QVariant::fromValue(element), vtmo, metaTypeId); + if (CppStackFrame *frame = internalClass->engine->currentStackFrame) + setLocation(frame->v4Function, frame->statementNumber()); + if (container) + m_container = listType.create(container); + else if (flags & EnforcesLocation) + QV4::ReferenceObject::readReference(this); } -#endif -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *, qreal element) +Heap::Sequence *Heap::Sequence::detached() const { - return QV4::Encode(element); + return internalClass->engine->memoryManager->allocate<QV4::Sequence>( + QMetaType(m_listType), QMetaSequence(m_metaSequence), m_container); } -static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *, bool element) +void Heap::Sequence::destroy() { - return QV4::Encode(element); + if (m_container) + listType().destroy(m_container); + ReferenceObject::destroy(); } -static QString convertElementToString(const QString &element) +void *Heap::Sequence::storagePointer() { - return element; + if (!m_container) + m_container = listType().create(); + return m_container; } -static QString convertElementToString(int element) +bool Heap::Sequence::setVariant(const QVariant &variant) { - return QString::number(element); + const QMetaType variantReferenceType = variant.metaType(); + if (variantReferenceType != listType()) { + // This is a stale reference. That is, the property has been + // overwritten with a different type in the meantime. + // We need to modify this reference to the updated type, if + // possible, or return false if it is not a sequence. + const QQmlType newType = QQmlMetaType::qmlListType(variantReferenceType); + if (newType.isSequentialContainer()) { + if (m_container) + listType().destroy(m_container); + m_listType = newType.qListTypeId().iface(); + m_metaSequence = newType.listMetaSequence().iface(); + m_container = listType().create(variant.constData()); + return true; + } else { + return false; + } + } + if (m_container) { + variantReferenceType.destruct(m_container); + variantReferenceType.construct(m_container, variant.constData()); + } else { + m_container = variantReferenceType.create(variant.constData()); + } + return true; } - -static QString convertElementToString(const QUrl &element) +QVariant Heap::Sequence::toVariant() const { - return element.toString(); + return QVariant(listType(), m_container); } -#if QT_CONFIG(qml_itemmodel) -static QString convertElementToString(const QModelIndex &element) +qsizetype Sequence::size() const { - return reinterpret_cast<const QQmlModelIndexValueType *>(&element)->toString(); + const auto *p = d(); + Q_ASSERT(p->storagePointer()); // Must readReference() before + return p->metaSequence().size(p->storagePointer()); } -static QString convertElementToString(const QItemSelectionRange &element) +QVariant Sequence::at(qsizetype index) const { - return reinterpret_cast<const QQmlItemSelectionRangeValueType *>(&element)->toString(); + const auto *p = d(); + Q_ASSERT(p->storagePointer()); // Must readReference() before + const QMetaType v = p->valueMetaType(); + QVariant result; + if (v == QMetaType::fromType<QVariant>()) { + p->metaSequence().valueAtIndex(p->storagePointer(), index, &result); + } else { + result = QVariant(v); + p->metaSequence().valueAtIndex(p->storagePointer(), index, result.data()); + } + return result; } -#endif -static QString convertElementToString(qreal element) +QVariant Sequence::shift() { - QString qstr; - RuntimeHelpers::numberToString(&qstr, element, 10); - return qstr; -} + auto *p = d(); + void *storage = p->storagePointer(); + Q_ASSERT(storage); // Must readReference() before + const QMetaType v = p->valueMetaType(); + const QMetaSequence m = p->metaSequence(); -static QString convertElementToString(bool element) -{ - if (element) - return QStringLiteral("true"); - else - return QStringLiteral("false"); -} + const auto variantData = [&](QVariant *variant) -> void *{ + if (v == QMetaType::fromType<QVariant>()) + return variant; -template <typename ElementType> ElementType convertValueToElement(const Value &value); + *variant = QVariant(v); + return variant->data(); + }; -template <> QString convertValueToElement(const Value &value) -{ - return value.toQString(); -} + QVariant result; + void *resultData = variantData(&result); + m.valueAtIndex(storage, 0, resultData); -template <> int convertValueToElement(const Value &value) -{ - return value.toInt32(); -} + if (m.canRemoveValueAtBegin()) { + m.removeValueAtBegin(storage); + return result; + } -template <> QUrl convertValueToElement(const Value &value) -{ - return QUrl(value.toQString()); + QVariant t; + void *tData = variantData(&t); + for (qsizetype i = 1, end = m.size(storage); i < end; ++i) { + m.valueAtIndex(storage, i, tData); + m.setValueAtIndex(storage, i - 1, tData); + } + m.removeValueAtEnd(storage); + + return result; } -#if QT_CONFIG(qml_itemmodel) -template <> QModelIndex convertValueToElement(const Value &value) + +template<typename Action> +void convertAndDo(const QVariant &item, const QMetaType v, Action action) { - const QQmlValueTypeWrapper *v = value.as<QQmlValueTypeWrapper>(); - if (v) - return v->toVariant().toModelIndex(); - return QModelIndex(); + if (item.metaType() == v) { + action(item.constData()); + } else if (v == QMetaType::fromType<QVariant>()) { + action(&item); + } else { + QVariant converted = item; + if (!converted.convert(v)) + converted = QVariant(v); + action(converted.constData()); + } } -template <> QItemSelectionRange convertValueToElement(const Value &value) +void Sequence::append(const QVariant &item) { - const QQmlValueTypeWrapper *v = value.as<QQmlValueTypeWrapper>(); - if (v) - return v->toVariant().value<QItemSelectionRange>(); - return QItemSelectionRange(); + Heap::Sequence *p = d(); + convertAndDo(item, p->valueMetaType(), [p](const void *data) { + p->metaSequence().addValueAtEnd(p->storagePointer(), data); + }); } -#endif -template <> qreal convertValueToElement(const Value &value) +void Sequence::append(qsizetype num, const QVariant &item) { - return value.toNumber(); + Heap::Sequence *p = d(); + convertAndDo(item, p->valueMetaType(), [p, num](const void *data) { + const QMetaSequence m = p->metaSequence(); + void *container = p->storagePointer(); + for (qsizetype i = 0; i < num; ++i) + m.addValueAtEnd(container, data); + }); } -template <> bool convertValueToElement(const Value &value) +void Sequence::replace(qsizetype index, const QVariant &item) { - return value.toBoolean(); + Heap::Sequence *p = d(); + convertAndDo(item, p->valueMetaType(), [p, index](const void *data) { + p->metaSequence().setValueAtIndex(p->storagePointer(), index, data); + }); } -namespace QV4 { - -template <typename Container> struct QQmlSequence; - -namespace Heap { +void Sequence::removeLast(qsizetype num) +{ + auto *p = d(); + const QMetaSequence m = p->metaSequence(); -template <typename Container> -struct QQmlSequence : Object { - void init(const Container &container); - void init(QObject *object, int propertyIndex, bool readOnly); - void destroy() { - delete container; - object.destroy(); - Object::destroy(); + if (m.canEraseRangeAtIterator() && m.hasRandomAccessIterator() && num > 1) { + void *i = m.end(p->storagePointer()); + m.advanceIterator(i, -num); + void *j = m.end(p->storagePointer()); + m.eraseRangeAtIterator(p->storagePointer(), i, j); + m.destroyIterator(i); + m.destroyIterator(j); + } else { + for (int i = 0; i < num; ++i) + m.removeValueAtEnd(p->storagePointer()); } - - mutable Container *container; - QQmlQPointer<QObject> object; - int propertyIndex; - bool isReference : 1; - bool isReadOnly : 1; -}; - } -template <typename Container> -struct QQmlSequence : public QV4::Object +ReturnedValue Sequence::containerGetIndexed(qsizetype index, bool *hasProperty) const { - V4_OBJECT2(QQmlSequence<Container>, QV4::Object) - Q_MANAGED_TYPE(QmlSequence) - V4_PROTOTYPE(sequencePrototype) - V4_NEEDS_DESTROY -public: - - void init() - { - defineAccessorProperty(QStringLiteral("length"), method_get_length, method_set_length); - } + if (d()->isReference() && !loadReference()) + return Encode::undefined(); - QV4::ReturnedValue containerGetIndexed(uint index, bool *hasProperty) const - { - /* Qt containers have int (rather than uint) allowable indexes. */ - if (index > INT_MAX) { - generateWarning(engine(), QLatin1String("Index out of range during indexed get")); - if (hasProperty) - *hasProperty = false; - return Encode::undefined(); - } - if (d()->isReference) { - if (!d()->object) { - if (hasProperty) - *hasProperty = false; - return Encode::undefined(); - } - loadReference(); - } - if (index < size_t(d()->container->size())) { - if (hasProperty) - *hasProperty = true; - return convertElementToValue(engine(), qAsConst(*(d()->container))[index]); - } + if (index >= 0 && index < size()) { if (hasProperty) - *hasProperty = false; - return Encode::undefined(); + *hasProperty = true; + return doGetIndexed(this, index); } + if (hasProperty) + *hasProperty = false; + return Encode::undefined(); +} - bool containerPutIndexed(uint index, const QV4::Value &value) - { - if (internalClass()->engine->hasException) - return false; +bool Sequence::containerPutIndexed(qsizetype index, const Value &value) +{ + if (internalClass()->engine->hasException) + return false; - /* Qt containers have int (rather than uint) allowable indexes. */ - if (index > INT_MAX) { - generateWarning(engine(), QLatin1String("Index out of range during indexed set")); - return false; - } + if (d()->isReadOnly()) { + engine()->throwTypeError(QLatin1String("Cannot insert into a readonly container")); + return false; + } - if (d()->isReadOnly) - return false; + if (d()->isReference() && !loadReference()) + return false; - if (d()->isReference) { - if (!d()->object) - return false; - loadReference(); - } + const qsizetype count = size(); + const QMetaType valueType = d()->valueMetaType(); + const QVariant element = ExecutionEngine::toVariant(value, valueType, false); - size_t count = size_t(d()->container->size()); + if (index < 0) + return false; - typename Container::value_type element = convertValueToElement<typename Container::value_type>(value); + if (index == count) { + append(element); + } else if (index < count) { + replace(index, element); + } else { + /* according to ECMA262r3 we need to insert */ + /* the value at the given index, increasing length to index+1. */ + append(index - count, + valueType == QMetaType::fromType<QVariant>() ? QVariant() : QVariant(valueType)); + append(element); + } - if (index == count) { - d()->container->push_back(element); - } else if (index < count) { - (*d()->container)[index] = element; - } else { - /* according to ECMA262r3 we need to insert */ - /* the value at the given index, increasing length to index+1. */ - d()->container->reserve(index + 1); - while (index > count++) { - d()->container->push_back(typename Container::value_type()); - } - d()->container->push_back(element); - } + if (d()->object()) + storeReference(); + return true; +} - if (d()->isReference) - storeReference(); - return true; - } +SequenceOwnPropertyKeyIterator *containerOwnPropertyKeys(const Object *m, Value *target) +{ + *target = *m; + return new SequenceOwnPropertyKeyIterator; +} - QV4::PropertyAttributes containerQueryIndexed(uint index) const - { - /* Qt containers have int (rather than uint) allowable indexes. */ - if (index > INT_MAX) { - generateWarning(engine(), QLatin1String("Index out of range during indexed query")); - return QV4::Attr_Invalid; - } - if (d()->isReference) { - if (!d()->object) - return QV4::Attr_Invalid; - loadReference(); - } - return (index < size_t(d()->container->size())) ? QV4::Attr_Data : QV4::Attr_Invalid; - } +bool Sequence::containerDeleteIndexedProperty(qsizetype index) +{ + if (d()->isReadOnly()) + return false; + if (d()->isReference() && !loadReference()) + return false; + if (index < 0 || index >= size()) + return false; - struct OwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator - { - ~OwnPropertyKeyIterator() override = default; - PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override - { - const QQmlSequence *s = static_cast<const QQmlSequence *>(o); - - if (s->d()->isReference) { - if (!s->d()->object) - return ObjectOwnPropertyKeyIterator::next(o, pd, attrs); - s->loadReference(); - } + /* according to ECMA262r3 it should be Undefined, */ + /* but we cannot, so we insert a default-value instead. */ + replace(index, QVariant()); - if (arrayIndex < static_cast<uint>(s->d()->container->size())) { - uint index = arrayIndex; - ++arrayIndex; - if (attrs) - *attrs = QV4::Attr_Data; - if (pd) - pd->value = convertElementToValue(s->engine(), s->d()->container->at(index)); - return PropertyKey::fromArrayIndex(index); - } + if (d()->object()) + storeReference(); - return ObjectOwnPropertyKeyIterator::next(o, pd, attrs); - } - }; + return true; +} - static OwnPropertyKeyIterator *containerOwnPropertyKeys(const Object *m, Value *target) - { - *target = *m; - return new OwnPropertyKeyIterator; +bool Sequence::containerIsEqualTo(Managed *other) +{ + if (!other) + return false; + Sequence *otherSequence = other->as<Sequence>(); + if (!otherSequence) + return false; + if (d()->object() && otherSequence->d()->object()) { + return d()->object() == otherSequence->d()->object() + && d()->property() == otherSequence->d()->property(); + } else if (!d()->object() && !otherSequence->d()->object()) { + return this == otherSequence; } + return false; +} - bool containerDeleteIndexedProperty(uint index) - { - /* Qt containers have int (rather than uint) allowable indexes. */ - if (index > INT_MAX) - return false; - if (d()->isReadOnly) - return false; - if (d()->isReference) { - if (!d()->object) - return false; - loadReference(); - } - - if (index >= size_t(d()->container->size())) - return false; +void *Sequence::getRawContainerPtr() const +{ return d()->storagePointer(); } - /* according to ECMA262r3 it should be Undefined, */ - /* but we cannot, so we insert a default-value instead. */ - (*d()->container)[index] = typename Container::value_type(); +bool Sequence::loadReference() const +{ + Q_ASSERT(d()->object()); + // If locations are enforced we only read once + return d()->enforcesLocation() || QV4::ReferenceObject::readReference(d()); +} - if (d()->isReference) - storeReference(); +bool Sequence::storeReference() +{ + Q_ASSERT(d()->object()); + return d()->isAttachedToProperty() && QV4::ReferenceObject::writeBack(d()); +} - return true; - } +ReturnedValue Sequence::virtualGet(const Managed *that, PropertyKey id, const Value *receiver, bool *hasProperty) +{ + if (id.isArrayIndex()) { + const uint index = id.asArrayIndex(); + if (qIsAtMostSizetypeLimit(index)) + return static_cast<const Sequence *>(that)->containerGetIndexed(qsizetype(index), hasProperty); - bool containerIsEqualTo(Managed *other) - { - if (!other) - return false; - QQmlSequence<Container> *otherSequence = other->as<QQmlSequence<Container> >(); - if (!otherSequence) - return false; - if (d()->isReference && otherSequence->d()->isReference) { - return d()->object == otherSequence->d()->object && d()->propertyIndex == otherSequence->d()->propertyIndex; - } else if (!d()->isReference && !otherSequence->d()->isReference) { - return this == otherSequence; - } + generateWarning(that->engine(), QLatin1String("Index out of range during indexed get")); 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::ExecutionEngine *v4, const QV4::Value &compareFn) - : m_v4(v4), m_compareFn(&compareFn) - {} - - bool operator()(typename Container::value_type lhs, typename Container::value_type rhs) - { - QV4::Scope scope(m_v4); - ScopedFunctionObject compare(scope, m_compareFn); - if (!compare) - return m_v4->throwTypeError(); - Value *argv = scope.alloc(2); - argv[0] = convertElementToValue(m_v4, lhs); - argv[1] = convertElementToValue(m_v4, rhs); - QV4::ScopedValue result(scope, compare->call(m_v4->globalObject, argv, 2)); - return result->toNumber() < 0; - } + return Object::virtualGet(that, id, receiver, hasProperty); +} - private: - QV4::ExecutionEngine *m_v4; - const QV4::Value *m_compareFn; - }; +qint64 Sequence::virtualGetLength(const Managed *m) +{ + const Sequence *s = static_cast<const Sequence *>(m); + if (s->d()->isReference() && !s->loadReference()) + return 0; + return s->size(); +} - bool sort(const FunctionObject *f, const Value *, const Value *argv, int argc) - { - if (d()->isReadOnly) - return false; - if (d()->isReference) { - if (!d()->object) - return false; - loadReference(); - } +bool Sequence::virtualPut(Managed *that, PropertyKey id, const Value &value, Value *receiver) +{ + if (id.isArrayIndex()) { + const uint index = id.asArrayIndex(); + if (qIsAtMostSizetypeLimit(index)) + return static_cast<Sequence *>(that)->containerPutIndexed(qsizetype(index), value); - if (argc == 1 && argv[0].as<FunctionObject>()) { - CompareFunctor cf(f->engine(), argv[0]); - std::sort(d()->container->begin(), d()->container->end(), cf); - } else { - DefaultCompareFunctor cf; - std::sort(d()->container->begin(), d()->container->end(), cf); - } + generateWarning(that->engine(), QLatin1String("Index out of range during indexed set")); + return false; + } + return Object::virtualPut(that, id, value, receiver); +} - if (d()->isReference) - storeReference(); +bool Sequence::virtualDeleteProperty(Managed *that, PropertyKey id) +{ + if (id.isArrayIndex()) { + const uint index = id.asArrayIndex(); + if (qIsAtMostSizetypeLimit(index)) + return static_cast<Sequence *>(that)->containerDeleteIndexedProperty(qsizetype(index)); - return true; + generateWarning(that->engine(), QLatin1String("Index out of range during indexed delete")); + return false; } + return Object::virtualDeleteProperty(that, id); +} - static QV4::ReturnedValue method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int) - { - QV4::Scope scope(b); - QV4::Scoped<QQmlSequence<Container>> This(scope, thisObject->as<QQmlSequence<Container> >()); - if (!This) - THROW_TYPE_ERROR(); - - if (This->d()->isReference) { - if (!This->d()->object) - RETURN_RESULT(Encode(0)); - This->loadReference(); - } - RETURN_RESULT(Encode(qint32(This->d()->container->size()))); - } +bool Sequence::virtualIsEqualTo(Managed *that, Managed *other) +{ + return static_cast<Sequence *>(that)->containerIsEqualTo(other); +} - static QV4::ReturnedValue method_set_length(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) - { - QV4::Scope scope(f); - QV4::Scoped<QQmlSequence<Container>> This(scope, thisObject->as<QQmlSequence<Container> >()); - if (!This) - THROW_TYPE_ERROR(); - - quint32 newLength = argc ? argv[0].toUInt32() : 0; - /* Qt containers have int (rather than uint) allowable indexes. */ - if (newLength > INT_MAX) { - generateWarning(scope.engine, QLatin1String("Index out of range during length set")); - RETURN_UNDEFINED(); - } +OwnPropertyKeyIterator *Sequence::virtualOwnPropertyKeys(const Object *m, Value *target) +{ + return containerOwnPropertyKeys(m, target); +} - if (This->d()->isReadOnly) - THROW_TYPE_ERROR(); +int Sequence::virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a) +{ + Sequence *sequence = static_cast<Sequence *>(object); + Q_ASSERT(sequence); - /* Read the sequence from the QObject property if we're a reference */ - if (This->d()->isReference) { - if (!This->d()->object) - RETURN_UNDEFINED(); - This->loadReference(); - } - /* Determine whether we need to modify the sequence */ - quint32 newCount = static_cast<quint32>(newLength); - quint32 count = static_cast<quint32>(This->d()->container->size()); - if (newCount == count) { - RETURN_UNDEFINED(); - } 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->d()->container->reserve(newCount); - while (newCount > count++) { - This->d()->container->push_back(typename Container::value_type()); - } - } else { - /* according to ECMA262r3 we need to remove */ - /* elements until the sequence is the required length. */ - if (newCount < count) { - This->d()->container->erase(This->d()->container->begin() + newCount, This->d()->container->end()); - } - } - /* write back if required. */ - if (This->d()->isReference) { - /* write back. already checked that object is non-null, so skip that check here. */ - This->storeReference(); - } - RETURN_UNDEFINED(); + switch (call) { + case QMetaObject::ReadProperty: { + const QMetaType valueType = sequence->d()->valueMetaType(); + if (sequence->d()->isReference() && !sequence->loadReference()) + return 0; + const QMetaSequence metaSequence = sequence->d()->metaSequence(); + if (metaSequence.valueMetaType() != valueType) + return 0; // value metatype is not what the caller expects anymore. + + const void *storagePointer = sequence->d()->storagePointer(); + if (index < 0 || index >= metaSequence.size(storagePointer)) + return 0; + metaSequence.valueAtIndex(storagePointer, index, a[0]); + break; + } + case QMetaObject::WriteProperty: { + void *storagePointer = sequence->d()->storagePointer(); + const QMetaSequence metaSequence = sequence->d()->metaSequence(); + if (index < 0 || index >= metaSequence.size(storagePointer)) + return 0; + metaSequence.setValueAtIndex(storagePointer, index, a[0]); + if (sequence->d()->isReference()) + sequence->storeReference(); + break; + } + default: + return 0; // not supported } - QVariant toVariant() const - { return QVariant::fromValue<Container>(*d()->container); } + return -1; +} - static QVariant toVariant(QV4::ArrayObject *array) - { - QV4::Scope scope(array->engine()); - Container result; - quint32 length = array->getLength(); - QV4::ScopedValue v(scope); - for (quint32 i = 0; i < length; ++i) - result.push_back(convertValueToElement<typename Container::value_type>((v = array->get(i)))); - return QVariant::fromValue(result); - } +static QV4::ReturnedValue method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + QV4::Scope scope(b); + QV4::Scoped<Sequence> This(scope, thisObject->as<Sequence>()); + if (!This) + THROW_TYPE_ERROR(); - void* getRawContainerPtr() const - { return d()->container; } + if (This->d()->isReference() && !This->loadReference()) + return Encode::undefined(); - void loadReference() const - { - Q_ASSERT(d()->object); - Q_ASSERT(d()->isReference); - void *a[] = { d()->container, nullptr }; - QMetaObject::metacall(d()->object, QMetaObject::ReadProperty, d()->propertyIndex, a); - } + const qsizetype size = This->size(); + if (qIsAtMostUintLimit(size)) + RETURN_RESULT(Encode(uint(size))); - void storeReference() - { - Q_ASSERT(d()->object); - Q_ASSERT(d()->isReference); - int status = -1; - QQmlPropertyData::WriteFlags flags = QQmlPropertyData::DontRemoveBinding; - void *a[] = { d()->container, nullptr, &status, &flags }; - QMetaObject::metacall(d()->object, QMetaObject::WriteProperty, d()->propertyIndex, a); - } + return scope.engine->throwRangeError(QLatin1String("Sequence length out of range")); +} - static QV4::ReturnedValue virtualGet(const QV4::Managed *that, PropertyKey id, const Value *receiver, bool *hasProperty) - { - if (!id.isArrayIndex()) - return Object::virtualGet(that, id, receiver, hasProperty); - return static_cast<const QQmlSequence<Container> *>(that)->containerGetIndexed(id.asArrayIndex(), hasProperty); - } - static bool virtualPut(Managed *that, PropertyKey id, const QV4::Value &value, Value *receiver) - { - if (id.isArrayIndex()) - return static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(id.asArrayIndex(), value); - return Object::virtualPut(that, id, value, receiver); - } - static QV4::PropertyAttributes queryIndexed(const QV4::Managed *that, uint index) - { return static_cast<const QQmlSequence<Container> *>(that)->containerQueryIndexed(index); } - static bool virtualDeleteProperty(QV4::Managed *that, PropertyKey id) - { - if (id.isArrayIndex()) { - uint index = id.asArrayIndex(); - return static_cast<QQmlSequence<Container> *>(that)->containerDeleteIndexedProperty(index); - } - return Object::virtualDeleteProperty(that, id); +static QV4::ReturnedValue method_set_length(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + QV4::Scope scope(f); + QV4::Scoped<Sequence> This(scope, thisObject->as<Sequence>()); + if (!This) + THROW_TYPE_ERROR(); + + bool ok = false; + const quint32 argv0 = argc ? argv[0].asArrayLength(&ok) : 0; + if (!ok || !qIsAtMostSizetypeLimit(argv0)) { + generateWarning(scope.engine, QLatin1String("Index out of range during length set")); + RETURN_UNDEFINED(); } - static bool virtualIsEqualTo(Managed *that, Managed *other) - { return static_cast<QQmlSequence<Container> *>(that)->containerIsEqualTo(other); } - static QV4::OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target) - { return static_cast<const QQmlSequence<Container> *>(m)->containerOwnPropertyKeys(m, target);} -}; + if (This->d()->isReadOnly()) + THROW_TYPE_ERROR(); + const qsizetype newCount = qsizetype(argv0); -template <typename Container> -void Heap::QQmlSequence<Container>::init(const Container &container) -{ - Object::init(); - this->container = new Container(container); - propertyIndex = -1; - isReference = false; - isReadOnly = false; - object.init(); + /* Read the sequence from the QObject property if we're a reference */ + if (This->d()->isReference() && !This->loadReference()) + RETURN_UNDEFINED(); - QV4::Scope scope(internalClass->engine); - QV4::Scoped<QV4::QQmlSequence<Container> > o(scope, this); - o->setArrayType(Heap::ArrayData::Custom); - o->init(); -} + /* Determine whether we need to modify the sequence */ + const qsizetype count = This->size(); + if (newCount == count) { + RETURN_UNDEFINED(); + } else if (newCount > count) { + const QMetaType valueMetaType = This->d()->valueMetaType(); + /* according to ECMA262r3 we need to insert */ + /* undefined values increasing length to newLength. */ + /* We cannot, so we insert default-values instead. */ + This->append(newCount - count, QVariant(valueMetaType)); + } else { + /* according to ECMA262r3 we need to remove */ + /* elements until the sequence is the required length. */ + Q_ASSERT(newCount < count); + This->removeLast(count - newCount); + } -template <typename Container> -void Heap::QQmlSequence<Container>::init(QObject *object, int propertyIndex, bool readOnly) -{ - Object::init(); - this->container = new Container; - this->propertyIndex = propertyIndex; - isReference = true; - this->isReadOnly = readOnly; - this->object.init(object); - QV4::Scope scope(internalClass->engine); - QV4::Scoped<QV4::QQmlSequence<Container> > o(scope, this); - o->setArrayType(Heap::ArrayData::Custom); - o->loadReference(); - o->init(); -} + /* write back if required. */ + if (This->d()->object()) + This->storeReference(); + RETURN_UNDEFINED(); } -namespace QV4 { - -typedef QQmlSequence<QVector<int> > QQmlIntVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlIntVectorList); -typedef QQmlSequence<QVector<qreal> > QQmlRealVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlRealVectorList); -typedef QQmlSequence<QVector<bool> > QQmlBoolVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlBoolVectorList); -typedef QQmlSequence<std::vector<int> > QQmlIntStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlIntStdVectorList); -typedef QQmlSequence<std::vector<qreal> > QQmlRealStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlRealStdVectorList); -typedef QQmlSequence<std::vector<bool> > QQmlBoolStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlBoolStdVectorList); -typedef QQmlSequence<QStringList> QQmlQStringList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQStringList); -typedef QQmlSequence<QList<QString> > QQmlStringList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlStringList); -typedef QQmlSequence<QVector<QString> > QQmlStringVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlStringVectorList); -typedef QQmlSequence<std::vector<QString> > QQmlStringStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlStringStdVectorList); -typedef QQmlSequence<QList<int> > QQmlIntList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlIntList); -typedef QQmlSequence<QList<QUrl> > QQmlUrlList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlList); -typedef QQmlSequence<QVector<QUrl> > QQmlUrlVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlVectorList); -typedef QQmlSequence<std::vector<QUrl> > QQmlUrlStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlStdVectorList); -#if QT_CONFIG(qml_itemmodel) -typedef QQmlSequence<QModelIndexList> QQmlQModelIndexList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexList); -typedef QQmlSequence<QVector<QModelIndex> > QQmlQModelIndexVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexVectorList); -typedef QQmlSequence<std::vector<QModelIndex> > QQmlQModelIndexStdVectorList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexStdVectorList); -typedef QQmlSequence<QItemSelection> QQmlQItemSelectionRangeList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQItemSelectionRangeList); -#endif -typedef QQmlSequence<QList<bool> > QQmlBoolList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlBoolList); -typedef QQmlSequence<QList<qreal> > QQmlRealList; -DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlRealList); - -} - -#define REGISTER_QML_SEQUENCE_METATYPE(unused, unused2, SequenceType, unused3) qRegisterMetaType<SequenceType>(#SequenceType); void SequencePrototype::init() { - FOREACH_QML_SEQUENCE_TYPE(REGISTER_QML_SEQUENCE_METATYPE) - defineDefaultProperty(QStringLiteral("sort"), method_sort, 1); defineDefaultProperty(engine()->id_valueOf(), method_valueOf, 0); + defineAccessorProperty(QStringLiteral("length"), method_get_length, method_set_length); + defineDefaultProperty(QStringLiteral("shift"), method_shift, 0); } -#undef REGISTER_QML_SEQUENCE_METATYPE ReturnedValue SequencePrototype::method_valueOf(const FunctionObject *f, const Value *thisObject, const Value *, int) { return Encode(thisObject->toString(f->engine())); } -ReturnedValue SequencePrototype::method_sort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +ReturnedValue SequencePrototype::method_shift( + const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); - QV4::ScopedObject o(scope, thisObject); - if (!o || !o->isListType()) - THROW_TYPE_ERROR(); + Scoped<Sequence> s(scope, thisObject); + if (!s) + return ArrayPrototype::method_shift(b, thisObject, argv, argc); - if (argc >= 2) - return o.asReturnedValue(); - -#define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \ - if (QQml##SequenceElementTypeName##List *s = o->as<QQml##SequenceElementTypeName##List>()) { \ - if (!s->sort(b, thisObject, argv, argc)) \ - THROW_TYPE_ERROR(); \ - } else + if (s->d()->isReference() && !s->loadReference()) + RETURN_UNDEFINED(); - FOREACH_QML_SEQUENCE_TYPE(CALL_SORT) + const qsizetype len = s->size(); + if (!len) + RETURN_UNDEFINED(); -#undef CALL_SORT - {} - return o.asReturnedValue(); -} + ScopedValue result(scope, scope.engine->fromVariant(s->shift())); -#define IS_SEQUENCE(unused1, unused2, SequenceType, unused3) \ - if (sequenceTypeId == qMetaTypeId<SequenceType>()) { \ - return true; \ - } else + if (s->d()->object()) + s->storeReference(); -bool SequencePrototype::isSequenceType(int sequenceTypeId) -{ - FOREACH_QML_SEQUENCE_TYPE(IS_SEQUENCE) { /* else */ return false; } + return result->asReturnedValue(); } -#undef IS_SEQUENCE -#define NEW_REFERENCE_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ - if (sequenceType == qMetaTypeId<SequenceType>()) { \ - QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QQml##ElementTypeName##List>(object, propertyIndex, readOnly)); \ - return obj.asReturnedValue(); \ - } else - -ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int sequenceType, QObject *object, int propertyIndex, bool readOnly, bool *succeeded) +ReturnedValue SequencePrototype::newSequence( + QV4::ExecutionEngine *engine, QMetaType type, QMetaSequence metaSequence, const void *data, + Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags) { - QV4::Scope scope(engine); // This function is called when the property is a QObject Q_PROPERTY of - // the given sequence type. Internally we store a typed-sequence + // the given sequence type. Internally we store a 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::Encode::undefined(); } + + return engine->memoryManager->allocate<Sequence>( + type, metaSequence, data, object, propertyIndex, flags)->asReturnedValue(); } -#undef NEW_REFERENCE_SEQUENCE -#define NEW_COPY_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ - if (sequenceType == qMetaTypeId<SequenceType>()) { \ - QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QQml##ElementTypeName##List>(v.value<SequenceType >())); \ - return obj.asReturnedValue(); \ - } else +ReturnedValue SequencePrototype::fromVariant(QV4::ExecutionEngine *engine, const QVariant &v) +{ + const QMetaType type = v.metaType(); + const QQmlType qmlType = QQmlMetaType::qmlListType(type); + if (qmlType.isSequentialContainer()) + return fromData(engine, type, qmlType.listMetaSequence(), v.constData()); + + QSequentialIterable iterable; + if (QMetaType::convert( + type, v.constData(), QMetaType::fromType<QSequentialIterable>(), &iterable)) { + return fromData(engine, type, iterable.metaContainer(), v.constData()); + } + + return Encode::undefined(); +} -ReturnedValue SequencePrototype::fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded) +ReturnedValue SequencePrototype::fromData( + ExecutionEngine *engine, QMetaType type, QMetaSequence metaSequence, const void *data) { - QV4::Scope scope(engine); // 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::Encode::undefined(); } -} -#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 + return engine->memoryManager->allocate<Sequence>(type, metaSequence, data)->asReturnedValue(); +} -QVariant SequencePrototype::toVariant(Object *object) +QVariant SequencePrototype::toVariant(const Sequence *object) { - Q_ASSERT(object->isListType()); - FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ return QVariant(); } -} + Q_ASSERT(object->isV4SequenceType()); + const auto *p = object->d(); -#undef SEQUENCE_TO_VARIANT -#define SEQUENCE_TO_VARIANT(ElementType, ElementTypeName, SequenceType, unused) \ - if (typeHint == qMetaTypeId<SequenceType>()) { \ - return QQml##ElementTypeName##List::toVariant(a); \ - } else + // Note: For historical reasons, we ignore the result of loadReference() + // here. This allows us to retain sequences whose objects have vaninshed + // as "var" properties. It comes at the price of potentially returning + // outdated data. This is the behavior sequences have always shown. + if (p->isReference()) + object->loadReference(); + if (!p->hasData()) + return QVariant(); -QVariant SequencePrototype::toVariant(const QV4::Value &array, int typeHint, bool *succeeded) -{ - *succeeded = true; + return QVariant(p->listType(), p->storagePointer()); +} - if (!array.as<ArrayObject>()) { - *succeeded = false; +QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHint) +{ + if (!array.as<ArrayObject>()) return QVariant(); - } + QV4::Scope scope(array.as<Object>()->engine()); QV4::ScopedArrayObject a(scope, array); - FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ *succeeded = false; return QVariant(); } -} + const QQmlType type = QQmlMetaType::qmlListType(typeHint); + if (type.isSequentialContainer()) { + const QQmlTypePrivate *priv = type.priv(); + const QMetaSequence *meta = &priv->extraData.sequentialContainerTypeData; + const QMetaType containerMetaType(priv->listId); + QVariant result(containerMetaType); + qint64 length = a->getLength(); + Q_ASSERT(length >= 0); + Q_ASSERT(length <= qint64(std::numeric_limits<quint32>::max())); -#undef SEQUENCE_TO_VARIANT + QV4::ScopedValue v(scope); + for (quint32 i = 0; i < quint32(length); ++i) { + const QMetaType valueMetaType = priv->typeId; + QVariant variant = ExecutionEngine::toVariant(a->get(i), valueMetaType, false); + if (valueMetaType == QMetaType::fromType<QVariant>()) { + meta->addValueAtEnd(result.data(), &variant); + } else { + const QMetaType originalType = variant.metaType(); + if (originalType != valueMetaType) { + const QVariant converted = QQmlValueTypeProvider::createValueType( + variant, valueMetaType); + if (converted.isValid()) { + variant = converted; + } else if (!variant.convert(valueMetaType) && originalType.isValid()) { + // If the original type was void, we're converting a "hole" in a sparse + // array. There is no point in warning about that. + qWarning().noquote() + << QLatin1String("Could not convert array value " + "at position %1 from %2 to %3") + .arg(QString::number(i), + QString::fromUtf8(originalType.name()), + QString::fromUtf8(valueMetaType.name())); + } + } + meta->addValueAtEnd(result.data(), variant.constData()); + } + } + return result; + } -#define SEQUENCE_GET_RAWCONTAINERPTR(ElementType, ElementTypeName, SequenceType, unused) \ - if (const QQml##ElementTypeName##List *list = [&]() -> const QQml##ElementTypeName##List* \ - { if (typeHint == qMetaTypeId<SequenceType>()) return object->as<QQml##ElementTypeName##List>(); return nullptr;}()) \ - return list->getRawContainerPtr(); \ - else + return QVariant(); +} -void* SequencePrototype::getRawContainerPtr(const Object *object, int typeHint) +void *SequencePrototype::getRawContainerPtr(const Sequence *object, QMetaType typeHint) { - FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_GET_RAWCONTAINERPTR) { /* else */ return nullptr; } + if (object->d()->listType() == typeHint) + return object->getRawContainerPtr(); + return nullptr; } -#undef SEQUENCE_GET_RAWCONTAINERPTR - -#define MAP_META_TYPE(ElementType, ElementTypeName, SequenceType, unused) \ - if (object->as<QQml##ElementTypeName##List>()) { \ - return qMetaTypeId<SequenceType>(); \ - } else - -int SequencePrototype::metaTypeForSequence(const QV4::Object *object) +QMetaType SequencePrototype::metaTypeForSequence(const Sequence *object) { - FOREACH_QML_SEQUENCE_TYPE(MAP_META_TYPE) - /*else*/ { - return -1; - } + return object->d()->listType(); } -#undef MAP_META_TYPE +} // namespace QV4 QT_END_NAMESPACE + +#include "moc_qv4sequenceobject_p.cpp" diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index 886dfaa521..0908e52574 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4SEQUENCEWRAPPER_P_H #define QV4SEQUENCEWRAPPER_P_H @@ -53,37 +17,137 @@ #include <QtCore/qglobal.h> #include <QtCore/qvariant.h> +#include <QtQml/qqml.h> -#include "qv4value_p.h" -#include "qv4object_p.h" -#include "qv4context_p.h" -#include "qv4string_p.h" - -QT_REQUIRE_CONFIG(qml_sequence_object); +#include <private/qv4referenceobject_p.h> +#include <private/qv4value_p.h> +#include <private/qv4object_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -struct Q_QML_PRIVATE_EXPORT SequencePrototype : public QV4::Object +struct Sequence; +struct Q_QML_EXPORT SequencePrototype : public QV4::Object { V4_PROTOTYPE(arrayPrototype) void init(); static ReturnedValue method_valueOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - - static bool isSequenceType(int sequenceTypeId); - static ReturnedValue newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool readOnly, bool *succeeded); - static ReturnedValue fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded); - static int metaTypeForSequence(const Object *object); - static QVariant toVariant(Object *object); - static QVariant toVariant(const Value &array, int typeHint, bool *succeeded); - static void* getRawContainerPtr(const Object *object, int typeHint); + static ReturnedValue method_shift(const FunctionObject *b, const Value *thisObject, const Value *, int); + + static ReturnedValue newSequence( + QV4::ExecutionEngine *engine, QMetaType type, QMetaSequence metaSequence, const void *data, + Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags); + static ReturnedValue fromVariant(QV4::ExecutionEngine *engine, const QVariant &vd); + static ReturnedValue fromData( + QV4::ExecutionEngine *engine, QMetaType type, QMetaSequence metaSequence, const void *data); + + static QMetaType metaTypeForSequence(const Sequence *object); + static QVariant toVariant(const Sequence *object); + static QVariant toVariant(const Value &array, QMetaType typeHint); + static void *getRawContainerPtr(const Sequence *object, QMetaType typeHint); +}; + +namespace Heap { + +struct Sequence : ReferenceObject +{ + void init(QMetaType listType, QMetaSequence metaSequence, const void *container); + void init(QMetaType listType, QMetaSequence metaSequence, const void *container, + Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags); + + Sequence *detached() const; + void destroy(); + + bool hasData() const { return m_container != nullptr; } + void *storagePointer(); + const void *storagePointer() const { return m_container; } + + bool isReadOnly() const { return m_object && !canWriteBack(); } + + bool setVariant(const QVariant &variant); + QVariant toVariant() const; + + QMetaType listType() const { return QMetaType(m_listType); } + QMetaType valueMetaType() const { return QMetaType(m_metaSequence->valueMetaType); } + QMetaSequence metaSequence() const { return QMetaSequence(m_metaSequence); } + +private: + void initTypes(QMetaType listType, QMetaSequence metaSequence); + + void *m_container; + const QtPrivate::QMetaTypeInterface *m_listType; + const QtMetaContainerPrivate::QMetaSequenceInterface *m_metaSequence; +}; + +} + +struct Q_QML_EXPORT Sequence : public QV4::ReferenceObject +{ + V4_OBJECT2(Sequence, QV4::ReferenceObject) + Q_MANAGED_TYPE(V4Sequence) + V4_PROTOTYPE(sequencePrototype) + V4_NEEDS_DESTROY +public: + static QV4::ReturnedValue virtualGet( + const QV4::Managed *that, PropertyKey id, const Value *receiver, bool *hasProperty); + static qint64 virtualGetLength(const Managed *m); + static bool virtualPut(Managed *that, PropertyKey id, const QV4::Value &value, Value *receiver); + static bool virtualDeleteProperty(QV4::Managed *that, PropertyKey id); + static bool virtualIsEqualTo(Managed *that, Managed *other); + static QV4::OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target); + static int virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a); + + qsizetype size() const; + QVariant at(qsizetype index) const; + QVariant shift(); + void append(const QVariant &item); + void append(qsizetype num, const QVariant &item); + void replace(qsizetype index, const QVariant &item); + void removeLast(qsizetype num); + + QV4::ReturnedValue containerGetIndexed(qsizetype index, bool *hasProperty) const; + bool containerPutIndexed(qsizetype index, const QV4::Value &value); + bool containerDeleteIndexedProperty(qsizetype index); + bool containerIsEqualTo(Managed *other); + void *getRawContainerPtr() const; + bool loadReference() const; + bool storeReference(); }; } +#define QT_DECLARE_SEQUENTIAL_CONTAINER(LOCAL, FOREIGN, VALUE) \ + struct LOCAL \ + { \ + Q_GADGET \ + QML_ANONYMOUS \ + QML_SEQUENTIAL_CONTAINER(VALUE) \ + QML_FOREIGN(FOREIGN) \ + QML_ADDED_IN_VERSION(2, 0) \ + } + +// We use the original QT_COORD_TYPE name because that will match up with relevant other +// types in plugins.qmltypes (if you use either float or double, that is; otherwise you're +// on your own). +#ifdef QT_COORD_TYPE +QT_DECLARE_SEQUENTIAL_CONTAINER(QStdRealVectorForeign, std::vector<qreal>, QT_COORD_TYPE); +QT_DECLARE_SEQUENTIAL_CONTAINER(QRealListForeign, QList<qreal>, QT_COORD_TYPE); +#else +QT_DECLARE_SEQUENTIAL_CONTAINER(QRealStdVectorForeign, std::vector<qreal>, double); +QT_DECLARE_SEQUENTIAL_CONTAINER(QRealListForeign, QList<qreal>, double); +#endif + +QT_DECLARE_SEQUENTIAL_CONTAINER(QDoubleStdVectorForeign, std::vector<double>, double); +QT_DECLARE_SEQUENTIAL_CONTAINER(QFloatStdVectorForeign, std::vector<float>, float); +QT_DECLARE_SEQUENTIAL_CONTAINER(QIntStdVectorForeign, std::vector<int>, int); +QT_DECLARE_SEQUENTIAL_CONTAINER(QBoolStdVectorForeign, std::vector<bool>, bool); +QT_DECLARE_SEQUENTIAL_CONTAINER(QStringStdVectorForeign, std::vector<QString>, QString); +QT_DECLARE_SEQUENTIAL_CONTAINER(QUrlStdVectorForeign, std::vector<QUrl>, QUrl); + +#undef QT_DECLARE_SEQUENTIAL_CONTAINER + QT_END_NAMESPACE #endif // QV4SEQUENCEWRAPPER_P_H diff --git a/src/qml/jsruntime/qv4setiterator.cpp b/src/qml/jsruntime/qv4setiterator.cpp index d32e2079a0..0491d26352 100644 --- a/src/qml/jsruntime/qv4setiterator.cpp +++ b/src/qml/jsruntime/qv4setiterator.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Crimson AS <info@crimson.no> -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 Crimson AS <info@crimson.no> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <private/qv4iterator_p.h> #include <private/qv4estable_p.h> diff --git a/src/qml/jsruntime/qv4setiterator_p.h b/src/qml/jsruntime/qv4setiterator_p.h index 78eda6d57b..37f912e01a 100644 --- a/src/qml/jsruntime/qv4setiterator_p.h +++ b/src/qml/jsruntime/qv4setiterator_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Crimson AS <info@crimson.no> -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 Crimson AS <info@crimson.no> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4SETITERATOR_P_H #define QV4SETITERATOR_P_H @@ -66,7 +30,7 @@ namespace Heap { Member(class, NoMark, quint32, setNextIndex) DECLARE_HEAP_OBJECT(SetIteratorObject, Object) { - DECLARE_MARKOBJECTS(SetIteratorObject); + DECLARE_MARKOBJECTS(SetIteratorObject) void init(Object *obj, QV4::ExecutionEngine *engine) { Object::init(); diff --git a/src/qml/jsruntime/qv4setobject.cpp b/src/qml/jsruntime/qv4setobject.cpp index 1664d1bd71..43cf34d2c3 100644 --- a/src/qml/jsruntime/qv4setobject.cpp +++ b/src/qml/jsruntime/qv4setobject.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Crimson AS <info@crimson.no> -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 Crimson AS <info@crimson.no> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4setobject_p.h" @@ -44,19 +8,20 @@ #include "qv4symbol_p.h" using namespace QV4; +using namespace Qt::Literals::StringLiterals; DEFINE_OBJECT_VTABLE(SetCtor); DEFINE_OBJECT_VTABLE(WeakSetCtor); DEFINE_OBJECT_VTABLE(SetObject); -void Heap::WeakSetCtor::init(QV4::ExecutionContext *scope) +void Heap::WeakSetCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("WeakSet")); + Heap::FunctionObject::init(engine, QStringLiteral("WeakSet")); } -void Heap::SetCtor::init(QV4::ExecutionContext *scope) +void Heap::SetCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("Set")); + Heap::FunctionObject::init(engine, QStringLiteral("Set")); } ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget, bool isWeak) @@ -73,7 +38,7 @@ ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv, if (argc > 0) { ScopedValue iterable(scope, argv[0]); if (!iterable->isUndefined() && !iterable->isNull()) { - ScopedFunctionObject adder(scope, a->get(ScopedString(scope, scope.engine->newString(QString::fromLatin1("add"))))); + ScopedFunctionObject adder(scope, a->get(ScopedString(scope, scope.engine->newString(u"add"_s)))); if (!adder) return scope.engine->throwTypeError(); ScopedObject iter(scope, Runtime::GetIterator::call(scope.engine, iterable, true)); @@ -90,10 +55,8 @@ ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv, return a.asReturnedValue(); adder->call(a, nextValue, 1); - if (scope.engine->hasException) { - ScopedValue falsey(scope, Encode(false)); - return Runtime::IteratorClose::call(scope.engine, iter, falsey); - } + if (scope.hasException()) + return Runtime::IteratorClose::call(scope.engine, iter); } } } @@ -141,6 +104,12 @@ ReturnedValue WeakSetPrototype::method_add(const FunctionObject *b, const Value (!argc || !argv[0].isObject())) return scope.engine->throwTypeError(); + QV4::WriteBarrier::markCustom(scope.engine, [&](QV4::MarkStack *ms) { + if (scope.engine->memoryManager->gcStateMachine->state <= GCState::FreeWeakSets) + return; + argv[0].heapObject()->mark(ms); + }); + that->d()->esTable->set(argv[0], Value::undefinedValue()); return that.asReturnedValue(); } @@ -229,6 +198,11 @@ ReturnedValue SetPrototype::method_add(const FunctionObject *b, const Value *thi if (!that || that->d()->isWeakSet) return scope.engine->throwTypeError(); + QV4::WriteBarrier::markCustom(scope.engine, [&](QV4::MarkStack *ms) { + if (auto *h = argv[0].heapObject()) + h->mark(ms); + }); + that->d()->esTable->set(argv[0], Value::undefinedValue()); return that.asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4setobject_p.h b/src/qml/jsruntime/qv4setobject_p.h index 21584e2132..118cdebd5a 100644 --- a/src/qml/jsruntime/qv4setobject_p.h +++ b/src/qml/jsruntime/qv4setobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Crimson AS <info@crimson.no> -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 Crimson AS <info@crimson.no> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4SETOBJECT_P_H #define QV4SETOBJECT_P_H @@ -65,12 +29,12 @@ class ESTable; namespace Heap { struct WeakSetCtor : FunctionObject { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; struct SetCtor : WeakSetCtor { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; struct SetObject : Object { @@ -115,7 +79,7 @@ struct WeakSetPrototype : Object { void init(ExecutionEngine *engine, Object *ctor); - static ReturnedValue method_add(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + Q_AUTOTEST_EXPORT static ReturnedValue method_add(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_delete(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; @@ -125,7 +89,7 @@ struct SetPrototype : WeakSetPrototype { void init(ExecutionEngine *engine, Object *ctor); - static ReturnedValue method_add(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + Q_AUTOTEST_EXPORT static ReturnedValue method_add(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_clear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_delete(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); diff --git a/src/qml/jsruntime/qv4sparsearray.cpp b/src/qml/jsruntime/qv4sparsearray.cpp index 8930c9a94d..656c496a2a 100644 --- a/src/qml/jsruntime/qv4sparsearray.cpp +++ b/src/qml/jsruntime/qv4sparsearray.cpp @@ -1,47 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4sparsearray_p.h" -#include "qv4runtime_p.h" -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include "qv4scopedvalue_p.h" #include <stdlib.h> #ifdef QT_QMAP_DEBUG @@ -359,7 +319,7 @@ static inline void qMapDeallocate(SparseArrayNode *node, int alignment) SparseArrayNode *SparseArray::createNode(uint sl, SparseArrayNode *parent, bool left) { - SparseArrayNode *node = static_cast<SparseArrayNode *>(qMapAllocate(sizeof(SparseArrayNode), Q_ALIGNOF(SparseArrayNode))); + SparseArrayNode *node = static_cast<SparseArrayNode *>(qMapAllocate(sizeof(SparseArrayNode), alignof(SparseArrayNode))); Q_CHECK_PTR(node); node->p = (quintptr)parent; diff --git a/src/qml/jsruntime/qv4sparsearray_p.h b/src/qml/jsruntime/qv4sparsearray_p.h index c1e50c8dcf..7da42a4985 100644 --- a/src/qml/jsruntime/qv4sparsearray_p.h +++ b/src/qml/jsruntime/qv4sparsearray_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4SPARSEARRAY_H #define QV4SPARSEARRAY_H @@ -146,8 +110,8 @@ struct Q_QML_EXPORT SparseArray { SparseArray(); ~SparseArray() { - if (root()) - freeTree(header.left, Q_ALIGNOF(SparseArrayNode)); + if (SparseArrayNode *n = root()) + freeTree(n, alignof(SparseArrayNode)); } SparseArray(const SparseArray &other); @@ -236,10 +200,10 @@ inline uint SparseArray::pop_front() 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; + SparseArrayNode *rootNode = root(); + while (rootNode) { + rootNode->size_left -= 1; + rootNode = rootNode->left; } } return idx; @@ -323,37 +287,45 @@ inline QList<int> SparseArray::keys() const inline const SparseArrayNode *SparseArray::lowerBound(uint akey) const { - const SparseArrayNode *lb = root()->lowerBound(akey); - if (!lb) - lb = end(); - return lb; + if (SparseArrayNode *n = root()) { + if (const SparseArrayNode *lb = n->lowerBound(akey)) + return lb; + } + + return end(); } inline SparseArrayNode *SparseArray::lowerBound(uint akey) { - SparseArrayNode *lb = root()->lowerBound(akey); - if (!lb) - lb = end(); - return lb; + if (SparseArrayNode *n = root()) { + if (SparseArrayNode *lb = n->lowerBound(akey)) + return lb; + } + + return end(); } inline const SparseArrayNode *SparseArray::upperBound(uint akey) const { - const SparseArrayNode *ub = root()->upperBound(akey); - if (!ub) - ub = end(); - return ub; + if (SparseArrayNode *n = root()) { + if (const SparseArrayNode *ub = n->upperBound(akey)) + return ub; + } + + return end(); } inline SparseArrayNode *SparseArray::upperBound(uint akey) { - SparseArrayNode *ub = root()->upperBound(akey); - if (!ub) - ub = end(); - return ub; + if (SparseArrayNode *n = root()) { + if (SparseArrayNode *ub = n->upperBound(akey)) + return ub; + } + + return end(); } } diff --git a/src/qml/jsruntime/qv4sqlerrors.cpp b/src/qml/jsruntime/qv4sqlerrors.cpp new file mode 100644 index 0000000000..c942871702 --- /dev/null +++ b/src/qml/jsruntime/qv4sqlerrors.cpp @@ -0,0 +1,27 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qv4sqlerrors_p.h" +#include "private/qv4engine_p.h" +#include "private/qv4object_p.h" + +QT_BEGIN_NAMESPACE + +using namespace QV4; + +void qt_add_sqlexceptions(QV4::ExecutionEngine *engine) +{ + Scope scope(engine); + ScopedObject sqlexception(scope, engine->newObject()); + sqlexception->defineReadonlyProperty(QStringLiteral("UNKNOWN_ERR"), Value::fromInt32(SQLEXCEPTION_UNKNOWN_ERR)); + sqlexception->defineReadonlyProperty(QStringLiteral("DATABASE_ERR"), Value::fromInt32(SQLEXCEPTION_DATABASE_ERR)); + sqlexception->defineReadonlyProperty(QStringLiteral("VERSION_ERR"), Value::fromInt32(SQLEXCEPTION_VERSION_ERR)); + sqlexception->defineReadonlyProperty(QStringLiteral("TOO_LARGE_ERR"), Value::fromInt32(SQLEXCEPTION_TOO_LARGE_ERR)); + sqlexception->defineReadonlyProperty(QStringLiteral("QUOTA_ERR"), Value::fromInt32(SQLEXCEPTION_QUOTA_ERR)); + sqlexception->defineReadonlyProperty(QStringLiteral("SYNTAX_ERR"), Value::fromInt32(SQLEXCEPTION_SYNTAX_ERR)); + sqlexception->defineReadonlyProperty(QStringLiteral("CONSTRAINT_ERR"), Value::fromInt32(SQLEXCEPTION_CONSTRAINT_ERR)); + sqlexception->defineReadonlyProperty(QStringLiteral("TIMEOUT_ERR"), Value::fromInt32(SQLEXCEPTION_TIMEOUT_ERR)); + engine->globalObject->defineDefaultProperty(QStringLiteral("SQLException"), sqlexception); +} + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4sqlerrors_p.h b/src/qml/jsruntime/qv4sqlerrors_p.h new file mode 100644 index 0000000000..b6c5d5446d --- /dev/null +++ b/src/qml/jsruntime/qv4sqlerrors_p.h @@ -0,0 +1,38 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QV8SQLERRORS_P_H +#define QV8SQLERRORS_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/private/qglobal_p.h> + +QT_BEGIN_NAMESPACE +#define SQLEXCEPTION_UNKNOWN_ERR 1 +#define SQLEXCEPTION_DATABASE_ERR 2 +#define SQLEXCEPTION_VERSION_ERR 3 +#define SQLEXCEPTION_TOO_LARGE_ERR 4 +#define SQLEXCEPTION_QUOTA_ERR 5 +#define SQLEXCEPTION_SYNTAX_ERR 6 +#define SQLEXCEPTION_CONSTRAINT_ERR 7 +#define SQLEXCEPTION_TIMEOUT_ERR 8 + +namespace QV4 { +struct ExecutionEngine; +} + +void qt_add_sqlexceptions(QV4::ExecutionEngine *engine); + +QT_END_NAMESPACE + +#endif // QV8SQLERRORS_P_H diff --git a/src/qml/jsruntime/qv4stackframe.cpp b/src/qml/jsruntime/qv4stackframe.cpp index a716c53aea..5117e745a0 100644 --- a/src/qml/jsruntime/qv4stackframe.cpp +++ b/src/qml/jsruntime/qv4stackframe.cpp @@ -1,42 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + #include "qv4stackframe_p.h" +#include <private/qv4qobjectwrapper_p.h> #include <QtCore/qstring.h> using namespace QV4; @@ -51,24 +17,54 @@ QString CppStackFrame::function() const return v4Function ? v4Function->name()->toQString() : QString(); } -int CppStackFrame::lineNumber() const +static const CompiledData::CodeOffsetToLineAndStatement *lineAndStatement(const CppStackFrame *frame) { - if (!v4Function) - return -1; + if (!frame->v4Function || frame->instructionPointer <= 0) + return nullptr; - auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) { + auto findLine = [](const CompiledData::CodeOffsetToLineAndStatement &entry, uint offset) { return entry.codeOffset < offset; }; - const QV4::CompiledData::Function *cf = v4Function->compiledFunction; - uint offset = instructionPointer; - const CompiledData::CodeOffsetToLine *lineNumbers = cf->lineNumberTable(); - uint nLineNumbers = cf->nLineNumbers; - const CompiledData::CodeOffsetToLine *line = std::lower_bound(lineNumbers, lineNumbers + nLineNumbers, offset, findLine) - 1; - return line->line; + const QV4::CompiledData::Function *cf = frame->v4Function->compiledFunction; + const uint offset = frame->instructionPointer; + const CompiledData::CodeOffsetToLineAndStatement *lineAndStatementNumbers + = cf->lineAndStatementNumberTable(); + const uint nLineAndStatementNumbers = cf->nLineAndStatementNumbers; + return std::lower_bound( + lineAndStatementNumbers, lineAndStatementNumbers + nLineAndStatementNumbers, + offset, findLine) - 1; } -ReturnedValue CppStackFrame::thisObject() const { - return jsFrame->thisObject.asReturnedValue(); +int CppStackFrame::lineNumber() const +{ + if (auto *line = lineAndStatement(this)) + return line->line; + return missingLineNumber(); +} + +int CppStackFrame::statementNumber() const +{ + if (auto *statement = lineAndStatement(this)) + return statement->statement; + return -1; } +int CppStackFrame::missingLineNumber() const +{ + // Remove the first bit so that we can cast to positive int and negate. + // Remove the last bit so that it can't be -1. + const int result = -int(quintptr(this) & 0x7ffffffe); + Q_ASSERT(result < -1); + return result; +} + +ReturnedValue QV4::CppStackFrame::thisObject() const +{ + if (isJSTypesFrame()) + return static_cast<const JSTypesStackFrame *>(this)->thisObject(); + + Q_ASSERT(isMetaTypesFrame()); + const auto metaTypesFrame = static_cast<const MetaTypesStackFrame *>(this); + return QObjectWrapper::wrap(metaTypesFrame->context()->engine(), metaTypesFrame->thisObject()); +} diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h index bf689a74bc..f24b0b2433 100644 --- a/src/qml/jsruntime/qv4stackframe_p.h +++ b/src/qml/jsruntime/qv4stackframe_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4STACKFRAME_H #define QV4STACKFRAME_H @@ -50,61 +14,179 @@ // We mean it. // +#include <private/qv4scopedvalue_p.h> #include <private/qv4context_p.h> #include <private/qv4enginebase_p.h> #include <private/qv4calldata_p.h> #include <private/qv4function_p.h> +#include <type_traits> + QT_BEGIN_NAMESPACE namespace QV4 { -struct Q_QML_EXPORT CppStackFrame { - EngineBase *engine; - Value *savedStackTop; +struct CppStackFrame; +struct Q_QML_EXPORT CppStackFrameBase +{ + enum class Kind : quint8 { JS, Meta }; + CppStackFrame *parent; Function *v4Function; - CallData *jsFrame; - const Value *originalArguments; int originalArgumentsCount; int instructionPointer; - const char *yield; - const char *unwindHandler; - const char *unwindLabel; - int unwindLevel; - bool yieldIsIterator; - bool callerCanHandleTailCall; - bool pendingTailCall; - bool isTailCalling; - - void init(EngineBase *engine, Function *v4Function, const Value *argv, int argc, bool callerCanHandleTailCall = false) { - this->engine = engine; + QT_WARNING_PUSH + QT_WARNING_DISABLE_MSVC(4201) // nonstandard extension used: nameless struct/union + union { + struct { + Value *savedStackTop; + CallData *jsFrame; + const Value *originalArguments; + const char *yield; + const char *unwindHandler; + const char *unwindLabel; + int unwindLevel; + bool yieldIsIterator; + bool callerCanHandleTailCall; + bool pendingTailCall; + bool isTailCalling; + }; + struct { + ExecutionContext *context; + QObject *thisObject; + const QMetaType *metaTypes; + void **returnAndArgs; + bool returnValueIsUndefined; + }; + }; + QT_WARNING_POP + + Kind kind; +}; + +struct Q_QML_EXPORT CppStackFrame : protected CppStackFrameBase +{ + // We want to have those public but we can't declare them as public without making the struct + // non-standard layout. So we have this other struct with "using" in between. + using CppStackFrameBase::instructionPointer; + using CppStackFrameBase::v4Function; + + void init(Function *v4Function, int argc, Kind kind) { this->v4Function = v4Function; - originalArguments = argv; originalArgumentsCount = argc; instructionPointer = 0; - yield = nullptr; - unwindHandler = nullptr; - unwindLabel = nullptr; - unwindLevel = 0; - yieldIsIterator = false; - this->callerCanHandleTailCall = callerCanHandleTailCall; - pendingTailCall = false; - isTailCalling = false; + this->kind = kind; } - void push() { + bool isJSTypesFrame() const { return kind == Kind::JS; } + bool isMetaTypesFrame() const { return kind == Kind::Meta; } + + QString source() const; + QString function() const; + int lineNumber() const; + int statementNumber() const; + + int missingLineNumber() const; + + CppStackFrame *parentFrame() const { return parent; } + void setParentFrame(CppStackFrame *parentFrame) { parent = parentFrame; } + + int argc() const { return originalArgumentsCount; } + + inline ExecutionContext *context() const; + + Heap::CallContext *callContext() const { return callContext(context()->d()); } + ReturnedValue thisObject() const; + +protected: + CppStackFrame() = default; + + void push(EngineBase *engine) + { + Q_ASSERT(kind == Kind::JS || kind == Kind::Meta); parent = engine->currentStackFrame; engine->currentStackFrame = this; - savedStackTop = engine->jsStackTop; } - void pop() { + void pop(EngineBase *engine) + { engine->currentStackFrame = parent; - engine->jsStackTop = savedStackTop; } + Heap::CallContext *callContext(Heap::ExecutionContext *ctx) const + { + while (ctx->type != Heap::ExecutionContext::Type_CallContext) + ctx = ctx->outer; + return static_cast<Heap::CallContext *>(ctx); + } +}; + +struct Q_QML_EXPORT MetaTypesStackFrame : public CppStackFrame +{ + using CppStackFrame::push; + using CppStackFrame::pop; + + void init(Function *v4Function, QObject *thisObject, ExecutionContext *context, + void **returnAndArgs, const QMetaType *metaTypes, int argc) + { + CppStackFrame::init(v4Function, argc, Kind::Meta); + CppStackFrameBase::thisObject = thisObject; + CppStackFrameBase::context = context; + CppStackFrameBase::metaTypes = metaTypes; + CppStackFrameBase::returnAndArgs = returnAndArgs; + CppStackFrameBase::returnValueIsUndefined = false; + } + + QMetaType returnType() const { return metaTypes[0]; } + void *returnValue() const { return returnAndArgs[0]; } + + bool isReturnValueUndefined() const { return CppStackFrameBase::returnValueIsUndefined; } + void setReturnValueUndefined() { CppStackFrameBase::returnValueIsUndefined = true; } + + const QMetaType *argTypes() const { return metaTypes + 1; } + void **argv() const { return returnAndArgs + 1; } + + const QMetaType *returnAndArgTypes() const { return metaTypes; } + void **returnAndArgValues() const { return returnAndArgs; } + + QObject *thisObject() const { return CppStackFrameBase::thisObject; } + + ExecutionContext *context() const { return CppStackFrameBase::context; } + void setContext(ExecutionContext *context) { CppStackFrameBase::context = context; } + + Heap::CallContext *callContext() const + { + return CppStackFrame::callContext(CppStackFrameBase::context->d()); + } +}; + +struct Q_QML_EXPORT JSTypesStackFrame : public CppStackFrame +{ + using CppStackFrame::jsFrame; + + // The JIT needs to poke directly into those using offsetof + using CppStackFrame::unwindHandler; + using CppStackFrame::unwindLabel; + using CppStackFrame::unwindLevel; + + void init(Function *v4Function, const Value *argv, int argc, + bool callerCanHandleTailCall = false) + { + CppStackFrame::init(v4Function, argc, Kind::JS); + CppStackFrame::originalArguments = argv; + CppStackFrame::yield = nullptr; + CppStackFrame::unwindHandler = nullptr; + CppStackFrame::yieldIsIterator = false; + CppStackFrame::callerCanHandleTailCall = callerCanHandleTailCall; + CppStackFrame::pendingTailCall = false; + CppStackFrame::isTailCalling = false; + CppStackFrame::unwindLabel = nullptr; + CppStackFrame::unwindLevel = 0; + } + + const Value *argv() const { return originalArguments; } + static uint requiredJSStackFrameSize(uint nRegisters) { return CallData::HeaderSize() + nRegisters; } @@ -114,13 +196,17 @@ struct Q_QML_EXPORT CppStackFrame { uint requiredJSStackFrameSize() const { return requiredJSStackFrameSize(v4Function); } + void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope, const Value &thisObject, const Value &newTarget = Value::undefinedValue()) { setupJSFrame(stackSpace, function, scope, thisObject, newTarget, - v4Function->nFormals, v4Function->compiledFunction->nRegisters); + v4Function->compiledFunction->nFormals, + v4Function->compiledFunction->nRegisters); } - void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope, - const Value &thisObject, const Value &newTarget, uint nFormals, uint nRegisters) + + void setupJSFrame( + Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope, + const Value &thisObject, const Value &newTarget, uint nFormals, uint nRegisters) { jsFrame = reinterpret_cast<CallData *>(stackSpace); jsFrame->function = function; @@ -134,13 +220,17 @@ struct Q_QML_EXPORT CppStackFrame { argc = nFormals; jsFrame->setArgc(argc); - memcpy(jsFrame->args, originalArguments, argc*sizeof(Value)); + // memcpy requires non-null ptr, even if argc * sizeof(Value) == 0 + if (originalArguments) + memcpy(jsFrame->args, originalArguments, argc * sizeof(Value)); Q_STATIC_ASSERT(Encode::undefined() == 0); - memset(jsFrame->args + argc, 0, (nRegisters - argc)*sizeof(Value)); + memset(jsFrame->args + argc, 0, (nRegisters - argc) * sizeof(Value)); if (v4Function && v4Function->compiledFunction) { - const int firstDeadZoneRegister = v4Function->compiledFunction->firstTemporalDeadZoneRegister; - const int registerDeadZoneSize = v4Function->compiledFunction->sizeOfRegisterTemporalDeadZone; + const int firstDeadZoneRegister + = v4Function->compiledFunction->firstTemporalDeadZoneRegister; + const int registerDeadZoneSize + = v4Function->compiledFunction->sizeOfRegisterTemporalDeadZone; const Value * tdzEnd = stackSpace + firstDeadZoneRegister + registerDeadZoneSize; for (Value *v = stackSpace + firstDeadZoneRegister; v < tdzEnd; ++v) @@ -148,22 +238,92 @@ struct Q_QML_EXPORT CppStackFrame { } } - QString source() const; - QString function() const; - inline QV4::ExecutionContext *context() const { + ExecutionContext *context() const + { return static_cast<ExecutionContext *>(&jsFrame->context); } - int lineNumber() const; - inline QV4::Heap::CallContext *callContext() const { - Heap::ExecutionContext *ctx = static_cast<ExecutionContext &>(jsFrame->context).d();\ - while (ctx->type != Heap::ExecutionContext::Type_CallContext) - ctx = ctx->outer; - return static_cast<Heap::CallContext *>(ctx); + void setContext(ExecutionContext *context) + { + jsFrame->context = context; + } + + Heap::CallContext *callContext() const + { + return CppStackFrame::callContext(static_cast<ExecutionContext &>(jsFrame->context).d()); + } + + bool isTailCalling() const { return CppStackFrame::isTailCalling; } + void setTailCalling(bool tailCalling) { CppStackFrame::isTailCalling = tailCalling; } + + bool pendingTailCall() const { return CppStackFrame::pendingTailCall; } + void setPendingTailCall(bool pending) { CppStackFrame::pendingTailCall = pending; } + + const char *yield() const { return CppStackFrame::yield; } + void setYield(const char *yield) { CppStackFrame::yield = yield; } + + bool yieldIsIterator() const { return CppStackFrame::yieldIsIterator; } + void setYieldIsIterator(bool isIter) { CppStackFrame::yieldIsIterator = isIter; } + + bool callerCanHandleTailCall() const { return CppStackFrame::callerCanHandleTailCall; } + + ReturnedValue thisObject() const + { + return jsFrame->thisObject.asReturnedValue(); + } + + Value *framePointer() const { return savedStackTop; } + + void push(EngineBase *engine) { + CppStackFrame::push(engine); + savedStackTop = engine->jsStackTop; + } + + void pop(EngineBase *engine) { + CppStackFrame::pop(engine); + engine->jsStackTop = savedStackTop; } - ReturnedValue thisObject() const; }; +inline ExecutionContext *CppStackFrame::context() const +{ + if (isJSTypesFrame()) + return static_cast<const JSTypesStackFrame *>(this)->context(); + + Q_ASSERT(isMetaTypesFrame()); + return static_cast<const MetaTypesStackFrame *>(this)->context(); +} + +struct ScopedStackFrame +{ + ScopedStackFrame(const Scope &scope, ExecutionContext *context) + : engine(scope.engine) + { + if (auto currentFrame = engine->currentStackFrame) { + frame.init(currentFrame->v4Function, nullptr, context, nullptr, nullptr, 0); + frame.instructionPointer = currentFrame->instructionPointer; + } else { + frame.init(nullptr, nullptr, context, nullptr, nullptr, 0); + } + frame.push(engine); + } + + ~ScopedStackFrame() + { + frame.pop(engine); + } + +private: + ExecutionEngine *engine = nullptr; + MetaTypesStackFrame frame; +}; + +Q_STATIC_ASSERT(sizeof(CppStackFrame) == sizeof(JSTypesStackFrame)); +Q_STATIC_ASSERT(sizeof(CppStackFrame) == sizeof(MetaTypesStackFrame)); +Q_STATIC_ASSERT(std::is_standard_layout_v<CppStackFrame>); +Q_STATIC_ASSERT(std::is_standard_layout_v<JSTypesStackFrame>); +Q_STATIC_ASSERT(std::is_standard_layout_v<MetaTypesStackFrame>); + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index 223f4a4769..8b5594b43b 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -1,48 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4string_p.h" #include "qv4value_p.h" #include "qv4identifiertable_p.h" #include "qv4runtime_p.h" -#include "qv4objectproto_p.h" -#include "qv4stringobject_p.h" +#include <QtQml/private/qv4mm_p.h> #include <QtCore/QHash> #include <QtCore/private/qnumeric_p.h> @@ -91,18 +54,14 @@ bool String::virtualIsEqualTo(Managed *t, Managed *o) void Heap::String::init(const QString &t) { - Base::init(); - + QString mutableText(t); + StringOrSymbol::init(mutableText.data_ptr()); subtype = String::StringType_Unknown; - - text = const_cast<QString &>(t).data_ptr(); - text->ref.ref(); } void Heap::ComplexString::init(String *l, String *r) { - Base::init(); - + StringOrSymbol::init(); subtype = String::StringType_AddedString; left = l; @@ -125,7 +84,7 @@ void Heap::ComplexString::init(String *l, String *r) void Heap::ComplexString::init(Heap::String *ref, int from, int len) { Q_ASSERT(ref->length() >= from + len); - Base::init(); + StringOrSymbol::init(); subtype = String::StringType_SubString; @@ -136,11 +95,11 @@ void Heap::ComplexString::init(Heap::String *ref, int from, int len) void Heap::StringOrSymbol::destroy() { - if (text) { - internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(-text->size) * (int)sizeof(QChar)); - if (!text->ref.deref()) - QStringData::deallocate(text); + if (subtype < Heap::String::StringType_AddedString) { + internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage( + qptrdiff(-text()->size) * qptrdiff(sizeof(QChar))); } + text().~QStringPrivate(); Base::destroy(); } @@ -164,27 +123,27 @@ uint String::toUInt(bool *ok) const void String::createPropertyKeyImpl() const { - if (!d()->text) + if (d()->subtype >= Heap::String::StringType_AddedString) d()->simplifyString(); - Q_ASSERT(d()->text); + Q_ASSERT(d()->subtype < Heap::String::StringType_AddedString); engine()->identifierTable->asPropertyKey(this); } void Heap::String::simplifyString() const { - Q_ASSERT(!text); + Q_ASSERT(subtype >= StringType_AddedString); int l = length(); QString result(l, Qt::Uninitialized); QChar *ch = const_cast<QChar *>(result.constData()); append(this, ch); - text = result.data_ptr(); - text->ref.ref(); + text() = result.data_ptr(); const ComplexString *cs = static_cast<const ComplexString *>(this); identifier = PropertyKey::invalid(); cs->left = cs->right = nullptr; - internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(text->size) * (qptrdiff)sizeof(QChar)); + internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage( + qptrdiff(text().size) * qptrdiff(sizeof(QChar))); subtype = StringType_Unknown; } @@ -206,43 +165,58 @@ bool Heap::String::startsWithUpper() const offset = cs->from; } Q_ASSERT(str->subtype < Heap::String::StringType_Complex); - return str->text->size > offset && QChar::isUpper(str->text->data()[offset]); + return str->text().size > offset && QChar::isUpper(str->text().data()[offset]); } void Heap::String::append(const String *data, QChar *ch) { - std::vector<const String *> worklist; + // in-order visitation with explicit stack + // where leaf nodes are "real" strings that get appended to ch + + enum StatusTag : bool { NotVisited, Visited }; + using Pointer = QTaggedPointer<const String, StatusTag>; + + std::vector<Pointer> worklist; worklist.reserve(32); - worklist.push_back(data); + worklist.push_back(Pointer(data)); while (!worklist.empty()) { - const String *item = worklist.back(); - worklist.pop_back(); + Pointer item = worklist.back(); + if (item.tag() == Visited) { + Q_ASSERT(item->subtype == StringType_AddedString); + const ComplexString *cs = static_cast<const ComplexString *>(item.data()); + worklist.pop_back(); + worklist.push_back(Pointer(cs->right)); + continue; + } if (item->subtype == StringType_AddedString) { - const ComplexString *cs = static_cast<const ComplexString *>(item); - worklist.push_back(cs->right); - worklist.push_back(cs->left); + // we need to keep the node in the worklist, as we still need to handle "right" + worklist.back().setTag(Visited); + const ComplexString *cs = static_cast<const ComplexString *>(item.data()); + worklist.push_back(Pointer(cs->left)); } else if (item->subtype == StringType_SubString) { - const ComplexString *cs = static_cast<const ComplexString *>(item); + worklist.pop_back(); + const ComplexString *cs = static_cast<const ComplexString *>(item.data()); memcpy(ch, cs->left->toQString().constData() + cs->from, cs->len*sizeof(QChar)); ch += cs->len; } else { - memcpy(static_cast<void *>(ch), static_cast<const void *>(item->text->data()), item->text->size * sizeof(QChar)); - ch += item->text->size; + worklist.pop_back(); + memcpy(static_cast<void *>(ch), item->text().data(), item->text().size * sizeof(QChar)); + ch += item->text().size; } } } void Heap::StringOrSymbol::createHashValue() const { - if (!text) { + if (subtype >= StringType_AddedString) { Q_ASSERT(internalClass->vtable->isString); static_cast<const Heap::String *>(this)->simplifyString(); } - Q_ASSERT(text); - const QChar *ch = reinterpret_cast<const QChar *>(text->data()); - const QChar *end = ch + text->size; + Q_ASSERT(subtype < StringType_AddedString); + const QChar *ch = reinterpret_cast<const QChar *>(text().data()); + const QChar *end = ch + text().size; stringHash = QV4::String::calculateHashValue(ch, end, &subtype); } diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 52fe09cd72..370baadc98 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4STRING_H #define QV4STRING_H @@ -65,7 +29,7 @@ struct PropertyKey; namespace Heap { -struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base +struct Q_QML_EXPORT StringOrSymbol : Base { enum StringType { StringType_Symbol, @@ -77,7 +41,18 @@ struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base StringType_Complex = StringType_AddedString }; - mutable QStringData *text; + void init() { + Base::init(); + new (&textStorage) QStringPrivate; + } + + void init(QStringPrivate text) + { + Base::init(); + new (&textStorage) QStringPrivate(std::move(text)); + } + + mutable struct { alignas(QStringPrivate) unsigned char data[sizeof(QStringPrivate)]; } textStorage; mutable PropertyKey identifier; mutable uint subtype; mutable uint stringHash; @@ -85,12 +60,11 @@ struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base static void markObjects(Heap::Base *that, MarkStack *markStack); void destroy(); + QStringPrivate &text() const { return *reinterpret_cast<QStringPrivate *>(&textStorage); } + inline QString toQString() const { - if (!text) - return QString(); - QStringDataPtr ptr = { text }; - text->ref.ref(); - return QString(ptr); + QStringPrivate dd = text(); + return QString(std::move(dd)); } void createHashValue() const; inline unsigned hashValue() const { @@ -102,7 +76,7 @@ struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base } }; -struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol { +struct Q_QML_EXPORT String : StringOrSymbol { static void markObjects(Heap::Base *that, MarkStack *markStack); const VTable *vtable() const { @@ -113,14 +87,12 @@ struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol { void simplifyString() const; int length() const; std::size_t retainedTextSize() const { - return subtype >= StringType_Complex ? 0 : (std::size_t(text->size) * sizeof(QChar)); + return subtype >= StringType_Complex ? 0 : (std::size_t(text().size) * sizeof(QChar)); } inline QString toQString() const { if (subtype >= StringType_Complex) simplifyString(); - QStringDataPtr ptr = { text }; - text->ref.ref(); - return QString(ptr); + return StringOrSymbol::toQString(); } inline bool isEqualTo(const String *other) const { if (this == other) @@ -141,7 +113,7 @@ struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol { private: static void append(const String *data, QChar *ch); }; -Q_STATIC_ASSERT(std::is_trivial< String >::value); +Q_STATIC_ASSERT(std::is_trivial_v<String>); struct ComplexString : String { void init(String *l, String *n); @@ -154,16 +126,17 @@ struct ComplexString : String { }; int len; }; -Q_STATIC_ASSERT(std::is_trivial< ComplexString >::value); +Q_STATIC_ASSERT(std::is_trivial_v<ComplexString>); inline int String::length() const { - return text ? text->size : static_cast<const ComplexString *>(this)->len; + // TODO: ensure that our strings never actually grow larger than INT_MAX + return subtype < StringType_AddedString ? int(text().size) : static_cast<const ComplexString *>(this)->len; } } -struct Q_QML_PRIVATE_EXPORT StringOrSymbol : public Managed { +struct Q_QML_EXPORT StringOrSymbol : public Managed { V4_MANAGED(StringOrSymbol, Managed) V4_NEEDS_DESTROY enum { @@ -182,7 +155,7 @@ public: } }; -struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol { +struct Q_QML_EXPORT String : public StringOrSymbol { V4_MANAGED(String, StringOrSymbol) Q_MANAGED_TYPE(String) V4_INTERNALCLASS(String) @@ -222,6 +195,12 @@ struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol { return calculateHashValue(ch, end, subtype); } + static uint createHashValueDisallowingArrayIndex(const QChar *ch, int length, uint *subtype) + { + const QChar *end = ch + length; + return calculateHashValue<String::DisallowArrayIndex>(ch, end, subtype); + } + static uint createHashValue(const char *ch, int length, uint *subtype) { const char *end = ch + length; @@ -235,15 +214,19 @@ protected: static qint64 virtualGetLength(const Managed *m); public: - template <typename T> + enum IndicesBehavior {Default, DisallowArrayIndex}; + template <IndicesBehavior Behavior = Default, typename T> static inline uint calculateHashValue(const T *ch, const T* end, uint *subtype) { // array indices get their number as hash value - uint h = stringToArrayIndex(ch, end); - if (h != UINT_MAX) { - if (subtype) - *subtype = Heap::StringOrSymbol::StringType_ArrayIndex; - return h; + uint h = UINT_MAX; + if constexpr (Behavior != DisallowArrayIndex) { + h = stringToArrayIndex(ch, end); + if (h != UINT_MAX) { + if (subtype) + *subtype = Heap::StringOrSymbol::StringType_ArrayIndex; + return h; + } } while (ch < end) { @@ -252,7 +235,7 @@ public: } if (subtype) - *subtype = (charToUInt(ch) == '@') ? Heap::StringOrSymbol::StringType_Symbol : Heap::StringOrSymbol::StringType_Regular; + *subtype = (ch != end && charToUInt(ch) == '@') ? Heap::StringOrSymbol::StringType_Symbol : Heap::StringOrSymbol::StringType_Regular; return h; } }; diff --git a/src/qml/jsruntime/qv4stringiterator.cpp b/src/qml/jsruntime/qv4stringiterator.cpp index 62db83ff26..9cb2711efb 100644 --- a/src/qml/jsruntime/qv4stringiterator.cpp +++ b/src/qml/jsruntime/qv4stringiterator.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Crimson AS <info@crimson.no> -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 Crimson AS <info@crimson.no> +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <private/qv4iterator_p.h> #include <private/qv4stringiterator_p.h> @@ -71,7 +35,7 @@ ReturnedValue StringIteratorPrototype::method_next(const FunctionObject *b, cons quint32 index = thisObject->d()->nextIndex; QString str = s->toQString(); - quint32 len = str.length(); + quint32 len = str.size(); if (index >= len) { thisObject->d()->iteratedString.set(scope.engine, nullptr); diff --git a/src/qml/jsruntime/qv4stringiterator_p.h b/src/qml/jsruntime/qv4stringiterator_p.h index 672ccc9963..742b8a895d 100644 --- a/src/qml/jsruntime/qv4stringiterator_p.h +++ b/src/qml/jsruntime/qv4stringiterator_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4STRINGITERATOR_P_H #define QV4STRINGITERATOR_P_H @@ -66,7 +30,7 @@ namespace Heap { Member(class, NoMark, quint32, nextIndex) DECLARE_HEAP_OBJECT(StringIteratorObject, Object) { - DECLARE_MARKOBJECTS(StringIteratorObject); + DECLARE_MARKOBJECTS(StringIteratorObject) void init(String *str, QV4::ExecutionEngine *engine) { Object::init(); diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 9b4a2d575e..5f3d833f33 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -1,47 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4stringobject_p.h" #include "qv4regexp_p.h" #include "qv4regexpobject_p.h" -#include "qv4objectproto_p.h" #include <private/qv4mm_p.h> #include "qv4scopedvalue_p.h" #include "qv4symbol_p.h" @@ -51,6 +14,7 @@ #include <QtCore/QDateTime> #include <QtCore/QDebug> #include <QtCore/QStringList> +#include <QtQml/private/qv4runtime_p.h> #include <cassert> @@ -62,10 +26,11 @@ # include "qplatformdefs.h" # endif #else -# include <windows.h> +# include <qt_windows.h> #endif using namespace QV4; +using namespace Qt::Literals::StringLiterals; DEFINE_OBJECT_VTABLE(StringObject); @@ -87,7 +52,7 @@ void Heap::StringObject::init(const QV4::String *str) Heap::String *Heap::StringObject::getIndex(uint index) const { QString str = string->toQString(); - if (index >= (uint)str.length()) + if (index >= (uint)str.size()) return nullptr; return internalClass->engine->newString(str.mid(index, 1)); } @@ -103,7 +68,7 @@ bool StringObject::virtualDeleteProperty(Managed *m, PropertyKey id) if (id.isArrayIndex()) { StringObject *o = static_cast<StringObject *>(m); uint index = id.asArrayIndex(); - if (index < static_cast<uint>(o->d()->string->toQString().length())) + if (index < static_cast<uint>(o->d()->string->toQString().size())) return false; } return Object::virtualDeleteProperty(m, id); @@ -119,7 +84,7 @@ struct StringObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator PropertyKey StringObjectOwnPropertyKeyIterator::next(const QV4::Object *o, Property *pd, PropertyAttributes *attrs) { const StringObject *s = static_cast<const StringObject *>(o); - uint slen = s->d()->string->toQString().length(); + uint slen = s->d()->string->toQString().size(); if (arrayIndex < slen) { uint index = arrayIndex; ++arrayIndex; @@ -155,7 +120,7 @@ PropertyAttributes StringObject::virtualGetOwnProperty(const Managed *m, Propert if (id.isArrayIndex()) { const uint index = id.asArrayIndex(); const auto s = static_cast<const StringObject *>(m); - if (index < uint(s->d()->string->toQString().length())) { + if (index < uint(s->d()->string->toQString().size())) { if (p) p->value = s->getIndex(index); return Attr_NotConfigurable|Attr_NotWritable; @@ -166,9 +131,9 @@ PropertyAttributes StringObject::virtualGetOwnProperty(const Managed *m, Propert DEFINE_OBJECT_VTABLE(StringCtor); -void Heap::StringCtor::init(QV4::ExecutionContext *scope) +void Heap::StringCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("String")); + Heap::FunctionObject::init(engine, QStringLiteral("String")); } ReturnedValue StringCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) @@ -208,7 +173,6 @@ ReturnedValue StringCtor::method_fromCharCode(const FunctionObject *b, const Val *ch = QChar(argv[i].toUInt16()); ++ch; } - *ch = 0; return Encode(b->engine()->newString(str)); } @@ -231,11 +195,10 @@ ReturnedValue StringCtor::method_fromCodePoint(const FunctionObject *f, const Va ++ch; *ch = QChar::lowSurrogate(cp); } else { - *ch = cp; + *ch = QChar(cp); } ++ch; } - *ch = 0; result.truncate(ch - result.constData()); return e->newString(result)->asReturnedValue(); } @@ -268,14 +231,14 @@ ReturnedValue StringCtor::method_raw(const FunctionObject *f, const Value *, con while (1) { val = raw->get(nextIndex); result += val->toQString(); - if (scope.engine->hasException) + if (scope.hasException()) return Encode::undefined(); if (nextIndex + 1 == literalSegments) return scope.engine->newString(result)->asReturnedValue(); if (nextIndex < static_cast<uint>(argc)) result += argv[nextIndex].toQString(); - if (scope.engine->hasException) + if (scope.hasException()) return Encode::undefined(); ++nextIndex; } @@ -371,12 +334,12 @@ ReturnedValue StringPrototype::method_charAt(const FunctionObject *b, const Valu if (v4->hasException) return QV4::Encode::undefined(); - int pos = 0; + double pos = 0; if (argc > 0) - pos = (int) argv[0].toInteger(); + pos = argv[0].toInteger(); QString result; - if (pos >= 0 && pos < str.length()) + if (pos >= 0 && pos < str.size()) result += str.at(pos); return Encode(v4->newString(result)); @@ -389,12 +352,12 @@ ReturnedValue StringPrototype::method_charCodeAt(const FunctionObject *b, const if (v4->hasException) return QV4::Encode::undefined(); - int pos = 0; + double pos = 0; if (argc > 0) - pos = (int) argv[0].toInteger(); + pos = argv[0].toInteger(); - if (pos >= 0 && pos < str.length()) + if (pos >= 0 && pos < str.size()) RETURN_RESULT(Encode(str.at(pos).unicode())); return Encode(qt_qnan()); @@ -407,7 +370,7 @@ ReturnedValue StringPrototype::method_codePointAt(const FunctionObject *f, const if (v4->hasException) return QV4::Encode::undefined(); - int index = argc ? argv[0].toInteger() : 0; + double index = argc ? argv[0].toInteger() : 0.0; if (v4->hasException) return QV4::Encode::undefined(); @@ -457,14 +420,14 @@ ReturnedValue StringPrototype::method_endsWith(const FunctionObject *b, const Va if (v4->hasException) return Encode::undefined(); - int pos = value.length(); + double pos = value.size(); if (argc > 1) - pos = (int) argv[1].toInteger(); + pos = argv[1].toInteger(); - if (pos == value.length()) + if (pos == value.size()) RETURN_RESULT(Encode(value.endsWith(searchString))); - QStringRef stringToSearch = value.leftRef(pos); + QStringView stringToSearch = QStringView{value}.left(pos); return Encode(stringToSearch.endsWith(searchString)); } @@ -479,13 +442,13 @@ ReturnedValue StringPrototype::method_indexOf(const FunctionObject *b, const Val if (v4->hasException) return Encode::undefined(); - int pos = 0; + double pos = 0; if (argc > 1) - pos = (int) argv[1].toInteger(); + pos = argv[1].toInteger(); int index = -1; - if (! value.isEmpty()) - index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length())); + if (!value.isEmpty()) + index = value.indexOf(searchString, qMin(qMax(pos, 0.0), double(value.size()))); return Encode(index); } @@ -503,18 +466,18 @@ ReturnedValue StringPrototype::method_includes(const FunctionObject *b, const Va if (v4->hasException) return Encode::undefined(); - int pos = 0; + double pos = 0; if (argc > 1) { const Value &posArg = argv[1]; - pos = (int) posArg.toInteger(); + pos = posArg.toInteger(); if (!posArg.isInteger() && posArg.isNumber() && qIsInf(posArg.toNumber())) - pos = value.length(); + pos = value.size(); } if (pos == 0) RETURN_RESULT(Encode(value.contains(searchString))); - QStringRef stringToSearch = value.midRef(pos); + QStringView stringToSearch = QStringView{value}.mid(pos); return Encode(stringToSearch.contains(searchString)); } @@ -533,10 +496,10 @@ ReturnedValue StringPrototype::method_lastIndexOf(const FunctionObject *b, const if (std::isnan(position)) position = +qInf(); else - position = trunc(position); + position = std::trunc(position); - int pos = trunc(qMin(qMax(position, 0.0), double(value.length()))); - if (!searchString.isEmpty() && pos == value.length()) + int pos = std::trunc(qMin(qMax(position, 0.0), double(value.size()))); + if (!searchString.isEmpty() && pos == value.size()) --pos; if (searchString.isNull() && pos == 0) RETURN_RESULT(Encode(-1)); @@ -571,7 +534,7 @@ ReturnedValue StringPrototype::method_match(const FunctionObject *b, const Value ScopedFunctionObject fo(scope, f); if (!fo) return scope.engine->throwTypeError(); - return fo->call(r, thisObject, 1); + return checkedResult(scope.engine, fo->call(r, thisObject, 1)); } } @@ -591,7 +554,7 @@ ReturnedValue StringPrototype::method_match(const FunctionObject *b, const Value ScopedFunctionObject match(scope, that->get(scope.engine->symbol_match())); if (!match) return scope.engine->throwTypeError(); - return match->call(that, s, 1); + return checkedResult(scope.engine, match->call(that, s, 1)); } ReturnedValue StringPrototype::method_normalize(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) @@ -634,10 +597,10 @@ ReturnedValue StringPrototype::method_padEnd(const FunctionObject *f, const Valu if (!argc) return s->asReturnedValue(); - int maxLen = argv[0].toInteger(); + double maxLen = argv[0].toInteger(); if (maxLen <= s->d()->length()) return s->asReturnedValue(); - QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : QString::fromLatin1(" "); + QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : u" "_s; if (v4->hasException) return Encode::undefined(); @@ -645,17 +608,17 @@ ReturnedValue StringPrototype::method_padEnd(const FunctionObject *f, const Valu return s->asReturnedValue(); QString padded = s->toQString(); - int oldLength = padded.length(); + int oldLength = padded.size(); int toFill = maxLen - oldLength; padded.resize(maxLen); QChar *ch = padded.data() + oldLength; while (toFill) { - int copy = qMin(fillString.length(), toFill); + int copy = qMin(fillString.size(), toFill); memcpy(ch, fillString.constData(), copy*sizeof(QChar)); toFill -= copy; ch += copy; } - *ch = 0; + *ch = QChar::Null; return v4->newString(padded)->asReturnedValue(); } @@ -673,10 +636,10 @@ ReturnedValue StringPrototype::method_padStart(const FunctionObject *f, const Va if (!argc) return s->asReturnedValue(); - int maxLen = argv[0].toInteger(); + double maxLen = argv[0].toInteger(); if (maxLen <= s->d()->length()) return s->asReturnedValue(); - QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : QString::fromLatin1(" "); + QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : u" "_s; if (v4->hasException) return Encode::undefined(); @@ -684,20 +647,20 @@ ReturnedValue StringPrototype::method_padStart(const FunctionObject *f, const Va return s->asReturnedValue(); QString original = s->toQString(); - int oldLength = original.length(); + int oldLength = original.size(); int toFill = maxLen - oldLength; QString padded; padded.resize(maxLen); QChar *ch = padded.data(); while (toFill) { - int copy = qMin(fillString.length(), toFill); + int copy = qMin(fillString.size(), toFill); memcpy(ch, fillString.constData(), copy*sizeof(QChar)); toFill -= copy; ch += copy; } memcpy(ch, original.constData(), oldLength*sizeof(QChar)); ch += oldLength; - *ch = 0; + *ch = QChar::Null; return v4->newString(padded)->asReturnedValue(); } @@ -720,9 +683,9 @@ ReturnedValue StringPrototype::method_repeat(const FunctionObject *b, const Valu static void appendReplacementString(QString *result, const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) { - result->reserve(result->length() + replaceValue.length()); - for (int i = 0; i < replaceValue.length(); ++i) { - if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) { + result->reserve(result->size() + replaceValue.size()); + for (int i = 0; i < replaceValue.size(); ++i) { + if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.size() - 1) { ushort ch = replaceValue.at(i + 1).unicode(); uint substStart = JSC::Yarr::offsetNoMatch; uint substEnd = JSC::Yarr::offsetNoMatch; @@ -741,12 +704,12 @@ static void appendReplacementString(QString *result, const QString &input, const skip = 1; } else if (ch == '\'') { substStart = matchOffsets[1]; - substEnd = input.length(); + substEnd = input.size(); skip = 1; } else if (ch >= '0' && ch <= '9') { uint capture = ch - '0'; skip = 1; - if (i < replaceValue.length() - 2) { + if (i < replaceValue.size() - 2) { ch = replaceValue.at(i + 2).unicode(); if (ch >= '0' && ch <= '9') { uint c = capture*10 + ch - '0'; @@ -765,7 +728,7 @@ static void appendReplacementString(QString *result, const QString &input, const } i += skip; if (substStart != JSC::Yarr::offsetNoMatch && substEnd != JSC::Yarr::offsetNoMatch) - *result += input.midRef(substStart, substEnd - substStart); + *result += QStringView{input}.mid(substStart, substEnd - substStart); else if (skip == 0) // invalid capture reference. Taken as literal value *result += replaceValue.at(i); } else { @@ -831,7 +794,7 @@ ReturnedValue StringPrototype::method_replace(const FunctionObject *b, const Val if (idx != -1) { numStringMatches = 1; matchOffsets[0] = idx; - matchOffsets[1] = idx + searchString.length(); + matchOffsets[1] = idx + searchString.size(); } } @@ -840,7 +803,7 @@ ReturnedValue StringPrototype::method_replace(const FunctionObject *b, const Val ScopedValue replaceValue(scope, argc > 1 ? argv[1] : Value::undefinedValue()); ScopedFunctionObject searchCallback(scope, replaceValue); if (!!searchCallback) { - result.reserve(string.length() + 10*numStringMatches); + result.reserve(string.size() + 10*numStringMatches); ScopedValue entry(scope); Value *arguments = scope.alloc(numCaptures + 2); int lastEnd = 0; @@ -862,14 +825,15 @@ ReturnedValue StringPrototype::method_replace(const FunctionObject *b, const Val Value that = Value::undefinedValue(); replacement = searchCallback->call(&that, arguments, numCaptures + 2); - result += string.midRef(lastEnd, matchStart - lastEnd); + CHECK_EXCEPTION(); + result += QStringView{string}.mid(lastEnd, matchStart - lastEnd); result += replacement->toQString(); lastEnd = matchEnd; } - result += string.midRef(lastEnd); + result += QStringView{string}.mid(lastEnd); } else { QString newString = replaceValue->toQString(); - result.reserve(string.length() + numStringMatches*newString.size()); + result.reserve(string.size() + numStringMatches*newString.size()); int lastEnd = 0; for (int i = 0; i < numStringMatches; ++i) { @@ -879,11 +843,11 @@ ReturnedValue StringPrototype::method_replace(const FunctionObject *b, const Val if (matchStart == JSC::Yarr::offsetNoMatch) continue; - result += string.midRef(lastEnd, matchStart - lastEnd); + result += QStringView{string}.mid(lastEnd, matchStart - lastEnd); appendReplacementString(&result, string, newString, matchOffsets + baseIndex, numCaptures); lastEnd = matchEnd; } - result += string.midRef(lastEnd); + result += QStringView{string}.mid(lastEnd); } if (matchOffsets != _matchOffsets) @@ -896,13 +860,13 @@ ReturnedValue StringPrototype::method_search(const FunctionObject *b, const Valu { Scope scope(b); QString string = getThisString(scope.engine, thisObject); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); Scoped<RegExpObject> regExp(scope, argc ? argv[0] : Value::undefinedValue()); if (!regExp) { regExp = scope.engine->regExpCtor()->callAsConstructor(argv, 1); - if (scope.engine->hasException) + if (scope.hasException()) return QV4::Encode::undefined(); Q_ASSERT(regExp); @@ -1012,7 +976,7 @@ ReturnedValue StringPrototype::method_split(const FunctionObject *b, const Value } else { QString separator = separatorValue->toQString(); if (separator.isEmpty()) { - for (uint i = 0; i < qMin(limit, uint(text.length())); ++i) + for (uint i = 0; i < qMin(limit, uint(text.size())); ++i) array->push_back((s = scope.engine->newString(text.mid(i, 1)))); return array.asReturnedValue(); } @@ -1044,14 +1008,14 @@ ReturnedValue StringPrototype::method_startsWith(const FunctionObject *b, const if (v4->hasException) return Encode::undefined(); - int pos = 0; + double pos = 0; if (argc > 1) - pos = (int) argv[1].toInteger(); + pos = argv[1].toInteger(); if (pos == 0) return Encode(value.startsWith(searchString)); - QStringRef stringToSearch = value.midRef(pos); + QStringView stringToSearch = QStringView{value}.mid(pos); RETURN_RESULT(Encode(stringToSearch.startsWith(searchString))); } @@ -1070,7 +1034,7 @@ ReturnedValue StringPrototype::method_substr(const FunctionObject *b, const Valu if (argc > 1) length = argv[1].toInteger(); - double count = value.length(); + double count = value.size(); if (start < 0) start = qMax(count + start, 0.0); @@ -1088,7 +1052,7 @@ ReturnedValue StringPrototype::method_substring(const FunctionObject *b, const V if (v4->hasException) return QV4::Encode::undefined(); - int length = value.length(); + int length = value.size(); double start = 0; double end = length; @@ -1161,11 +1125,11 @@ ReturnedValue StringPrototype::method_trim(const FunctionObject *b, const Value const QChar *chars = s.constData(); int start, end; - for (start = 0; start < s.length(); ++start) { + for (start = 0; start < s.size(); ++start) { if (!chars[start].isSpace() && chars[start].unicode() != 0xfeff) break; } - for (end = s.length() - 1; end >= start; --end) { + for (end = s.size() - 1; end >= start; --end) { if (!chars[end].isSpace() && chars[end].unicode() != 0xfeff) break; } diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index 794ee91575..73c2bd7b34 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4STRINGOBJECT_P_H #define QV4STRINGOBJECT_P_H @@ -64,7 +28,7 @@ namespace Heap { Member(class, Pointer, String *, string) DECLARE_HEAP_OBJECT(StringObject, Object) { - DECLARE_MARKOBJECTS(StringObject); + DECLARE_MARKOBJECTS(StringObject) enum { LengthPropertyIndex = 0 @@ -80,7 +44,7 @@ DECLARE_HEAP_OBJECT(StringObject, Object) { }; struct StringCtor : FunctionObject { - void init(QV4::ExecutionContext *scope); + void init(QV4::ExecutionEngine *engine); }; } diff --git a/src/qml/jsruntime/qv4symbol.cpp b/src/qml/jsruntime/qv4symbol.cpp index 004a9938e2..85ef57f680 100644 --- a/src/qml/jsruntime/qv4symbol.cpp +++ b/src/qml/jsruntime/qv4symbol.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <qv4symbol_p.h> #include <qv4functionobject_p.h> @@ -50,15 +14,14 @@ DEFINE_OBJECT_VTABLE(SymbolObject); void Heap::Symbol::init(const QString &s) { Q_ASSERT(s.at(0) == QLatin1Char('@')); - identifier = PropertyKey::fromStringOrSymbol(this); QString desc(s); - text = desc.data_ptr(); - text->ref.ref(); + StringOrSymbol::init(desc.data_ptr()); + identifier = PropertyKey::fromStringOrSymbol(internalClass->engine, this); } -void Heap::SymbolCtor::init(QV4::ExecutionContext *scope) +void Heap::SymbolCtor::init(QV4::ExecutionEngine *engine) { - Heap::FunctionObject::init(scope, QStringLiteral("Symbol")); + Heap::FunctionObject::init(engine, QStringLiteral("Symbol")); } void Heap::SymbolObject::init(const QV4::Symbol *s) @@ -88,7 +51,7 @@ ReturnedValue SymbolCtor::virtualCallAsConstructor(const FunctionObject *f, cons ReturnedValue SymbolCtor::method_for(const FunctionObject *f, const Value *, const Value *argv, int argc) { Scope scope(f); - ScopedValue k(scope, argc ? argv[0]: Value::undefinedValue()); + ScopedValue k(scope, argc ? argv[0] : Value::undefinedValue()); ScopedString key(scope, k->toString(scope.engine)); if (scope.hasException()) return Encode::undefined(); @@ -183,5 +146,5 @@ Heap::Symbol *Symbol::create(ExecutionEngine *e, const QString &s) QString Symbol::descriptiveString() const { - return QLatin1String("Symbol(") + toQString().midRef(1) + QLatin1String(")"); + return QLatin1String("Symbol(") + QStringView{toQString()}.mid(1) + QLatin1String(")"); } diff --git a/src/qml/jsruntime/qv4symbol_p.h b/src/qml/jsruntime/qv4symbol_p.h index c7e12b512b..29a0189b69 100644 --- a/src/qml/jsruntime/qv4symbol_p.h +++ b/src/qml/jsruntime/qv4symbol_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4_SYMBOL_H #define QV4_SYMBOL_H @@ -61,7 +25,7 @@ namespace QV4 { namespace Heap { struct SymbolCtor : FunctionObject { - void init(QV4::ExecutionContext *scope); + void init(ExecutionEngine *engine); }; struct Symbol : StringOrSymbol { @@ -72,7 +36,7 @@ struct Symbol : StringOrSymbol { Member(class, Pointer, Symbol *, symbol) DECLARE_HEAP_OBJECT(SymbolObject, Object) { - DECLARE_MARKOBJECTS(SymbolObject); + DECLARE_MARKOBJECTS(SymbolObject) void init(const QV4::Symbol *s); }; diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index 7d33167762..9c752f43bb 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -1,47 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4typedarray_p.h" #include "qv4arrayiterator_p.h" #include "qv4arraybuffer_p.h" -#include "qv4string_p.h" -#include "qv4jscall_p.h" #include "qv4symbol_p.h" #include "qv4runtime_p.h" #include <QtCore/qatomic.h> @@ -63,7 +25,7 @@ static inline int toInt32(Value v) Q_ASSERT(v.isNumber()); if (v.isInteger()) return v.integerValue(); - return Double::toInt32(v.doubleValue()); + return QJSNumberCoercion::toInteger(v.doubleValue()); } static inline double toDouble(Value v) @@ -274,9 +236,9 @@ const TypedArrayOperations operations[NTypedArrayTypes] = { }; -void Heap::TypedArrayCtor::init(QV4::ExecutionContext *scope, TypedArray::Type t) +void Heap::TypedArrayCtor::init(QV4::ExecutionEngine *engine, TypedArray::Type t) { - Heap::FunctionObject::init(scope, QLatin1String(operations[t].name)); + Heap::FunctionObject::init(engine, QLatin1String(operations[t].name)); type = t; } @@ -296,18 +258,21 @@ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, if (!argc || !argv[0].isObject()) { // ECMA 6 22.2.1.1 - qint64 l = argc ? argv[0].toIndex() : 0; - if (scope.engine->hasException) + const double l = argc ? argv[0].toInteger() : 0; + if (scope.hasException()) return Encode::undefined(); - // ### lift UINT_MAX restriction - if (l < 0 || l > UINT_MAX) + if (l < 0 || l > std::numeric_limits<int>::max()) + return scope.engine->throwRangeError(QLatin1String("Index out of range.")); + + const double byteLength = l * operations[that->d()->type].bytesPerElement; + + // TODO: This is an artificial restriction due to the fact that we store the byteLength in + // uint below. We should allow up to INT_MAX elements of any size. + if (byteLength > std::numeric_limits<uint>::max()) return scope.engine->throwRangeError(QLatin1String("Index out of range.")); - uint len = (uint)l; - if (l != len) - scope.engine->throwRangeError(QStringLiteral("Non integer length for typed array.")); - uint byteLength = len * operations[that->d()->type].bytesPerElement; - Scoped<ArrayBuffer> buffer(scope, scope.engine->newArrayBuffer(byteLength)); - if (scope.engine->hasException) + + Scoped<ArrayBuffer> buffer(scope, scope.engine->newArrayBuffer(size_t(byteLength))); + if (scope.hasException()) return Encode::undefined(); Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type)); @@ -322,15 +287,15 @@ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, if (!!typedArray) { // ECMA 6 22.2.1.2 Scoped<ArrayBuffer> buffer(scope, typedArray->d()->buffer); - if (!buffer || buffer->isDetachedBuffer()) + if (!buffer || buffer->hasDetachedArrayData()) return scope.engine->throwTypeError(); - uint srcElementSize = typedArray->d()->type->bytesPerElement; + uint srcElementSize = typedArray->bytesPerElement(); uint destElementSize = operations[that->d()->type].bytesPerElement; - uint byteLength = typedArray->d()->byteLength; + uint byteLength = typedArray->byteLength(); uint destByteLength = byteLength*destElementSize/srcElementSize; Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(destByteLength)); - if (scope.engine->hasException) + if (scope.hasException()) return Encode::undefined(); Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type)); @@ -338,8 +303,8 @@ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, array->d()->byteLength = destByteLength; array->d()->byteOffset = 0; - const char *src = buffer->d()->data->data() + typedArray->d()->byteOffset; - char *dest = newBuffer->d()->data->data(); + const char *src = buffer->constArrayData() + typedArray->byteOffset(); + char *dest = newBuffer->arrayData(); // check if src and new type have the same size. In that case we can simply memcpy the data if (srcElementSize == destElementSize) { @@ -365,27 +330,27 @@ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, double dbyteOffset = argc > 1 ? argv[1].toInteger() : 0; - if (buffer->isDetachedBuffer()) + if (buffer->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint byteOffset = (uint)dbyteOffset; uint elementSize = operations[that->d()->type].bytesPerElement; - if (dbyteOffset < 0 || (byteOffset % elementSize) || dbyteOffset > buffer->byteLength()) + if (dbyteOffset < 0 || (byteOffset % elementSize) || dbyteOffset > buffer->arrayDataLength()) return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid byteOffset")); uint byteLength; if (argc < 3 || argv[2].isUndefined()) { - byteLength = buffer->byteLength() - byteOffset; - if (buffer->byteLength() < byteOffset || byteLength % elementSize) + byteLength = buffer->arrayDataLength() - byteOffset; + if (buffer->arrayDataLength() < byteOffset || byteLength % elementSize) return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); } else { double l = qBound(0., argv[2].toInteger(), (double)UINT_MAX); - if (scope.engine->hasException) + if (scope.hasException()) return Encode::undefined(); - if (buffer->isDetachedBuffer()) + if (buffer->hasDetachedArrayData()) return scope.engine->throwTypeError(); l *= elementSize; - if (buffer->byteLength() - byteOffset < l) + if (buffer->arrayDataLength() - byteOffset < l) return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); byteLength = (uint)l; } @@ -403,15 +368,15 @@ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, ScopedObject o(scope, argc ? argv[0] : Value::undefinedValue()); uint l = (uint) qBound(0., ScopedValue(scope, o->get(scope.engine->id_length()))->toInteger(), (double)UINT_MAX); - if (scope.engine->hasException) + if (scope.hasException()) return scope.engine->throwTypeError(); uint elementSize = operations[that->d()->type].bytesPerElement; size_t bufferSize; - if (mul_overflow(size_t(l), size_t(elementSize), &bufferSize)) + if (qMulOverflow(size_t(l), size_t(elementSize), &bufferSize)) return scope.engine->throwRangeError(QLatin1String("new TypedArray: invalid length")); Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(bufferSize)); - if (scope.engine->hasException) + if (scope.hasException()) return Encode::undefined(); Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type)); @@ -420,15 +385,15 @@ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, array->d()->byteOffset = 0; uint idx = 0; - char *b = newBuffer->d()->data->data(); + char *b = newBuffer->arrayData(); ScopedValue val(scope); while (idx < l) { val = o->get(idx); val = val->convertedToNumber(); - if (scope.engine->hasException) + if (scope.hasException()) return Encode::undefined(); array->d()->type->write(b, val); - if (scope.engine->hasException) + if (scope.hasException()) return Encode::undefined(); ++idx; b += elementSize; @@ -465,7 +430,7 @@ ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Val Scope scope(static_cast<const Object *>(m)->engine()); Scoped<TypedArray> a(scope, static_cast<const TypedArray *>(m)); - if (a->d()->buffer->isDetachedBuffer()) + if (a->hasDetachedArrayData()) return scope.engine->throwTypeError(); if (!isArrayIndex || id.asArrayIndex() >= a->length()) { @@ -474,13 +439,13 @@ ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Val return Encode::undefined(); } - uint bytesPerElement = a->d()->type->bytesPerElement; - uint byteOffset = a->d()->byteOffset + id.asArrayIndex() * bytesPerElement; - Q_ASSERT(byteOffset + bytesPerElement <= (uint)a->d()->buffer->byteLength()); + uint bytesPerElement = a->bytesPerElement(); + uint byteOffset = a->byteOffset() + id.asArrayIndex() * bytesPerElement; + Q_ASSERT(byteOffset + bytesPerElement <= a->arrayDataLength()); if (hasProperty) *hasProperty = true; - return a->d()->type->read(a->d()->buffer->data->data() + byteOffset); + return a->d()->type->read(a->constArrayData() + byteOffset); } bool TypedArray::virtualHasProperty(const Managed *m, PropertyKey id) @@ -490,7 +455,7 @@ bool TypedArray::virtualHasProperty(const Managed *m, PropertyKey id) return Object::virtualHasProperty(m, id); const TypedArray *a = static_cast<const TypedArray *>(m); - if (a->d()->buffer->isDetachedBuffer()) { + if (a->hasDetachedArrayData()) { a->engine()->throwTypeError(); return false; } @@ -521,7 +486,7 @@ bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Valu Scope scope(v4); Scoped<TypedArray> a(scope, static_cast<TypedArray *>(m)); - if (a->d()->buffer->isDetachedBuffer()) + if (a->hasDetachedArrayData()) return scope.engine->throwTypeError(); if (!isArrayIndex) @@ -531,14 +496,14 @@ bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Valu if (index >= a->length()) return false; - uint bytesPerElement = a->d()->type->bytesPerElement; - uint byteOffset = a->d()->byteOffset + index * bytesPerElement; - Q_ASSERT(byteOffset + bytesPerElement <= (uint)a->d()->buffer->byteLength()); + uint bytesPerElement = a->bytesPerElement(); + uint byteOffset = a->byteOffset() + index * bytesPerElement; + Q_ASSERT(byteOffset + bytesPerElement <= a->arrayDataLength()); Value v = Value::fromReturnedValue(value.convertedToNumber()); - if (scope.hasException() || a->d()->buffer->isDetachedBuffer()) + if (scope.hasException() || a->hasDetachedArrayData()) return scope.engine->throwTypeError(); - a->d()->type->write(a->d()->buffer->data->data() + byteOffset, v); + a->d()->type->write(a->arrayData() + byteOffset, v); return true; } @@ -564,12 +529,12 @@ bool TypedArray::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Prop ExecutionEngine *engine = a->engine(); Value v = Value::fromReturnedValue(p->value.convertedToNumber()); - if (engine->hasException || a->d()->buffer->isDetachedBuffer()) + if (engine->hasException || a->hasDetachedArrayData()) return engine->throwTypeError(); - uint bytesPerElement = a->d()->type->bytesPerElement; - uint byteOffset = a->d()->byteOffset + index * bytesPerElement; - Q_ASSERT(byteOffset + bytesPerElement <= (uint)a->d()->buffer->byteLength()); - a->d()->type->write(a->d()->buffer->data->data() + byteOffset, v); + uint bytesPerElement = a->bytesPerElement(); + uint byteOffset = a->byteOffset() + index * bytesPerElement; + Q_ASSERT(byteOffset + bytesPerElement <= a->arrayDataLength()); + a->d()->type->write(a->arrayData() + byteOffset, v); } return true; } @@ -638,10 +603,10 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteLength(const Function if (!v) return v4->throwTypeError(); - if (v->d()->buffer->isDetachedBuffer()) + if (v->hasDetachedArrayData()) return Encode(0); - return Encode(v->d()->byteLength); + return Encode(v->byteLength()); } ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int) @@ -651,10 +616,10 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteOffset(const Function if (!v) return v4->throwTypeError(); - if (v->d()->buffer->isDetachedBuffer()) + if (v->hasDetachedArrayData()) return Encode(0); - return Encode(v->d()->byteOffset); + return Encode(v->byteOffset()); } ReturnedValue IntrinsicTypedArrayPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int) @@ -664,67 +629,66 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_get_length(const FunctionObje if (!v) return v4->throwTypeError(); - if (v->d()->buffer->isDetachedBuffer()) + if (v->hasDetachedArrayData()) return Encode(0); - return Encode(v->d()->byteLength/v->d()->type->bytesPerElement); + return Encode(v->length()); } ReturnedValue IntrinsicTypedArrayPrototype::method_copyWithin(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) { Scope scope(f); - Scoped<TypedArray> O(scope, thisObject); - if (!O || O->d()->buffer->isDetachedBuffer()) + Scoped<TypedArray> instance(scope, thisObject); + if (!instance || instance->hasDetachedArrayData()) return scope.engine->throwTypeError(); if (!argc) - return O->asReturnedValue(); + return instance->asReturnedValue(); - qint64 len = static_cast<uint>(O->length()); + const double len = instance->length(); + Q_ASSERT(std::isfinite(len)); - qint64 to = static_cast<qint64>(argv[0].toInteger()); - if (to < 0) - to = qMax(len + to, 0ll); - else - to = qMin(to, len); + const double target = argv[0].toInteger(); - qint64 from = (argc > 1) ? static_cast<qint64>(argv[1].toInteger()) : 0ll; - if (from < 0) - from = qMax(len + from, 0ll); - else - from = qMin(from, len); - - double fend = argv[2].toInteger(); - if (fend > len) - fend = len; - qint64 end = (argc > 2 && !argv[2].isUndefined()) ? static_cast<qint64>(fend) : len; - if (end < 0) - end = qMax(len + end, 0ll); - else - end = qMin(end, len); + const double start = (argc > 1) + ? argv[1].toInteger() + : 0; - qint64 count = qMin(end - from, len - to); + const double end = (argc > 2 && !argv[2].isUndefined()) + ? argv[2].toInteger() + : len; - if (count <= 0) - return O->asReturnedValue(); + const double fin = end < 0 + ? std::max(len + end, 0.0) + : std::min(end, len); - if (O->d()->buffer->isDetachedBuffer()) - return scope.engine->throwTypeError(); + const qsizetype from = start < 0 + ? std::max(len + start, 0.0) + : std::min(start, len); + + const qsizetype to = target < 0 + ? std::max(len + target, 0.0) + : std::min(target, len); + + const qsizetype count = std::min(fin - from, len - to); + + if (count <= 0) + return instance->asReturnedValue(); if (from != to) { - int elementSize = O->d()->type->bytesPerElement; - char *data = O->d()->buffer->data->data() + O->d()->byteOffset; - memmove(data + to*elementSize, data + from*elementSize, count*elementSize); + int elementSize = instance->bytesPerElement(); + char *data = instance->arrayData() + instance->byteOffset(); + memmove(data + to * elementSize, data + from * elementSize, count * elementSize); } - return O->asReturnedValue(); + return instance->asReturnedValue(); } ReturnedValue IntrinsicTypedArrayPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int) { Scope scope(b); Scoped<TypedArray> v(scope, thisObject); - if (!v || v->d()->buffer->isDetachedBuffer()) + if (!v || v->hasDetachedArrayData()) return scope.engine->throwTypeError(); Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v)); @@ -736,7 +700,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_every(const FunctionObject *b { Scope scope(b); Scoped<TypedArray> v(scope, thisObject); - if (!v || v->d()->buffer->isDetachedBuffer()) + if (!v || v->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint len = v->length(); @@ -749,13 +713,13 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_every(const FunctionObject *b ScopedValue r(scope); Value *arguments = scope.alloc(3); - const char *data = v->d()->buffer->data->data(); - uint bytesPerElement = v->d()->type->bytesPerElement; - uint byteOffset = v->d()->byteOffset; + const char *data = v->constArrayData(); + uint bytesPerElement = v->bytesPerElement(); + uint byteOffset = v->byteOffset(); bool ok = true; for (uint k = 0; ok && k < len; ++k) { - if (v->d()->buffer->isDetachedBuffer()) + if (v->hasDetachedArrayData()) return scope.engine->throwTypeError(); arguments[0] = v->d()->type->read(data + byteOffset + k * bytesPerElement); @@ -763,6 +727,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_every(const FunctionObject *b arguments[1] = Value::fromDouble(k); arguments[2] = v; r = callback->call(that, arguments, 3); + CHECK_EXCEPTION(); ok = r->toBoolean(); } return Encode(ok); @@ -772,7 +737,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_fill(const FunctionObject *b, { Scope scope(b); Scoped<TypedArray> v(scope, thisObject); - if (!v || v->d()->buffer->isDetachedBuffer()) + if (!v || v->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint len = v->length(); @@ -797,14 +762,20 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_fill(const FunctionObject *b, fin = static_cast<uint>(std::min(relativeEnd, dlen)); } - double val = argc ? argv[0].toNumber() : std::numeric_limits<double>::quiet_NaN(); - Value value = Value::fromDouble(val); - if (scope.hasException() || v->d()->buffer->isDetachedBuffer()) + if (scope.hasException() || v->hasDetachedArrayData()) return scope.engine->throwTypeError(); - char *data = v->d()->buffer->data->data(); - uint bytesPerElement = v->d()->type->bytesPerElement; - uint byteOffset = v->d()->byteOffset; + char *data = v->arrayData(); + uint bytesPerElement = v->bytesPerElement(); + uint byteOffset = v->byteOffset(); + + Value value; + if (!argc) + value.setDouble(std::numeric_limits<double>::quiet_NaN()); + else if (argv[0].isNumber()) + value = argv[0]; + else + value.setDouble(argv[0].toNumber()); while (k < fin) { v->d()->type->write(data + byteOffset + k * bytesPerElement, value); @@ -825,7 +796,7 @@ static TypedArray *typedArraySpeciesCreate(Scope &scope, const TypedArray *insta Value *arguments = scope.alloc(1); arguments[0] = Encode(len); Scoped<TypedArray> a(scope, constructor->callAsConstructor(arguments, 1)); - if (!a || a->d()->buffer->isDetachedBuffer() || a->length() < len) { + if (!a || a->hasDetachedArrayData() || a->length() < len) { scope.engine->throwTypeError(); return nullptr; } @@ -836,7 +807,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_filter(const FunctionObject * { Scope scope(b); Scoped<TypedArray> instance(scope, thisObject); - if (!instance || instance->d()->buffer->isDetachedBuffer()) + if (!instance || instance->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint len = instance->length(); @@ -852,7 +823,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_filter(const FunctionObject * uint to = 0; for (uint k = 0; k < len; ++k) { - if (instance->d()->buffer->isDetachedBuffer()) + if (instance->hasDetachedArrayData()) return scope.engine->throwTypeError(); bool exists; arguments[0] = instance->get(k, &exists); @@ -862,6 +833,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_filter(const FunctionObject * arguments[1] = Value::fromDouble(k); arguments[2] = instance; selected = callback->call(that, arguments, 3); + CHECK_EXCEPTION(); if (selected->toBoolean()) { ++arguments; scope.alloc(1); @@ -883,7 +855,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_find(const FunctionObject *b, { Scope scope(b); Scoped<TypedArray> v(scope, thisObject); - if (!v || v->d()->buffer->isDetachedBuffer()) + if (!v || v->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint len = v->length(); @@ -898,7 +870,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_find(const FunctionObject *b, ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue()); for (uint k = 0; k < len; ++k) { - if (v->d()->buffer->isDetachedBuffer()) + if (v->hasDetachedArrayData()) return scope.engine->throwTypeError(); arguments[0] = v->get(k); CHECK_EXCEPTION(); @@ -919,7 +891,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_findIndex(const FunctionObjec { Scope scope(b); Scoped<TypedArray> v(scope, thisObject); - if (!v || v->d()->buffer->isDetachedBuffer()) + if (!v || v->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint len = v->length(); @@ -934,7 +906,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_findIndex(const FunctionObjec ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue()); for (uint k = 0; k < len; ++k) { - if (v->d()->buffer->isDetachedBuffer()) + if (v->hasDetachedArrayData()) return scope.engine->throwTypeError(); arguments[0] = v->get(k); CHECK_EXCEPTION(); @@ -955,7 +927,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_forEach(const FunctionObject { Scope scope(b); Scoped<TypedArray> v(scope, thisObject); - if (!v || v->d()->buffer->isDetachedBuffer()) + if (!v || v->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint len = v->length(); @@ -968,7 +940,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_forEach(const FunctionObject Value *arguments = scope.alloc(3); for (uint k = 0; k < len; ++k) { - if (v->d()->buffer->isDetachedBuffer()) + if (v->hasDetachedArrayData()) return scope.engine->throwTypeError(); bool exists; arguments[0] = v->get(k, &exists); @@ -987,7 +959,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_includes(const FunctionObject { Scope scope(b); Scoped<TypedArray> v(scope, thisObject); - if (!v || v->d()->buffer->isDetachedBuffer()) + if (!v || v->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint len = v->length(); @@ -1025,7 +997,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_indexOf(const FunctionObject { Scope scope(b); Scoped<TypedArray> v(scope, thisObject); - if (!v || v->d()->buffer->isDetachedBuffer()) + if (!v || v->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint len = v->length(); @@ -1068,58 +1040,51 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_indexOf(const FunctionObject return Encode(-1); } -ReturnedValue IntrinsicTypedArrayPrototype::method_join(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +ReturnedValue IntrinsicTypedArrayPrototype::method_join( + const FunctionObject *functionObject, const Value *thisObject, const Value *argv, int argc) { - Scope scope(b); - Scoped<TypedArray> v(scope, thisObject); - if (!v || v->d()->buffer->isDetachedBuffer()) + Scope scope(functionObject); + Scoped<TypedArray> typedArray(scope, thisObject); + if (!typedArray || typedArray->hasDetachedArrayData()) return scope.engine->throwTypeError(); - uint len = v->length(); - - ScopedValue arg(scope, argc ? argv[0] : Value::undefinedValue()); + // We cannot optimize the resolution of the argument away if length is 0. + // It may have side effects. + ScopedValue argument(scope, argc ? argv[0] : Value::undefinedValue()); + const QString separator = argument->isUndefined() + ? QStringLiteral(",") + : argument->toQString(); - QString r4; - if (arg->isUndefined()) - r4 = QStringLiteral(","); - else - r4 = arg->toQString(); - - const quint32 r2 = len; - - if (!r2) + const quint32 length = typedArray->length(); + if (!length) return Encode(scope.engine->newString()); - QString R; + QString result; - // - // crazy! - // ScopedString name(scope, scope.engine->newString(QStringLiteral("0"))); - ScopedValue r6(scope, v->get(name)); - if (!r6->isNullOrUndefined()) - R = r6->toQString(); + ScopedValue value(scope, typedArray->get(name)); + if (!value->isNullOrUndefined()) + result = value->toQString(); - ScopedValue r12(scope); - for (quint32 k = 1; k < r2; ++k) { - R += r4; + for (quint32 i = 1; i < length; ++i) { + result += separator; - name = Value::fromDouble(k).toString(scope.engine); - r12 = v->get(name); + name = Value::fromDouble(i).toString(scope.engine); + value = typedArray->get(name); CHECK_EXCEPTION(); - if (!r12->isNullOrUndefined()) - R += r12->toQString(); + if (!value->isNullOrUndefined()) + result += value->toQString(); } - return Encode(scope.engine->newString(R)); + return Encode(scope.engine->newString(result)); } ReturnedValue IntrinsicTypedArrayPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int) { Scope scope(b); Scoped<TypedArray> v(scope, thisObject); - if (!v || v->d()->buffer->isDetachedBuffer()) + if (!v || v->hasDetachedArrayData()) return scope.engine->throwTypeError(); Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v)); @@ -1132,7 +1097,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_lastIndexOf(const FunctionObj { Scope scope(b); Scoped<TypedArray> instance(scope, thisObject); - if (!instance || instance->d()->buffer->isDetachedBuffer()) + if (!instance || instance->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint len = instance->length(); @@ -1175,7 +1140,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_map(const FunctionObject *b, { Scope scope(b); Scoped<TypedArray> instance(scope, thisObject); - if (!instance || instance->d()->buffer->isDetachedBuffer()) + if (!instance || instance->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint len = instance->length(); @@ -1194,13 +1159,14 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_map(const FunctionObject *b, Value *arguments = scope.alloc(3); for (uint k = 0; k < len; ++k) { - if (instance->d()->buffer->isDetachedBuffer()) + if (instance->hasDetachedArrayData()) return scope.engine->throwTypeError(); arguments[0] = instance->get(k); arguments[1] = Value::fromDouble(k); arguments[2] = instance; mapped = callback->call(that, arguments, 3); + CHECK_EXCEPTION(); a->put(k, mapped); } return a->asReturnedValue(); @@ -1210,7 +1176,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_reduce(const FunctionObject * { Scope scope(b); Scoped<TypedArray> instance(scope, thisObject); - if (!instance || instance->d()->buffer->isDetachedBuffer()) + if (!instance || instance->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint len = instance->length(); @@ -1240,7 +1206,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_reduce(const FunctionObject * Value *arguments = scope.alloc(4); while (k < len) { - if (instance->d()->buffer->isDetachedBuffer()) + if (instance->hasDetachedArrayData()) return scope.engine->throwTypeError(); bool kPresent; v = instance->get(k, &kPresent); @@ -1250,6 +1216,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_reduce(const FunctionObject * arguments[2] = Value::fromDouble(k); arguments[3] = instance; acc = callback->call(nullptr, arguments, 4); + CHECK_EXCEPTION(); } ++k; } @@ -1260,7 +1227,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_reduceRight(const FunctionObj { Scope scope(b); Scoped<TypedArray> instance(scope, thisObject); - if (!instance || instance->d()->buffer->isDetachedBuffer()) + if (!instance || instance->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint len = instance->length(); @@ -1295,7 +1262,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_reduceRight(const FunctionObj Value *arguments = scope.alloc(4); while (k > 0) { - if (instance->d()->buffer->isDetachedBuffer()) + if (instance->hasDetachedArrayData()) return scope.engine->throwTypeError(); bool kPresent; v = instance->get(k - 1, &kPresent); @@ -1305,6 +1272,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_reduceRight(const FunctionObj arguments[2] = Value::fromDouble(k - 1); arguments[3] = instance; acc = callback->call(nullptr, arguments, 4); + CHECK_EXCEPTION(); } --k; } @@ -1315,7 +1283,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_reverse(const FunctionObject { Scope scope(b); Scoped<TypedArray> instance(scope, thisObject); - if (!instance || instance->d()->buffer->isDetachedBuffer()) + if (!instance || instance->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint length = instance->length(); @@ -1342,7 +1310,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_some(const FunctionObject *b, { Scope scope(b); Scoped<TypedArray> instance(scope, thisObject); - if (!instance || instance->d()->buffer->isDetachedBuffer()) + if (!instance || instance->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint len = instance->length(); @@ -1356,7 +1324,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_some(const FunctionObject *b, Value *arguments = scope.alloc(3); for (uint k = 0; k < len; ++k) { - if (instance->d()->buffer->isDetachedBuffer()) + if (instance->hasDetachedArrayData()) return scope.engine->throwTypeError(); bool exists; arguments[0] = instance->get(k, &exists); @@ -1366,6 +1334,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_some(const FunctionObject *b, arguments[1] = Value::fromDouble(k); arguments[2] = instance; result = callback->call(that, arguments, 3); + CHECK_EXCEPTION(); if (result->toBoolean()) return Encode(true); } @@ -1377,7 +1346,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_values(const FunctionObject * { Scope scope(b); Scoped<TypedArray> v(scope, thisObject); - if (!v || v->d()->buffer->isDetachedBuffer()) + if (!v || v->hasDetachedArrayData()) return scope.engine->throwTypeError(); Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v)); @@ -1394,45 +1363,46 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b, Scoped<ArrayBuffer> buffer(scope, a->d()->buffer); double doffset = argc >= 2 ? argv[1].toInteger() : 0; - if (scope.engine->hasException) + if (scope.hasException()) RETURN_UNDEFINED(); - if (!buffer || buffer->isDetachedBuffer()) + if (!buffer || buffer->hasDetachedArrayData()) return scope.engine->throwTypeError(); if (doffset < 0 || doffset >= UINT_MAX) RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range"))); uint offset = (uint)doffset; - uint elementSize = a->d()->type->bytesPerElement; + uint elementSize = a->bytesPerElement(); Scoped<TypedArray> srcTypedArray(scope, argv[0]); if (!srcTypedArray) { // src is a regular object ScopedObject o(scope, argv[0].toObject(scope.engine)); - if (scope.engine->hasException || !o) + if (scope.hasException() || !o) return scope.engine->throwTypeError(); double len = ScopedValue(scope, o->get(scope.engine->id_length()))->toNumber(); uint l = (uint)len; - if (scope.engine->hasException || l != len) + if (scope.hasException() || l != len) return scope.engine->throwTypeError(); - if (offset + l > a->length()) + const uint aLength = a->length(); + if (offset > aLength || l > aLength - offset) RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range"))); uint idx = 0; - if (buffer->isDetachedBuffer()) + if (buffer->hasDetachedArrayData()) return scope.engine->throwTypeError(); - char *b = buffer->d()->data->data() + a->d()->byteOffset + offset*elementSize; + char *b = buffer->arrayData() + a->byteOffset() + offset*elementSize; ScopedValue val(scope); while (idx < l) { val = o->get(idx); if (scope.hasException()) return Encode::undefined(); val = val->convertedToNumber(); - if (scope.hasException() || buffer->isDetachedBuffer()) + if (scope.hasException() || buffer->hasDetachedArrayData()) return scope.engine->throwTypeError(); a->d()->type->write(b, val); - if (scope.engine->hasException) + if (scope.hasException()) RETURN_UNDEFINED(); ++idx; b += elementSize; @@ -1442,31 +1412,33 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b, // src is a typed array Scoped<ArrayBuffer> srcBuffer(scope, srcTypedArray->d()->buffer); - if (!srcBuffer || srcBuffer->isDetachedBuffer()) + if (!srcBuffer || srcBuffer->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint l = srcTypedArray->length(); - if (offset + l > a->length()) + + const uint aLength = a->length(); + if (offset > aLength || l > aLength - offset) RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range"))); - char *dest = buffer->d()->data->data() + a->d()->byteOffset + offset*elementSize; - const char *src = srcBuffer->d()->data->data() + srcTypedArray->d()->byteOffset; + char *dest = buffer->arrayData() + a->byteOffset() + offset*elementSize; + const char *src = srcBuffer->d()->constArrayData() + srcTypedArray->byteOffset(); if (srcTypedArray->d()->type == a->d()->type) { // same type of typed arrays, use memmove (as srcbuffer and buffer could be the same) - memmove(dest, src, srcTypedArray->d()->byteLength); + memmove(dest, src, srcTypedArray->byteLength()); RETURN_UNDEFINED(); } char *srcCopy = nullptr; if (buffer->d() == srcBuffer->d()) { // same buffer, need to take a temporary copy, to not run into problems - srcCopy = new char[srcTypedArray->d()->byteLength]; - memcpy(srcCopy, src, srcTypedArray->d()->byteLength); + srcCopy = new char[srcTypedArray->byteLength()]; + memcpy(srcCopy, src, srcTypedArray->byteLength()); src = srcCopy; } // typed arrays of different kind, need to manually loop - uint srcElementSize = srcTypedArray->d()->type->bytesPerElement; + uint srcElementSize = srcTypedArray->bytesPerElement(); TypedArrayOperations::Read read = srcTypedArray->d()->type->read; TypedArrayOperations::Write write = a->d()->type->write; for (uint i = 0; i < l; ++i) { @@ -1485,7 +1457,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_slice(const FunctionObject *b { Scope scope(b); Scoped<TypedArray> instance(scope, thisObject); - if (!instance || instance->d()->buffer->isDetachedBuffer()) + if (!instance || instance->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint len = instance->length(); @@ -1517,10 +1489,10 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_slice(const FunctionObject *b ScopedValue v(scope); uint n = 0; for (uint i = start; i < end; ++i) { - if (instance->d()->buffer->isDetachedBuffer()) + if (instance->hasDetachedArrayData()) return scope.engine->throwTypeError(); v = instance->get(i); - if (a->d()->buffer->isDetachedBuffer()) + if (a->hasDetachedArrayData()) return scope.engine->throwTypeError(); a->put(n, v); ++n; @@ -1552,7 +1524,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject if (end < begin) end = begin; - if (scope.engine->hasException) + if (scope.hasException()) RETURN_UNDEFINED(); int newLen = end - begin; @@ -1563,10 +1535,10 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject Value *arguments = scope.alloc(3); arguments[0] = buffer; - arguments[1] = Encode(a->d()->byteOffset + begin*a->d()->type->bytesPerElement); + arguments[1] = Encode(a->byteOffset() + begin * a->bytesPerElement()); arguments[2] = Encode(newLen); a = constructor->callAsConstructor(arguments, 3); - if (!a || a->d()->buffer->isDetachedBuffer()) + if (!a || a->hasDetachedArrayData()) return scope.engine->throwTypeError(); return a->asReturnedValue(); } @@ -1575,7 +1547,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_toLocaleString(const Function { Scope scope(b); Scoped<TypedArray> instance(scope, thisObject); - if (!instance || instance->d()->buffer->isDetachedBuffer()) + if (!instance || instance->hasDetachedArrayData()) return scope.engine->throwTypeError(); uint len = instance->length(); @@ -1586,14 +1558,29 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_toLocaleString(const Function ScopedValue v(scope); ScopedString s(scope); + ScopedPropertyKey tolocaleString(scope, scope.engine->id_toLocaleString()->toPropertyKey()); + Q_ASSERT(!scope.engine->hasException); + for (uint k = 0; k < len; ++k) { - if (instance->d()->buffer->isDetachedBuffer()) + if (instance->hasDetachedArrayData()) return scope.engine->throwTypeError(); if (k) R += separator; v = instance->get(k); - v = Runtime::CallElement::call(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0); + Q_ASSERT(!v->isNullOrUndefined()); // typed array cannot hold null or undefined + + ScopedObject valueAsObject(scope, v->toObject(scope.engine)); + Q_ASSERT(valueAsObject); // only null or undefined cannot be converted to object + + ScopedFunctionObject function(scope, valueAsObject->get(tolocaleString)); + if (!function) + return scope.engine->throwTypeError(); + + v = function->call(valueAsObject, nullptr, 0); + if (scope.hasException()) + return Encode::undefined(); + s = v->toString(scope.engine); if (scope.hasException()) return Encode::undefined(); @@ -1617,7 +1604,7 @@ static bool validateTypedArray(const Object *o) const TypedArray *a = o->as<TypedArray>(); if (!a) return false; - if (a->d()->buffer->isDetachedBuffer()) + if (a->hasDetachedArrayData()) return false; return true; } @@ -1648,6 +1635,157 @@ ReturnedValue IntrinsicTypedArrayCtor::method_of(const FunctionObject *f, const return newObj->asReturnedValue(); } +ReturnedValue IntrinsicTypedArrayCtor::method_from(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(f); + ScopedObject itemsObject(scope, argv[0]); + bool usingIterator = false; + + ScopedFunctionObject mapfn(scope, Value::undefinedValue()); + Value *mapArguments = nullptr; + if (argc > 1) { + mapfn = ScopedFunctionObject(scope, argv[1]); + if (!mapfn) + return scope.engine->throwTypeError(QString::fromLatin1("%1 is not a function").arg(argv[1].toQStringNoThrow())); + mapArguments = scope.alloc(2); + } + + // Iterator validity check goes after map function validity has been checked. + if (itemsObject) { + // If the object claims to support iterators, then let's try use them. + ScopedValue it(scope, itemsObject->get(scope.engine->symbol_iterator())); + CHECK_EXCEPTION(); + if (!it->isNullOrUndefined()) { + ScopedFunctionObject itfunc(scope, it); + if (!itfunc) + return scope.engine->throwTypeError(); + usingIterator = true; + } + } + + ScopedValue thisArg(scope); + if (argc > 2) + thisArg = argv[2]; + + const FunctionObject *C = thisObject->as<FunctionObject>(); + + if (usingIterator) { + // Item iteration supported, so let's go ahead and try use that. + CHECK_EXCEPTION(); + + qint64 iterableLength = 0; + Value *nextValue = scope.alloc(1); + ScopedValue done(scope); + + ScopedObject lengthIterator(scope, Runtime::GetIterator::call(scope.engine, itemsObject, true)); + CHECK_EXCEPTION(); // symbol_iterator threw; whoops. + if (!lengthIterator) { + return scope.engine->throwTypeError(); // symbol_iterator wasn't an object. + } + + forever { + // Here we calculate the length of the iterable range. + if (iterableLength > (static_cast<qint64>(1) << 53) - 1) { + ScopedValue error(scope, scope.engine->throwTypeError()); + return Runtime::IteratorClose::call(scope.engine, lengthIterator); + } + // Retrieve the next value. If the iteration ends, we're done here. + done = Value::fromReturnedValue(Runtime::IteratorNext::call(scope.engine, lengthIterator, nextValue)); + if (scope.hasException()) + return Runtime::IteratorClose::call(scope.engine, lengthIterator); + if (done->toBoolean()) { + break; + } + iterableLength++; + } + + // Constructor validity check goes after we have calculated the length, because that calculation can throw + // errors that are not type errors and at least the tests expect those rather than type errors. + if (!C || !C->isConstructor()) + return scope.engine->throwTypeError(); + + ScopedObject iterator(scope, Runtime::GetIterator::call(scope.engine, itemsObject, true)); + CHECK_EXCEPTION(); // symbol_iterator can throw. + if (!iterator) { + return scope.engine->throwTypeError(); // symbol_iterator wasn't an object. + } + + ScopedObject a(scope, Value::undefinedValue()); + ScopedValue ctorArgument(scope, Value::fromReturnedValue(QV4::Encode(int(iterableLength)))); + a = C->callAsConstructor(ctorArgument, 1); + CHECK_EXCEPTION(); + + // We check exceptions above, and only after doing so, check the array's validity after construction. + if (!::validateTypedArray(a) || (a->getLength() < iterableLength)) + return scope.engine->throwTypeError(); + + + // The loop below traverses the iterator, and puts elements into the created array. + ScopedValue mappedValue(scope, Value::undefinedValue()); + for (qint64 k = 0; k < iterableLength; ++k) { + done = Value::fromReturnedValue(Runtime::IteratorNext::call(scope.engine, iterator, nextValue)); + if (scope.hasException()) + return Runtime::IteratorClose::call(scope.engine, iterator); + + if (mapfn) { + mapArguments[0] = *nextValue; + mapArguments[1] = Value::fromDouble(k); + mappedValue = mapfn->call(thisArg, mapArguments, 2); + if (scope.hasException()) + return Runtime::IteratorClose::call(scope.engine, iterator); + } else { + mappedValue = *nextValue; + } + + a->put(k, mappedValue); + if (scope.hasException()) + return Runtime::IteratorClose::call(scope.engine, iterator); + } + return a.asReturnedValue(); + } else { + // Array-like fallback. We request elements by index, and put them into the created array. + ScopedObject arrayLike(scope, argv[0].toObject(scope.engine)); + if (!arrayLike) + return scope.engine->throwTypeError(QString::fromLatin1("Cannot convert %1 to object").arg(argv[0].toQStringNoThrow())); + + int len = arrayLike->getLength(); + CHECK_EXCEPTION(); + + // Getting the length may throw, and must do so before we check the constructor validity. + if (!C || !C->isConstructor()) + return scope.engine->throwTypeError(); + + ScopedObject a(scope, Value::undefinedValue()); + ScopedValue ctorArgument(scope, Value::fromReturnedValue(QV4::Encode(len))); + a = C->callAsConstructor(ctorArgument, 1); + CHECK_EXCEPTION(); + + // We check exceptions above, and only after doing so, check the array's validity after construction. + if (!::validateTypedArray(a) || (a->getLength() < len)) + return scope.engine->throwTypeError(); + + ScopedValue mappedValue(scope, Value::undefinedValue()); + ScopedValue kValue(scope); + for (int k = 0; k < len; ++k) { + kValue = arrayLike->get(k); + CHECK_EXCEPTION(); + + if (mapfn) { + mapArguments[0] = kValue; + mapArguments[1] = Value::fromDouble(k); + mappedValue = mapfn->call(thisArg, mapArguments, 2); + CHECK_EXCEPTION(); + } else { + mappedValue = kValue; + } + + a->put(k, mappedValue); + CHECK_EXCEPTION(); + } + return a.asReturnedValue(); + } +} + void IntrinsicTypedArrayPrototype::init(ExecutionEngine *engine, IntrinsicTypedArrayCtor *ctor) { Scope scope(engine); @@ -1657,6 +1795,8 @@ void IntrinsicTypedArrayPrototype::init(ExecutionEngine *engine, IntrinsicTypedA ctor->defineReadonlyConfigurableProperty(engine->id_name(), s); s = scope.engine->newString(QStringLiteral("of")); ctor->defineDefaultProperty(s, IntrinsicTypedArrayCtor::method_of); + s = scope.engine->newString(QStringLiteral("from")); + ctor->defineDefaultProperty(s, IntrinsicTypedArrayCtor::method_from, 1); ctor->addSymbolSpecies(); defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr); diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index 64792f23a2..50db9610c7 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4TYPEDARRAY_H #define QV4TYPEDARRAY_H @@ -116,7 +80,7 @@ namespace Heap { Member(class, NoMark, uint, arrayType) DECLARE_HEAP_OBJECT(TypedArray, Object) { - DECLARE_MARKOBJECTS(TypedArray); + DECLARE_MARKOBJECTS(TypedArray) using Type = TypedArrayType; void init(Type t); @@ -126,7 +90,7 @@ struct IntrinsicTypedArrayCtor : FunctionObject { }; struct TypedArrayCtor : FunctionObject { - void init(QV4::ExecutionContext *scope, TypedArray::Type t); + void init(ExecutionEngine *engine, TypedArray::Type t); TypedArray::Type type; }; @@ -142,25 +106,24 @@ struct TypedArrayPrototype : Object { } -struct Q_QML_PRIVATE_EXPORT TypedArray : Object +struct Q_QML_EXPORT TypedArray : Object { V4_OBJECT2(TypedArray, Object) static Heap::TypedArray *create(QV4::ExecutionEngine *e, Heap::TypedArray::Type t); - uint byteLength() const { - return d()->byteLength; - } + uint byteOffset() const noexcept { return d()->byteOffset; } + uint byteLength() const noexcept { return d()->byteLength; } + int bytesPerElement() const noexcept { return d()->type->bytesPerElement; } + uint length() const noexcept { return d()->byteLength / d()->type->bytesPerElement; } - uint length() const { - return d()->byteLength/d()->type->bytesPerElement; - } + char *arrayData() noexcept { return d()->buffer->arrayData(); } + const char *constArrayData() const noexcept { return d()->buffer->constArrayData(); } + bool hasDetachedArrayData() const noexcept { return d()->buffer->hasDetachedArrayData(); } + uint arrayDataLength() const noexcept { return d()->buffer->arrayDataLength(); } - QTypedArrayData<char> *arrayData() { - return d()->buffer->data; - } - - Heap::TypedArray::Type arrayType() const { + Heap::TypedArray::Type arrayType() const noexcept + { return static_cast<Heap::TypedArray::Type>(d()->arrayType); } using Object::get; @@ -178,9 +141,8 @@ struct IntrinsicTypedArrayCtor: FunctionObject { V4_OBJECT2(IntrinsicTypedArrayCtor, FunctionObject) - static constexpr VTable::Call virtualCall = nullptr; - static ReturnedValue method_of(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_from(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; struct TypedArrayCtor: FunctionObject diff --git a/src/qml/jsruntime/qv4urlobject.cpp b/src/qml/jsruntime/qv4urlobject.cpp new file mode 100644 index 0000000000..89c8b9cda2 --- /dev/null +++ b/src/qml/jsruntime/qv4urlobject.cpp @@ -0,0 +1,1539 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qv4arrayiterator_p.h" +#include "qv4urlobject_p.h" + +#include <QtCore/QUrl> + +#include <qv4jscall_p.h> +#include <qv4objectiterator_p.h> + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(UrlObject); +DEFINE_OBJECT_VTABLE(UrlCtor); + +DEFINE_OBJECT_VTABLE(UrlSearchParamsObject); +DEFINE_OBJECT_VTABLE(UrlSearchParamsCtor); + + +void Heap::UrlCtor::init(QV4::ExecutionEngine *engine) +{ + Heap::FunctionObject::init(engine, QLatin1String("URL")); +} + +void UrlPrototype::init(ExecutionEngine *engine, Object *ctor) +{ + Q_UNUSED(ctor); + + Scope scope(engine); + ScopedObject o(scope); + + defineDefaultProperty(QLatin1String("toString"), method_getHref); + defineDefaultProperty(QLatin1String("toJSON"), method_getHref); + + defineAccessorProperty(QLatin1String("hash"), method_getHash, method_setHash); + defineAccessorProperty(QLatin1String("host"), method_getHost, method_setHost); + defineAccessorProperty(QLatin1String("hostname"), method_getHostname, method_setHostname); + defineAccessorProperty(QLatin1String("href"), method_getHref, method_setHref); + defineAccessorProperty(QLatin1String("origin"), method_getOrigin, nullptr); + defineAccessorProperty(QLatin1String("password"), method_getPassword, method_setPassword); + defineAccessorProperty(QLatin1String("pathname"), method_getPathname, method_setPathname); + defineAccessorProperty(QLatin1String("port"), method_getPort, method_setPort); + defineAccessorProperty(QLatin1String("protocol"), method_getProtocol, method_setProtocol); + defineAccessorProperty(QLatin1String("search"), method_getSearch, method_setSearch); + defineAccessorProperty(QLatin1String("searchParams"), method_getSearchParams, nullptr); + defineAccessorProperty(QLatin1String("username"), method_getUsername, method_setUsername); +} + +bool UrlObject::setHash(QString hash) +{ + if (hash.startsWith(QLatin1Char('#'))) + hash = hash.mid(1); + + QUrl url = toQUrl(); + url.setFragment(hash); + + if (!url.isValid()) + return false; + + d()->hash.set(engine(), engine()->newString(url.fragment())); + d()->href.set(engine(), engine()->newString(url.toString())); + + return true; +} + +bool UrlObject::setHostname(QString host) +{ + QUrl url = toQUrl(); + url.setHost(host); + + if (!url.isValid()) + return false; + + d()->hostname.set(engine(), engine()->newString(url.host())); + d()->href.set(engine(), engine()->newString(url.toString())); + + updateOrigin(); + updateHost(); + + return true; +} + +bool UrlObject::setHost(QString hostname) +{ + int port = -1; + + if (hostname.contains(QLatin1Char(':'))) { + const QStringList list = hostname.split(QLatin1Char(':')); + hostname = list[0]; + port = list[1].toInt(); + } + + QUrl url = toQUrl(); + url.setHost(hostname); + url.setPort(port); + + if (!url.isValid()) + return false; + + if (url.port() != -1) + d()->port.set(engine(), engine()->newString(QString::number(url.port()))); + + d()->hostname.set(engine(), engine()->newString(url.host())); + d()->href.set(engine(), engine()->newString(url.toString())); + + updateOrigin(); + updateHost(); + + return true; +} + +bool UrlObject::setHref(QString href) +{ + const QUrl url(href); + if (!url.isValid() || url.isRelative()) + return false; + + setUrl(url); + return true; +} + +void UrlObject::setUrl(const QUrl &url) +{ + d()->hash.set(engine(), engine()->newString(url.fragment())); + d()->hostname.set(engine(), engine()->newString(url.host())); + d()->href.set(engine(), engine()->newString(url.toString(QUrl::ComponentFormattingOptions(QUrl::ComponentFormattingOption::FullyEncoded)))); + d()->password.set(engine(), engine()->newString(url.password())); + d()->pathname.set(engine(), engine()->newString(url.path())); + d()->port.set(engine(), + engine()->newString(url.port() == -1 ? QLatin1String("") + : QString::number(url.port()))); + d()->protocol.set(engine(), engine()->newString(url.scheme() + QLatin1Char(':'))); + d()->search.set(engine(), engine()->newString(url.query(QUrl::ComponentFormattingOptions(QUrl::ComponentFormattingOption::FullyEncoded)))); + d()->username.set(engine(), engine()->newString(url.userName())); + + updateOrigin(); + updateHost(); +} + +bool UrlObject::setPassword(QString password) +{ + QUrl url = toQUrl(); + url.setPassword(password); + + if (!url.isValid()) + return false; + + d()->password.set(engine(), engine()->newString(url.password())); + d()->href.set(engine(), engine()->newString(url.toString())); + + return true; +} + +bool UrlObject::setPathname(QString pathname) +{ + QUrl url = toQUrl(); + url.setPath(pathname); + + if (!url.isValid()) + return false; + + d()->pathname.set(engine(), engine()->newString(url.path())); + d()->href.set(engine(), engine()->newString(url.toString())); + + return true; +} + +bool UrlObject::setPort(QString port) +{ + QUrl url = toQUrl(); + url.setPort(port.isEmpty() ? -1 : port.toInt()); + + if (!url.isValid()) + return false; + + d()->port.set(engine(), + engine()->newString(url.port() == -1 ? QLatin1String("") + : QString::number(url.port()))); + d()->href.set(engine(), engine()->newString(url.toString())); + + updateOrigin(); + updateHost(); + + return true; +} + +bool UrlObject::setProtocol(QString protocolOrScheme) +{ + QUrl url = toQUrl(); + // If there is one or several ':' in the protocolOrScheme, + // everything from the first colon is removed. + + qsizetype firstColonPos = protocolOrScheme.indexOf(QLatin1Char(':')); + + if (firstColonPos != -1) + protocolOrScheme.truncate(firstColonPos); + + url.setScheme(protocolOrScheme); + + if (!url.isValid()) + return false; + + d()->protocol.set(engine(), engine()->newString(url.scheme() + QLatin1Char(':'))); + d()->href.set(engine(), engine()->newString(url.toString())); + + updateOrigin(); + updateHost(); + + return true; +} + +bool UrlObject::setSearch(QString search) +{ + QUrl url = toQUrl(); + + if (search.startsWith(QLatin1Char('?'))) + search = search.mid(1); + + url.setQuery(search); + + if (!url.isValid()) + return false; + + d()->search.set(engine(), engine()->newString(url.query())); + d()->href.set(engine(), engine()->newString(url.toString())); + + return true; +} + +bool UrlObject::setUsername(QString username) +{ + QUrl url = toQUrl(); + url.setUserName(username); + + if (!url.isValid()) + return false; + + d()->username.set(engine(), engine()->newString(url.userName())); + d()->href.set(engine(), engine()->newString(url.toString())); + + return true; +} + +QString UrlObject::search() const +{ + auto url = QUrl(href()); + if (auto url = QUrl(href()); !url.hasQuery() || url.query().isEmpty()) + return QLatin1String(""); + + constexpr auto options = QUrl::ComponentFormattingOption::EncodeSpaces + | QUrl::ComponentFormattingOption::EncodeUnicode + | QUrl::ComponentFormattingOption::EncodeReserved; + return u'?' + url.query(options); +} + +QUrl UrlObject::toQUrl() const +{ + return QUrl(href()); +} + +void UrlObject::updateOrigin() +{ + QUrl url = toQUrl(); + + QString proto = url.scheme(); + + // A blob's origin is the origin of the URL that it points to + if (proto == QLatin1String("blob")) { + url = QUrl(url.path()); + proto = url.scheme(); + } + + QString origin; + if (proto == QLatin1String("http") || proto == QLatin1String("https") + || proto == QLatin1String("ftp")) { + origin = QLatin1String("%1://%2").arg(url.scheme(), url.host()); + + if (url.port() != -1) + origin.append(QLatin1String(":") + QString::number(url.port())); + } + + d()->origin.set(engine(), engine()->newString(origin)); +} + +void UrlObject::updateHost() +{ + QUrl url = toQUrl(); + + QString host = url.host(); + + if (url.port() != -1) + host.append(QLatin1String(":") + QString::number(url.port())); + + d()->host.set(engine(), engine()->newString(host)); +} + +static bool checkUrlObjectType(ExecutionEngine *v4, const Scoped<UrlObject> &r) +{ + if (r) + return true; + + v4->throwTypeError(QStringLiteral("Value of \"this\" must be of type URL")); + return false; +} + +ReturnedValue UrlPrototype::method_getHash(const FunctionObject *b, const Value *thisObject, + const Value *, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + return Encode(v4->newString(r->hash())); +} + +ReturnedValue UrlPrototype::method_setHash(const FunctionObject *b, const Value *thisObject, + const Value *argv, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + ScopedValue arg(scope, argv[0]); + String *stringValue = arg->stringValue(); + + if (stringValue == nullptr) + return v4->throwTypeError(QLatin1String("Invalid parameter provided")); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + r->setHash(stringValue->toQString()); + + return Encode::undefined(); +} + +ReturnedValue UrlPrototype::method_getHost(const FunctionObject *b, const Value *thisObject, + const Value *, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + return Encode(v4->newString(r->host())); +} + +ReturnedValue UrlPrototype::method_setHost(const FunctionObject *b, const Value *thisObject, + const Value *argv, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + ScopedValue arg(scope, argv[0]); + String *stringValue = arg->stringValue(); + + if (stringValue == nullptr) + return v4->throwTypeError(QLatin1String("Invalid parameter provided")); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + QString host = stringValue->toQString(); + if (!r->setHost(host)) + return v4->throwTypeError(QLatin1String("Invalid host: %1").arg(host)); + + return Encode::undefined(); +} + +ReturnedValue UrlPrototype::method_getHostname(const FunctionObject *b, const Value *thisObject, + const Value *, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + return Encode(v4->newString(r->hostname())); +} + +ReturnedValue UrlPrototype::method_setHostname(const FunctionObject *b, const Value *thisObject, + const Value *argv, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + ScopedValue arg(scope, argv[0]); + String *stringValue = arg->stringValue(); + + if (stringValue == nullptr) + return v4->throwTypeError(QLatin1String("Invalid parameter provided")); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + QString hostname = stringValue->toQString(); + if (!r->setHostname(hostname)) + return v4->throwTypeError(QLatin1String("Invalid hostname: %1").arg(hostname)); + + return Encode::undefined(); +} + +ReturnedValue UrlPrototype::method_getHref(const FunctionObject *b, const Value *thisObject, + const Value *, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + return Encode(v4->newString(r->href())); +} + +ReturnedValue UrlPrototype::method_setHref(const FunctionObject *b, const Value *thisObject, + const Value *argv, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + ScopedValue arg(scope, argv[0]); + String *stringValue = arg->stringValue(); + + if (stringValue == nullptr) + return v4->throwTypeError(QLatin1String("Invalid parameter provided")); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + QString href = stringValue->toQString(); + if (!r->setHref(href)) + return v4->throwTypeError(QLatin1String("Invalid URL: %1").arg(href)); + + return Encode::undefined(); +} + +ReturnedValue UrlPrototype::method_getOrigin(const FunctionObject *b, const Value *thisObject, + const Value *, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + return Encode(v4->newString(r->origin())); +} + +ReturnedValue UrlPrototype::method_getPassword(const FunctionObject *b, const Value *thisObject, + const Value *, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + return Encode(v4->newString(r->password())); +} + +ReturnedValue UrlPrototype::method_setPassword(const FunctionObject *b, const Value *thisObject, + const Value *argv, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + ScopedValue arg(scope, argv[0]); + String *stringValue = arg->stringValue(); + + if (stringValue == nullptr) + return v4->throwTypeError(QLatin1String("Invalid parameter provided")); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + r->setPassword(stringValue->toQString()); + + return Encode::undefined(); +} + +ReturnedValue UrlPrototype::method_getPathname(const FunctionObject *b, const Value *thisObject, + const Value *, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + return Encode(v4->newString(r->pathname())); +} + +ReturnedValue UrlPrototype::method_setPathname(const FunctionObject *b, const Value *thisObject, + const Value *argv, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + ScopedValue arg(scope, argv[0]); + String *stringValue = arg->stringValue(); + + if (stringValue == nullptr) + return v4->throwTypeError(QLatin1String("Invalid parameter provided")); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + r->setPathname(stringValue->toQString()); + + return Encode::undefined(); +} + +ReturnedValue UrlPrototype::method_getPort(const FunctionObject *b, const Value *thisObject, + const Value *, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + return Encode(v4->newString(r->port())); +} + +ReturnedValue UrlPrototype::method_setPort(const FunctionObject *b, const Value *thisObject, + const Value *argv, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + ScopedValue arg(scope, argv[0]); + String *stringValue = arg->stringValue(); + + QString port; + + if (stringValue != nullptr) + port = stringValue->toQString(); + else if (arg->isInt32()) + port = QString::number(arg->toInt32()); + else + return v4->throwTypeError(QLatin1String("Invalid parameter provided")); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + if (!r->setPort(port)) + return v4->throwTypeError(QLatin1String("Invalid port: %1").arg(port)); + + return Encode::undefined(); +} + +ReturnedValue UrlPrototype::method_getProtocol(const FunctionObject *b, const Value *thisObject, + const Value *, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + return Encode(v4->newString(r->protocol())); +} + +ReturnedValue UrlPrototype::method_setProtocol(const FunctionObject *b, const Value *thisObject, + const Value *argv, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + ScopedValue arg(scope, argv[0]); + String *stringValue = arg->stringValue(); + + if (stringValue == nullptr) + return v4->throwTypeError(QLatin1String("Invalid parameter provided")); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + r->setProtocol(stringValue->toQString()); + + return Encode::undefined(); +} + +ReturnedValue UrlPrototype::method_getSearch(const FunctionObject *b, const Value *thisObject, + const Value *, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + return Encode(v4->newString(r->search())); +} + +ReturnedValue UrlPrototype::method_setSearch(const FunctionObject *b, const Value *thisObject, + const Value *argv, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + ScopedValue arg(scope, argv[0]); + String *stringValue = arg->stringValue(); + + if (stringValue == nullptr) + return v4->throwTypeError(QLatin1String("Invalid parameter provided")); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + r->setSearch(stringValue->toQString()); + + return Encode::undefined(); +} + +ReturnedValue UrlPrototype::method_getUsername(const FunctionObject *b, const Value *thisObject, + const Value *, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + return Encode(v4->newString(r->username())); +} + +ReturnedValue UrlPrototype::method_setUsername(const FunctionObject *b, const Value *thisObject, + const Value *argv, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + ScopedValue arg(scope, argv[0]); + String *stringValue = arg->stringValue(); + + if (stringValue == nullptr) + return v4->throwTypeError(QLatin1String("Invalid parameter provided")); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + r->setUsername(stringValue->toQString()); + + return Encode::undefined(); +} + +ReturnedValue UrlPrototype::method_getSearchParams(const FunctionObject *b, const Value *thisObject, + const Value *, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + Scoped<UrlObject> r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); + + Scoped<UrlSearchParamsObject> usp(scope, v4->newUrlSearchParamsObject()); + + usp->setUrlObject(thisObject->as<UrlObject>()); + usp->initializeParams(r->search()); + + return usp->asReturnedValue(); +} + +ReturnedValue UrlCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, + int argc, const Value *newTarget) +{ + ExecutionEngine *v4 = that->engine(); + + if (argc < 1 || argc > 2) + return v4->throwError(QLatin1String("Invalid amount of arguments")); + + Scope scope(v4); + + ScopedValue arg1(scope, argv[0]); + + QString arg1String = arg1->toQString(); + QString urlString; + + if (argc == 2) { + ScopedValue arg2(scope, argv[1]); + String *arg2StringValue = arg2->stringValue(); + + if (arg2StringValue == nullptr) + return v4->throwTypeError(QLatin1String("Invalid parameter provided")); + + QUrl url = QUrl(arg2StringValue->toQString()); + QUrl relativeUrl = QUrl(arg1String); + + QString baseUrlPath = url.path(); + QString relativePath = relativeUrl.path(); + + // If the base URL contains a path the last section of it is discarded + int lastSlash = baseUrlPath.lastIndexOf(QLatin1Char('/')); + if (lastSlash != -1) + baseUrlPath.truncate(lastSlash); + + if (!relativePath.startsWith(QLatin1Char('/'))) + relativePath = relativePath.prepend(QLatin1Char('/')); + + url.setPath(baseUrlPath + relativePath); + url.setFragment(relativeUrl.fragment()); + url.setQuery(relativeUrl.query()); + + urlString = url.toString(); + } else { + urlString = arg1String; + } + + ReturnedValue o = Encode(v4->newUrlObject()); + + if (!newTarget) + return o; + + ScopedObject obj(scope, o); + obj->setProtoFromNewTarget(newTarget); + + UrlObject *urlObject = obj->as<UrlObject>(); + + if (!urlObject->setHref(urlString)) + return v4->throwTypeError(QLatin1String("Invalid URL: %1").arg(urlString)); + + return obj->asReturnedValue(); +} + + +void Heap::UrlSearchParamsCtor::init(QV4::ExecutionEngine *engine) +{ + Heap::FunctionObject::init(engine, QLatin1String("URLSearchParams")); +} + +void UrlSearchParamsPrototype::init(ExecutionEngine *engine, Object *ctor) +{ + Q_UNUSED(ctor); + + Scope scope(engine); + ScopedObject o(scope); + + defineDefaultProperty(QLatin1String("toString"), method_toString); + defineDefaultProperty(QLatin1String("sort"), method_sort); + defineDefaultProperty(QLatin1String("append"), method_append); + defineDefaultProperty(QLatin1String("delete"), method_delete); + defineDefaultProperty(QLatin1String("has"), method_has); + defineDefaultProperty(QLatin1String("set"), method_set); + defineDefaultProperty(QLatin1String("get"), method_get); + defineDefaultProperty(QLatin1String("getAll"), method_getAll); + defineDefaultProperty(QLatin1String("forEach"), method_forEach); + defineDefaultProperty(QLatin1String("entries"), method_entries); + defineDefaultProperty(QLatin1String("keys"), method_keys); + defineDefaultProperty(QLatin1String("values"), method_values); +} + +ReturnedValue UrlSearchParamsCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, + int argc, const Value *newTarget) +{ + ExecutionEngine *v4 = that->engine(); + + if (argc > 1) + return v4->throwError(QLatin1String("Invalid amount of arguments")); + + Scope scope(v4); + + ScopedValue arg(scope, argv[0]); + ArrayObject *argArrayObject = arg->as<ArrayObject>(); + Object *argObject = arg->as<Object>(); + + ReturnedValue o = Encode(v4->newUrlSearchParamsObject()); + + if (!newTarget) + return o; + + ScopedObject obj(scope, o); + obj->setProtoFromNewTarget(newTarget); + + UrlSearchParamsObject *urlSearchParamsObject = obj->as<UrlSearchParamsObject>(); + + if (argArrayObject != nullptr) { + ScopedArrayObject argArray(scope, argArrayObject); + + uint len = argArray->getLength(); + + for (uint i = 0; i < len; i++) { + QV4::Value pair = argArray->get(i); + auto *pairArrayObject = pair.as<ArrayObject>(); + + if (pairArrayObject == nullptr) { + return v4->throwTypeError( + QLatin1String("element %1 is not a pair").arg(QString::number(i))); + } + + + ScopedArrayObject pairArray(scope, pairArrayObject); + + + uint pairLen = pairArray->getLength(); + + + if (pairLen != 2) { + return v4->throwTypeError(QLatin1String("pair %1 has %2 elements instead of 2") + .arg(QString::number(i)) + .arg(QString::number(pairLen))); + } + } + + urlSearchParamsObject->initializeParams(argArray); + } else if (argObject != nullptr) { + ScopedObject scopedObject(scope, argObject); + urlSearchParamsObject->initializeParams(scopedObject); + } else { + QString value = argc > 0 ? arg->toQString() : QLatin1String(""); + urlSearchParamsObject->initializeParams(value); + } + + return obj->asReturnedValue(); +} + +void UrlSearchParamsObject::initializeParams() +{ + auto *arrayObject = engine()->newArrayObject(0); + auto *keys = engine()->newArrayObject(0); + auto *values = engine()->newArrayObject(0); + + d()->params.set(engine(), arrayObject); + d()->keys.set(engine(), keys); + d()->values.set(engine(), values); +} + +void UrlSearchParamsObject::initializeParams(QString value) +{ + Q_ASSERT(d()->params == nullptr); + + initializeParams(); + + if (value.startsWith(QLatin1Char('?'))) + value = value.mid(1); + + const QStringList params = value.split(QLatin1Char('&')); + + for (const QString& param : params) { + if (param.isEmpty()) + continue; + + QString key, value; + + int equalsIndex = param.indexOf(QLatin1Char('=')); + if (equalsIndex != -1) { + key = param.left(equalsIndex); + value = param.mid(equalsIndex+1); + } else { + key = param; + } + + append(engine()->newString(key), engine()->newString(value)); + } +} + +void UrlSearchParamsObject::initializeParams(ScopedArrayObject& params) +{ + Q_ASSERT(d()->params == nullptr); + + Scope scope(engine()); + + uint len = params->getLength(); + auto *keys = engine()->newArrayObject(len); + auto *values = engine()->newArrayObject(len); + + ScopedArrayObject scopedKeys(scope, keys); + ScopedArrayObject scopedValues(scope, values); + + for (uint i = 0; i < len; i++) + { + QV4::Value pair = params->get(i); + auto *pairArrayObject = pair.as<ArrayObject>(); + + QV4::Value key = pairArrayObject->get(uint(0)); + QV4::Value value = pairArrayObject->get(uint(1)); + + scopedKeys->put(i, key); + scopedValues->put(i, value); + } + + + d()->params.set(engine(), params->d()); + d()->keys.set(engine(), keys); + d()->values.set(engine(), values); +} + +void UrlSearchParamsObject::initializeParams(ScopedObject& params) +{ + Q_ASSERT(d()->params == nullptr); + + initializeParams(); + + Scope scope(engine()); + ObjectIterator it(scope, params, ObjectIterator::EnumerableOnly); + + ScopedValue name(scope); + ScopedValue val(scope); + + while (true) { + name = it.nextPropertyNameAsString(val); + if (name->isNull()) + break; + + Heap::String *nameStr = name->as<String>()->d(); + Heap::String *valStr = val->toString(engine()); + + append(nameStr, valStr); + } +} + +void UrlSearchParamsObject::setParams(QList<QStringList> params) +{ + auto *arrayObject = engine()->newArrayObject(0); + auto *keys = engine()->newArrayObject(0); + auto *values = engine()->newArrayObject(0); + + Scope scope(engine()); + + ScopedArrayObject scopedArray(scope, arrayObject); + + ScopedArrayObject scopedKeys(scope, keys); + ScopedArrayObject scopedValues(scope, values); + + uint len = 0; + + for (const QStringList& param : params) { + + auto *valuePair = engine()->newArrayObject(2); + + ScopedArrayObject valuePairObject(scope, valuePair); + + ScopedValue key(scope, Value::fromHeapObject(engine()->newString(param[0]))); + ScopedValue value(scope, Value::fromHeapObject(engine()->newString(param[1]))); + valuePairObject->put(uint(0), key); + valuePairObject->put(uint(1), value); + + scopedKeys->put(len, key); + scopedValues->put(len, value); + + scopedArray->put(len, valuePairObject); + len++; + } + + d()->params.set(engine(), arrayObject); + d()->keys.set(engine(), keys); + d()->values.set(engine(), values); +} + +void UrlSearchParamsObject::setUrlObject(const UrlObject *url) +{ + d()->url.set(engine(), url->d()); +} + +void UrlSearchParamsObject::append(Heap::String *name, Heap::String *value) +{ + Scope scope(engine()); + + ScopedArrayObject scopedArray(scope, d()->params); + ScopedArrayObject scopedKeys(scope, d()->keys); + ScopedArrayObject scopedValues(scope, d()->values); + + auto *valuePair = engine()->newArrayObject(2); + + ScopedArrayObject valuePairObject(scope, valuePair); + + ScopedValue keyScoped(scope, Value::fromHeapObject(name)); + ScopedValue valueScoped(scope, Value::fromHeapObject(value)); + valuePairObject->put(uint(0), keyScoped); + valuePairObject->put(uint(1), valueScoped); + + uint len = scopedArray->getLength(); + + scopedKeys->put(len, keyScoped); + scopedValues->put(len, valueScoped); + + scopedArray->put(len, valuePairObject); +} + +QList<QStringList> UrlSearchParamsObject::params() const +{ + auto *arrayObject = d()->params.get(); + Scope scope(engine()); + ScopedArrayObject scopedArray(scope, arrayObject); + + QList<QStringList> result; + + uint len = scopedArray->getLength(); + + for (uint i = 0; i < len; i++) { + QV4::Value pair = scopedArray->get(i); + auto *pairArrayObject = pair.as<ArrayObject>(); + + QV4::Value key = pairArrayObject->get(uint(0)); + QV4::Value value = pairArrayObject->get(uint(1)); + + result << QStringList { key.toQString(), value.toQString() }; + } + + return result; +} + +Heap::UrlObject *UrlSearchParamsObject::urlObject() const +{ + return d()->url.get(); +} + +QString UrlSearchParamsObject::searchString() const +{ + QString search = QLatin1String(""); + auto params = this->params(); + auto len = params.size(); + for (int i = 0; i < len; ++i) { + const QStringList ¶m = params[i]; + search += param[0] + QLatin1Char('=') + param[1]; + if (i != len - 1) + search += QLatin1Char('&'); + } + return search; +} + +int UrlSearchParamsObject::length() const +{ + auto *arrayObject = d()->params.get(); + Scope scope(engine()); + ScopedArrayObject scopedArray(scope, arrayObject); + + return scopedArray->getLength(); +} + +int UrlSearchParamsObject::indexOf(QString name, int last) const +{ + auto *arrayObject = d()->params.get(); + Scope scope(engine()); + ScopedArrayObject scopedArray(scope, arrayObject); + + int len = scopedArray->getLength(); + + for (int i = last + 1; i < len; i++) { + QV4::Value pair = scopedArray->get(i); + auto *pairArrayObject = pair.as<ArrayObject>(); + + QV4::Value key = pairArrayObject->get(uint(0)); + + if (key.toQString() == name) + return i; + } + + return -1; +} + +QString UrlSearchParamsObject::stringAt(int index, int pairIndex) const +{ + auto *arrayObject = d()->params.get(); + Scope scope(engine()); + ScopedArrayObject scopedArray(scope, arrayObject); + + if (index >= scopedArray->getLength()) + return {}; + + QV4::Value pair = scopedArray->get(index); + auto *pairArrayObject = pair.as<ArrayObject>(); + + QV4::Value value = pairArrayObject->get(pairIndex); + + return value.toQString(); +} + +QV4::Heap::String * UrlSearchParamsObject::stringAtRaw(int index, int pairIndex) const +{ + auto *arrayObject = d()->params.get(); + Scope scope(engine()); + ScopedArrayObject scopedArray(scope, arrayObject); + + if (index >= scopedArray->getLength()) + return nullptr; + + QV4::Value pair = scopedArray->get(index); + auto *pairArrayObject = pair.as<ArrayObject>(); + + QV4::Value value = pairArrayObject->get(pairIndex); + + return value.as<String>()->d(); +} + +QString UrlSearchParamsObject::nameAt(int index) const +{ + return stringAt(index, 0); +} + +QV4::Heap::String * UrlSearchParamsObject::nameAtRaw(int index) const +{ + return stringAtRaw(index, 0); +} + + +QString UrlSearchParamsObject::valueAt(int index) const +{ + return stringAt(index, 1); +} + +QV4::Heap::String * UrlSearchParamsObject::valueAtRaw(int index) const +{ + return stringAtRaw(index, 1); +} + + +struct UrlSearchParamsObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator +{ + ~UrlSearchParamsObjectOwnPropertyKeyIterator() override = default; + PropertyKey next(const QV4::Object *o, Property *pd = nullptr, + PropertyAttributes *attrs = nullptr) override; +}; + +PropertyKey UrlSearchParamsObjectOwnPropertyKeyIterator::next(const QV4::Object *o, Property *pd, + PropertyAttributes *attrs) +{ + const UrlSearchParamsObject *usp = static_cast<const UrlSearchParamsObject *>(o); + + Scope scope(usp); + + uint len = usp->length(); + if (arrayIndex < len) { + uint index = arrayIndex; + ++arrayIndex; + if (attrs) + *attrs = Attr_NotConfigurable | Attr_NotWritable; + if (pd) + pd->value = usp->engine()->newString(usp->nameAt(index)); + return PropertyKey::fromArrayIndex(index); + } + + return ObjectOwnPropertyKeyIterator::next(o, pd, attrs); +} + +OwnPropertyKeyIterator *UrlSearchParamsObject::virtualOwnPropertyKeys(const Object *m, + Value *target) +{ + *target = *m; + return new UrlSearchParamsObjectOwnPropertyKeyIterator; +} + +PropertyAttributes UrlSearchParamsObject::virtualGetOwnProperty(const Managed *m, PropertyKey id, + Property *p) +{ + PropertyAttributes attributes = Object::virtualGetOwnProperty(m, id, p); + if (attributes != Attr_Invalid) + return attributes; + + if (id.isArrayIndex()) { + const int index = id.asArrayIndex(); + const auto usp = static_cast<const UrlSearchParamsObject *>(m); + if (index < usp->length()) { + if (p) + p->value = usp->engine()->newString(usp->nameAt(index)); + return Attr_NotConfigurable | Attr_NotWritable; + } + } + + return Object::virtualGetOwnProperty(m, id, p); +} + +static bool checkSearchParamsType(ExecutionEngine *v4, const Scoped<UrlSearchParamsObject> &o) +{ + if (o) + return true; + + v4->throwTypeError(QStringLiteral("Value of \"this\" must be of type URLSearchParams")); + return false; +} + +ReturnedValue UrlSearchParamsPrototype::method_toString( + const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + Scoped<UrlSearchParamsObject> o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); + + auto params = o->params(); + + QString value; + + for (const QStringList &pair : params) + value += QLatin1String("%1=%2&").arg(QString::fromUtf8(QUrl::toPercentEncoding(pair[0])), + QString::fromUtf8(QUrl::toPercentEncoding(pair[1]))); + + value.chop(1); + + return Encode(v4->newString(value)); +} + +ReturnedValue UrlSearchParamsPrototype::method_sort(const FunctionObject *b, const Value *thisObject, + const Value *, int) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + Scoped<UrlSearchParamsObject> o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); + + QList<QStringList> params = o->params(); + std::stable_sort(params.begin(), params.end(), [](QStringList a, QStringList b) { return a[0] < b[0]; }); + + o->setParams(params); + + return Encode::undefined(); +} + +ReturnedValue UrlSearchParamsPrototype::method_append(const FunctionObject *b, const Value *thisObject, + const Value *argv, int argc) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + if (argc != 2) + return v4->throwError(QLatin1String("Bad amount of arguments")); + + ScopedValue argName(scope, argv[0]); + ScopedValue argValue(scope, argv[1]); + + String *argNameString = argName->stringValue(); + + if (argNameString == nullptr) + return v4->throwTypeError(QLatin1String("Invalid argument provided")); + + ScopedString name(scope, argName->as<String>()); + ScopedString value(scope, argValue->toString(v4)); + + Scoped<UrlSearchParamsObject> o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); + + o->append(name->d(), value->d()); + + return Encode::undefined(); +} + +ReturnedValue UrlSearchParamsPrototype::method_delete(const FunctionObject *b, const Value *thisObject, + const Value *argv, int argc) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + if (argc != 1) + return v4->throwError(QLatin1String("Bad amount of arguments")); + + ScopedValue argName(scope, argv[0]); + + String *argNameString = argName->stringValue(); + + if (argNameString == nullptr) + return v4->throwTypeError(QLatin1String("Invalid argument provided")); + + QString name = argNameString->toQString(); + + Scoped<UrlSearchParamsObject> o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); + + QList<QStringList> params = o->params(); + params.removeIf([&name](const auto &pair) { return pair.at(0) == name; }); + + o->setParams(params); + + return Encode::undefined(); +} + +ReturnedValue UrlSearchParamsPrototype::method_has(const FunctionObject *b, const Value *thisObject, + const Value *argv, int argc) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + if (argc != 1) + return v4->throwError(QLatin1String("Bad amount of arguments")); + + ScopedValue argName(scope, argv[0]); + + String *argNameString = argName->stringValue(); + + if (argNameString == nullptr) + return v4->throwTypeError(QLatin1String("Invalid argument provided")); + + Scoped<UrlSearchParamsObject> o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); + + QString name = argNameString->toQString(); + + return Encode(o->indexOf(name) != -1); +} + +ReturnedValue UrlSearchParamsPrototype::method_set(const FunctionObject *b, const Value *thisObject, + const Value *argv, int argc) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + if (argc != 2) + return v4->throwError(QLatin1String("Bad amount of arguments")); + + ScopedValue argName(scope, argv[0]); + ScopedValue argValue(scope, argv[1]); + + String *argNameString = argName->stringValue(); + + if (argNameString == nullptr) + return v4->throwTypeError(QLatin1String("Invalid argument provided")); + + Scoped<UrlSearchParamsObject> o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); + + QString name = argNameString->toQString(); + QString value = argValue->toQString(); + + auto params = o->params(); + + bool matched = false; + + for (auto it = params.begin(); it != params.end();) { + QStringList ¶m = *it; + if (param[0] == name) { + if (!matched) { + param[1] = value; + matched = true; + } else { + it = params.erase(it); + continue; + } + } + it++; + } + + if (!matched) + params << QStringList { name, value }; + + o->setParams(params); + + Scoped<UrlObject> scopedUrlObject(scope, o->d()->url.get()); + if (scopedUrlObject) + scopedUrlObject->setSearch(o->searchString()); + + return Encode::undefined(); +} + +ReturnedValue UrlSearchParamsPrototype::method_get(const FunctionObject *b, const Value *thisObject, + const Value *argv, int argc) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + if (argc != 1) + return v4->throwError(QLatin1String("Bad amount of arguments")); + + ScopedValue argName(scope, argv[0]); + + String *argNameString = argName->stringValue(); + + if (argNameString == nullptr) + return v4->throwTypeError(QLatin1String("Invalid argument provided")); + + Scoped<UrlSearchParamsObject> o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); + + QString name = argNameString->toQString(); + + int index = o->indexOf(name); + + if (index == -1) + return Encode::null(); + + return Encode(o->valueAtRaw(index)); +} + +ReturnedValue UrlSearchParamsPrototype::method_getAll(const FunctionObject *b, + const Value *thisObject, const Value *argv, + int argc) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + if (argc != 1) + return v4->throwError(QLatin1String("Bad amount of arguments")); + + ScopedValue argName(scope, argv[0]); + + String *argNameString = argName->stringValue(); + + if (argNameString == nullptr) + return v4->throwTypeError(QLatin1String("Invalid argument provided")); + + Scoped<UrlSearchParamsObject> o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); + + QString name = argNameString->toQString(); + + auto *arrayObject = v4->newArrayObject(0); + ScopedArrayObject result(scope, arrayObject); + + int i = 0; + for (int index = o->indexOf(name); index != -1; index = o->indexOf(name, index)) { + ScopedValue value(scope, Value::fromHeapObject(o->valueAtRaw(index))); + result->put(i++, value); + } + + return Encode(arrayObject); +} + +ReturnedValue UrlSearchParamsPrototype::method_forEach(const FunctionObject *b, + const Value *thisObject, const Value *argv, + int argc) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + if (argc != 1) + return v4->throwError(QLatin1String("Bad amount of arguments")); + + ScopedValue argFunc(scope, argv[0]); + + FunctionObject *func = argFunc->as<FunctionObject>(); + + if (func == nullptr) + return v4->throwTypeError(QLatin1String("Invalid argument: must be a function")); + + Scoped<UrlSearchParamsObject> o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); + + for (int i = 0; i < o->length(); i++) { + Scoped<String> name(scope, o->nameAtRaw(i)); + Scoped<String> value(scope, o->valueAtRaw(i)); + + QV4::JSCallArguments calldata(scope, 2); + + calldata.args[0] = value; + calldata.args[1] = name; + + func->call(calldata); + } + + return Encode::undefined(); +} + +ReturnedValue UrlSearchParamsPrototype::method_entries(const FunctionObject *b, + const Value *thisObject, const Value *, + int argc) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + if (argc != 0) + return v4->throwError(QLatin1String("Bad amount of arguments")); + + Scoped<UrlSearchParamsObject> o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); + + ScopedObject params(scope, o->d()->params.get()); + + Scoped<ArrayIteratorObject> paramsIterator(scope, v4->newArrayIteratorObject(params)); + paramsIterator->d()->iterationKind = IteratorKind::KeyValueIteratorKind; + return paramsIterator->asReturnedValue(); +} + +ReturnedValue UrlSearchParamsPrototype::method_keys(const FunctionObject *b, + const Value *thisObject, const Value *, + int argc) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + if (argc != 0) + return v4->throwError(QLatin1String("Bad amount of arguments")); + + Scoped<UrlSearchParamsObject> o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); + + ScopedObject keys(scope, o->d()->keys.get()); + + Scoped<ArrayIteratorObject> keysIterator(scope, v4->newArrayIteratorObject(keys)); + keysIterator->d()->iterationKind = IteratorKind::KeyValueIteratorKind; + return keysIterator->asReturnedValue(); +} + +ReturnedValue UrlSearchParamsPrototype::method_values(const FunctionObject *b, + const Value *thisObject, const Value *, + int argc) +{ + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + + if (argc != 0) + return v4->throwError(QLatin1String("Bad amount of arguments")); + + Scoped<UrlSearchParamsObject> o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); + + ScopedObject values(scope, o->d()->values.get()); + + Scoped<ArrayIteratorObject> valuesIterator(scope, v4->newArrayIteratorObject(values)); + valuesIterator->d()->iterationKind = IteratorKind::KeyValueIteratorKind; + return valuesIterator->asReturnedValue(); +} diff --git a/src/qml/jsruntime/qv4urlobject_p.h b/src/qml/jsruntime/qv4urlobject_p.h new file mode 100644 index 0000000000..b3b76e1158 --- /dev/null +++ b/src/qml/jsruntime/qv4urlobject_p.h @@ -0,0 +1,293 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#ifndef QV4URLOBJECT_P_H +#define QV4URLOBJECT_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 "qv4object_p.h" +#include "qv4functionobject_p.h" + +#include <QtCore/QString> +#include <QtCore/QUrl> + +QT_BEGIN_NAMESPACE + +namespace QV4 { +namespace Heap { +// clang-format off +#define UrlObjectMembers(class, Member) \ + Member(class, Pointer, String *, hash) \ + Member(class, Pointer, String *, host) \ + Member(class, Pointer, String *, hostname) \ + Member(class, Pointer, String *, href) \ + Member(class, Pointer, String *, origin) \ + Member(class, Pointer, String *, password) \ + Member(class, Pointer, String *, pathname) \ + Member(class, Pointer, String *, port) \ + Member(class, Pointer, String *, protocol) \ + Member(class, Pointer, String *, search) \ + Member(class, Pointer, String *, username) +// clang-format on + +DECLARE_HEAP_OBJECT(UrlObject, Object) +{ + DECLARE_MARKOBJECTS(UrlObject) + void init() { Object::init(); } +}; + +struct UrlCtor : FunctionObject +{ + void init(ExecutionEngine *engine); +}; + +// clang-format off +#define UrlSearchParamsObjectMembers(class, Member) \ + Member(class, Pointer, ArrayObject *, params) \ + Member(class, Pointer, ArrayObject *, keys) \ + Member(class, Pointer, ArrayObject *, values) \ + Member(class, Pointer, UrlObject *, url) +// clang-format on + +DECLARE_HEAP_OBJECT(UrlSearchParamsObject, Object) +{ + DECLARE_MARKOBJECTS(UrlSearchParamsObject) + void init() { Object::init(); } +}; + +struct UrlSearchParamsCtor : FunctionObject +{ + void init(ExecutionEngine *engine); +}; +} + +struct UrlObject : Object +{ + V4_OBJECT2(UrlObject, Object) + Q_MANAGED_TYPE(UrlObject) + V4_PROTOTYPE(urlPrototype) + + QString hash() const { return QLatin1String("#") + toQString(d()->hash); } + bool setHash(QString hash); + + QString host() const { return toQString(d()->host); } + bool setHost(QString host); + + QString hostname() const { return toQString(d()->hostname); } + bool setHostname(QString hostname); + + QString href() const { return toQString(d()->href); } + bool setHref(QString href); + + QString origin() const { return toQString(d()->origin); } + + QString password() const { return toQString(d()->password); } + bool setPassword(QString password); + + QString pathname() const { return toQString(d()->pathname); } + bool setPathname(QString pathname); + + QString port() const { return toQString(d()->port); } + bool setPort(QString port); + + QString protocol() const { return toQString(d()->protocol); } + bool setProtocol(QString protocol); + + Q_QML_AUTOTEST_EXPORT QString search() const; + bool setSearch(QString search); + + QString username() const { return toQString(d()->username); } + bool setUsername(QString username); + + QUrl toQUrl() const; + void setUrl(const QUrl &url); + +private: + static QString toQString(const Heap::String *string) + { + return string ? string->toQString() : QString(); + } + + void updateOrigin(); + void updateHost(); +}; + +template<> +inline const UrlObject *Value::as() const +{ + return isManaged() && m()->internalClass->vtable->type == Managed::Type_UrlObject + ? static_cast<const UrlObject *>(this) + : nullptr; +} + +struct UrlCtor : FunctionObject +{ + V4_OBJECT2(UrlCtor, FunctionObject) + + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, + int argc, const Value *); +}; + +struct UrlPrototype : Object +{ + V4_PROTOTYPE(objectPrototype) + + void init(ExecutionEngine *engine, Object *ctor); + + static ReturnedValue method_getHash(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_setHash(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + + static ReturnedValue method_getHost(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_setHost(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + + static ReturnedValue method_getHostname(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_setHostname(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + + static ReturnedValue method_getHref(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_setHref(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + + static ReturnedValue method_getOrigin(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + + static ReturnedValue method_getPassword(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_setPassword(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + + static ReturnedValue method_getPathname(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_setPathname(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + + static ReturnedValue method_getPort(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_setPort(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + + static ReturnedValue method_getProtocol(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_setProtocol(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + + static ReturnedValue method_getSearch(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_setSearch(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + + static ReturnedValue method_getUsername(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_setUsername(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + + static ReturnedValue method_getSearchParams(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); +}; + +struct UrlSearchParamsObject : Object +{ + V4_OBJECT2(UrlSearchParamsObject, Object) + Q_MANAGED_TYPE(UrlSearchParamsObject) + V4_PROTOTYPE(urlSearchParamsPrototype) + + void initializeParams(); + void initializeParams(QString params); + void initializeParams(ScopedArrayObject& params); + void initializeParams(ScopedObject& params); + + QList<QStringList> params() const; + void setParams(QList<QStringList> params); + Heap::UrlObject *urlObject() const; + void setUrlObject(const UrlObject *url); + + QString searchString() const; + + QString nameAt(int index) const; + Heap::String * nameAtRaw(int index) const; + QString valueAt(int index) const; + Heap::String * valueAtRaw(int index) const; + + void append(Heap::String *name, Heap::String *value); + + int indexOf(QString name, int last = -1) const; + int length() const; + + using Object::getOwnProperty; +protected: + static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target); + static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p); +private: + QString stringAt(int index, int pairIndex) const; + Heap::String * stringAtRaw(int index, int pairIndex) const; +}; + +template<> +inline const UrlSearchParamsObject *Value::as() const +{ + return isManaged() && m()->internalClass->vtable->type == Managed::Type_UrlSearchParamsObject + ? static_cast<const UrlSearchParamsObject *>(this) + : nullptr; +} + +struct UrlSearchParamsCtor : FunctionObject +{ + V4_OBJECT2(UrlSearchParamsCtor, FunctionObject) + + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, + int argc, const Value *); +}; + +struct UrlSearchParamsPrototype : Object +{ + V4_PROTOTYPE(objectPrototype) + + void init(ExecutionEngine *engine, Object *ctor); + + static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_append(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_delete(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_getAll(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, + const Value *argv, int argc); + +}; + +} + +QT_END_NAMESPACE + +#endif // QV4URLOBJECT_P_H diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp index d29b060b9e..223a004602 100644 --- a/src/qml/jsruntime/qv4value.cpp +++ b/src/qml/jsruntime/qv4value.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <qv4runtime_p.h> #include <qv4propertykey_p.h> @@ -109,7 +73,7 @@ double Value::toNumberImpl(Value val) Scope scope(val.objectValue()->engine()); ScopedValue protectThis(scope, val); ScopedValue prim(scope, RuntimeHelpers::toPrimitive(val, NUMBER_HINT)); - if (scope.engine->hasException) + if (scope.hasException()) return 0; return prim->toNumber(); } @@ -122,100 +86,122 @@ double Value::toNumberImpl(Value val) } } -QString Value::toQStringNoThrow() const +static QString primitiveToQString(const Value *value) { - switch (type()) { + switch (value->type()) { case Value::Empty_Type: Q_ASSERT(!"empty Value encountered"); - Q_UNREACHABLE(); + Q_UNREACHABLE_RETURN(QString()); case Value::Undefined_Type: return QStringLiteral("undefined"); case Value::Null_Type: return QStringLiteral("null"); case Value::Boolean_Type: - if (booleanValue()) + if (value->booleanValue()) return QStringLiteral("true"); else return QStringLiteral("false"); case Value::Managed_Type: + Q_UNREACHABLE_RETURN(QString()); + case Value::Integer_Type: { + QString str; + RuntimeHelpers::numberToString(&str, (double)value->int_32(), 10); + return str; + } + case Value::Double_Type: { + QString str; + RuntimeHelpers::numberToString(&str, value->doubleValue(), 10); + return str; + } + } // switch + + Q_UNREACHABLE_RETURN(QString()); +} + + +QString Value::toQStringNoThrow() const +{ + if (isManaged()) { if (String *s = stringValue()) return s->toQString(); if (Symbol *s = symbolValue()) return s->descriptiveString(); - { - Q_ASSERT(isObject()); - Scope scope(objectValue()->engine()); - ScopedValue ex(scope); - bool caughtException = false; - ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT)); + + Q_ASSERT(isObject()); + Scope scope(objectValue()->engine()); + ScopedValue ex(scope); + bool caughtException = false; + ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT)); + if (scope.hasException()) { + ex = scope.engine->catchException(); + caughtException = true; + } else if (prim->isPrimitive()) { + return prim->toQStringNoThrow(); + } + + // Can't nest try/catch due to CXX ABI limitations for foreign exception nesting. + if (caughtException) { + ScopedValue prim(scope, RuntimeHelpers::toPrimitive(ex, STRING_HINT)); if (scope.hasException()) { ex = scope.engine->catchException(); - caughtException = true; } else if (prim->isPrimitive()) { - return prim->toQStringNoThrow(); - } - // Can't nest try/catch due to CXX ABI limitations for foreign exception nesting. - if (caughtException) { - ScopedValue prim(scope, RuntimeHelpers::toPrimitive(ex, STRING_HINT)); - if (scope.hasException()) { - ex = scope.engine->catchException(); - } else if (prim->isPrimitive()) { - return prim->toQStringNoThrow(); - } + return prim->toQStringNoThrow(); } - return QString(); } - case Value::Integer_Type: { - QString str; - RuntimeHelpers::numberToString(&str, (double)int_32(), 10); - return str; - } - default: { // double - QString str; - RuntimeHelpers::numberToString(&str, doubleValue(), 10); - return str; + + return QString(); } - } // switch + + return primitiveToQString(this); } QString Value::toQString() const { - switch (type()) { - case Value::Empty_Type: - Q_ASSERT(!"empty Value encountered"); - Q_UNREACHABLE(); - 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::Managed_Type: - if (String *s = stringValue()) { + if (isManaged()) { + if (String *s = stringValue()) return s->toQString(); - } else if (isSymbol()) { + + if (isSymbol()) { static_cast<const Managed *>(this)->engine()->throwTypeError(); return QString(); - } else { - Q_ASSERT(isObject()); - Scope scope(objectValue()->engine()); - ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT)); - return prim->toQString(); } - case Value::Integer_Type: { - QString str; - RuntimeHelpers::numberToString(&str, (double)int_32(), 10); - return str; + + Q_ASSERT(isObject()); + Scope scope(objectValue()->engine()); + ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT)); + return prim->toQString(); } - default: { // double - QString str; - RuntimeHelpers::numberToString(&str, doubleValue(), 10); - return str; + + return primitiveToQString(this); +} + +QString Value::toQString(bool *ok) const +{ + if (isManaged()) { + if (String *s = stringValue()) { + *ok = true; + return s->toQString(); + } + + if (isSymbol()) { + static_cast<const Managed *>(this)->engine()->throwTypeError(); + *ok = false; + return QString(); + } + + Q_ASSERT(isObject()); + Scope scope(objectValue()->engine()); + ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT)); + + if (scope.hasException()) { + *ok = false; + return QString(); + } + + return prim->toQString(ok); } - } // switch + + return primitiveToQString(this); } QV4::PropertyKey Value::toPropertyKey(ExecutionEngine *e) const @@ -250,6 +236,8 @@ bool Value::sameValue(Value other) const { if (isDouble() && other.isInteger()) return other.int_32() ? (doubleValue() == double(other.int_32())) : (doubleValue() == 0 && !std::signbit(doubleValue())); + if (isManaged()) + return other.isManaged() && cast<Managed>()->isEqualTo(other.cast<Managed>()); return false; } @@ -269,6 +257,8 @@ bool Value::sameValueZero(Value other) const { return true; } } + if (isManaged()) + return other.isManaged() && cast<Managed>()->isEqualTo(other.cast<Managed>()); return false; } diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 4e901721cb..eaa57a36aa 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4VALUE_P_H #define QV4VALUE_P_H @@ -70,9 +34,8 @@ namespace Heap { struct Base; } -struct Q_QML_PRIVATE_EXPORT Value : public StaticValue +struct Q_QML_EXPORT Value : public StaticValue { - using HeapBasePtr = Heap::Base *; using ManagedPtr = Managed *; Value() = default; @@ -83,53 +46,6 @@ struct Q_QML_PRIVATE_EXPORT Value : public StaticValue return {staticValue._val}; } -#if QT_POINTER_SIZE == 8 - QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const - { - HeapBasePtr b; -#ifdef __ia64 -// Restore bits 49-47 to bits 63-61, undoing the workaround explained in -// setM below. - quint64 _tmp; - - _tmp = _val & (7L << 47); // 0x3800000000000 - _tmp = (_tmp << 14) | (_val ^ _tmp); - memcpy(&b, &_tmp, 8); -#else - memcpy(&b, &_val, 8); -#endif - return b; - } - QML_NEARLY_ALWAYS_INLINE void setM(HeapBasePtr b) - { - memcpy(&_val, &b, 8); -#ifdef __ia64 -// On ia64, bits 63-61 in a 64-bit pointer are used to store the virtual region -// number. Since this implementation is not 64-bit clean, we move bits 63-61 -// to bits 49-47 and hope for the best. This is undone in *m(), above. - _val |= ((_val & (7L << 61)) >> 14); - _val &= ((1L << 50)-1); -#endif - } -#elif QT_POINTER_SIZE == 4 - QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const - { - Q_STATIC_ASSERT(sizeof(HeapBasePtr) == sizeof(quint32)); - HeapBasePtr b; - quint32 v = value(); - memcpy(&b, &v, 4); - return b; - } - QML_NEARLY_ALWAYS_INLINE void setM(HeapBasePtr b) - { - quint32 v; - memcpy(&v, &b, 4); - setTagValue(Managed_Type_Internal, v); - } -#else -# error "unsupported pointer size" -#endif - inline bool isString() const; inline bool isStringOrSymbol() const; inline bool isSymbol() const; @@ -190,8 +106,11 @@ struct Q_QML_PRIVATE_EXPORT Value : public StaticValue inline double toNumber() const; static double toNumberImpl(Value v); double toNumberImpl() const { return toNumberImpl(*this); } + QString toQStringNoThrow() const; QString toQString() const; + QString toQString(bool *ok) const; + Heap::String *toString(ExecutionEngine *e) const { if (isString()) return reinterpret_cast<Heap::String *>(m()); @@ -308,7 +227,7 @@ struct Q_QML_PRIVATE_EXPORT Value : public StaticValue template<typename T> Value &operator=(const Scoped<T> &t); }; -Q_STATIC_ASSERT(std::is_trivial<Value>::value); +Q_STATIC_ASSERT(std::is_trivial_v<Value>); Q_STATIC_ASSERT(sizeof(Value) == sizeof(StaticValue)); template<> @@ -390,7 +309,10 @@ inline bool Value::isObject() const inline bool Value::isFunctionObject() const { HeapBasePtr b = heapObject(); - return b && b->internalClass->vtable->isFunctionObject; + if (!b) + return false; + const VTable *vtable = b->internalClass->vtable; + return vtable->call || vtable->callAsConstructor; } inline bool Value::isPrimitive() const @@ -434,9 +356,9 @@ inline int Value::toInt32() const return int_32(); if (Q_LIKELY(isDouble())) - return Double::toInt32(doubleValue()); + return QJSNumberCoercion::toInteger(doubleValue()); - return Double::toInt32(toNumberImpl()); + return QJSNumberCoercion::toInteger(toNumberImpl()); } inline unsigned int Value::toUInt32() const @@ -480,7 +402,7 @@ inline double Value::toInteger() const template <size_t o> struct HeapValue : Value { - static Q_CONSTEXPR size_t offset = o; + static constexpr size_t offset = o; HeapBasePtr base() { HeapBasePtr base = reinterpret_cast<HeapBasePtr>(this) - (offset/sizeof(Heap::Base)); Q_ASSERT(base->inUse()); @@ -497,7 +419,7 @@ struct HeapValue : Value { template <size_t o> struct ValueArray { - static Q_CONSTEXPR size_t offset = o; + static constexpr size_t offset = o; uint size; uint alloc; Value values[1]; @@ -523,41 +445,9 @@ struct ValueArray { return values; } - void insertData(EngineBase *e, uint index, Value v) { - for (uint i = size - 1; i > index; --i) { - values[i] = values[i - 1]; - } - set(e, index, v); - } - void removeData(EngineBase *e, uint index, int n = 1) { - Q_UNUSED(e); - for (uint i = index; i < size - n; ++i) { - values[i] = values[i + n]; - } - } - void mark(MarkStack *markStack) { - Value *v = values; - const Value *end = v + alloc; - if (alloc > 32*1024) { - // drain from time to time to avoid overflows in the js stack - Value::HeapBasePtr *currentBase = markStack->top; - while (v < end) { - v->mark(markStack); - ++v; - if (markStack->top >= currentBase + 32*1024) { - Value::HeapBasePtr *oldBase = markStack->base; - markStack->base = currentBase; - markStack->drain(); - markStack->base = oldBase; - } - } - } else { - while (v < end) { - v->mark(markStack); - ++v; - } - } + for (Value *v = values, *end = values + alloc; v < end; ++v) + v->mark(markStack); } }; diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp index e117e509ab..62e21a120c 100644 --- a/src/qml/jsruntime/qv4variantobject.cpp +++ b/src/qml/jsruntime/qv4variantobject.cpp @@ -1,45 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4variantobject_p.h" #include "qv4functionobject_p.h" -#include "qv4objectproto_p.h" #include <private/qqmlvaluetypewrapper_p.h> #include <private/qv4qobjectwrapper_p.h> @@ -55,18 +18,18 @@ void Heap::VariantObject::init() scarceData = new ExecutionEngine::ScarceResourceData; } -void Heap::VariantObject::init(const QVariant &value) +void Heap::VariantObject::init(const QMetaType type, const void *data) { Object::init(); - scarceData = new ExecutionEngine::ScarceResourceData(value); + scarceData = new ExecutionEngine::ScarceResourceData(type, data); if (isScarce()) removeVmePropertyReference(); } bool VariantObject::Data::isScarce() const { - QVariant::Type t = data().type(); - return t == QVariant::Pixmap || t == QVariant::Image; + int t = data().userType(); + return t == QMetaType::QPixmap || t == QMetaType::QImage; } bool VariantObject::virtualIsEqualTo(Managed *m, Managed *other) @@ -139,7 +102,7 @@ ReturnedValue VariantPrototype::method_toString(const FunctionObject *b, const V RETURN_UNDEFINED(); const QVariant variant = o->d()->data(); QString result = variant.toString(); - if (result.isEmpty() && !variant.canConvert(QVariant::String)) { + if (result.isEmpty() && !variant.canConvert(QMetaType(QMetaType::QString))) { QDebug dbg(&result); dbg << variant; // QDebug appends a space, we're not interested in continuing the stream so we chop it off. @@ -154,21 +117,32 @@ ReturnedValue VariantPrototype::method_valueOf(const FunctionObject *b, const Va const VariantObject *o = thisObject->as<QV4::VariantObject>(); if (o) { QVariant v = o->d()->data(); - switch (v.type()) { - case QVariant::Invalid: + switch (v.userType()) { + case QMetaType::UnknownType: return Encode::undefined(); - case QVariant::String: + case QMetaType::QString: return Encode(b->engine()->newString(v.toString())); - case QVariant::Int: + case QMetaType::Int: return Encode(v.toInt()); - case QVariant::Double: - case QVariant::UInt: + case QMetaType::Double: + case QMetaType::UInt: return Encode(v.toDouble()); - case QVariant::Bool: + case QMetaType::Bool: return Encode(v.toBool()); default: - if (QMetaType::typeFlags(v.userType()) & QMetaType::IsEnumeration) - RETURN_RESULT(Encode(v.toInt())); + if (QMetaType(v.metaType()).flags() & QMetaType::IsEnumeration) + if (v.metaType().sizeOf() <= qsizetype(sizeof(int))) + return Encode(v.toInt()); + if (v.canConvert<double>()) + return Encode(v.toDouble()); + if (v.canConvert<int>()) + return Encode(v.toInt()); + if (v.canConvert<uint>()) + return Encode(v.toUInt()); + if (v.canConvert<bool>()) + return Encode(v.toBool()); + if (v.canConvert<QString>()) + return Encode(b->engine()->newString(v.toString())); break; } } diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h index 78e0a5373a..f2394ce9a2 100644 --- a/src/qml/jsruntime/qv4variantobject_p.h +++ b/src/qml/jsruntime/qv4variantobject_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4VARIANTOBJECT_P_H #define QV4VARIANTOBJECT_P_H @@ -67,7 +31,7 @@ namespace Heap { struct VariantObject : Object { void init(); - void init(const QVariant &value); + void init(const QMetaType type, const void *data); void destroy() { Q_ASSERT(scarceData); if (isScarce()) @@ -90,7 +54,7 @@ private: } -struct VariantObject : Object +struct Q_QML_EXPORT VariantObject : Object { V4_OBJECT2(VariantObject, Object) V4_PROTOTYPE(variantPrototype) diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 27d518f5c6..096b9a6299 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -1,47 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4vme_moth_p.h" #include <QtCore/qjsondocument.h> #include <QtCore/qjsonobject.h> +#include <private/qv4alloca_p.h> #include <private/qv4instr_moth_p.h> #include <private/qv4value_p.h> #include <private/qv4debugging_p.h> @@ -58,6 +23,8 @@ #include <private/qv4generatorobject_p.h> #include <private/qv4alloca_p.h> #include <private/qqmljavascriptexpression_p.h> +#include <private/qv4qmlcontext_p.h> +#include <QtQml/private/qv4runtime_p.h> #include <iostream> #if QT_CONFIG(qml_jit) @@ -68,6 +35,9 @@ #undef COUNT_INSTRUCTIONS +Q_TRACE_POINT(qtqml, QQmlV4_function_call_entry, const QV4::ExecutionEngine *engine, const QString &function, const QString &fileName, int line, int column) +Q_TRACE_POINT(qtqml, QQmlV4_function_call_exit) + enum { ShowWhenDeoptimiationHappens = 0 }; extern "C" { @@ -341,14 +311,24 @@ static struct InstrCount { } #endif -#define STACK_VALUE(temp) stack[temp] +static inline QV4::Value &stackValue(QV4::Value *stack, size_t slot, const JSTypesStackFrame *frame) +{ + Q_ASSERT(slot < CallData::HeaderSize() / sizeof(QV4::StaticValue) + + frame->jsFrame->argc() + + frame->v4Function->compiledFunction->nRegisters); + Q_UNUSED(frame); + + return stack[slot]; +} + +#define STACK_VALUE(temp) stackValue(stack, temp, frame) // qv4scopedvalue_p.h also defines a CHECK_EXCEPTION macro #ifdef CHECK_EXCEPTION #undef CHECK_EXCEPTION #endif #define CHECK_EXCEPTION \ - if (engine->hasException || engine->isInterrupted.loadAcquire()) \ + if (engine->hasException || engine->isInterrupted.loadRelaxed()) \ goto handleUnwind static inline Heap::CallContext *getScope(QV4::Value *stack, int level) @@ -364,26 +344,24 @@ static inline Heap::CallContext *getScope(QV4::Value *stack, int level) static inline const QV4::Value &constant(Function *function, int index) { - return function->compilationUnit->constants[index].asValue<Value>(); + return function->compilationUnit->constants[index].asValue<QV4::Value>(); } static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs) { redo: - switch (lhs.quickType()) { - case QV4::Value::QT_ManagedOrUndefined: - if (lhs.isUndefined()) - return false; - Q_FALLTHROUGH(); - case QV4::Value::QT_ManagedOrUndefined1: - case QV4::Value::QT_ManagedOrUndefined2: - case QV4::Value::QT_ManagedOrUndefined3: + if (lhs.isUndefined()) + return false; + if (lhs.isManagedOrUndefined()) { // LHS: Managed if (lhs.m()->internalClass->vtable->isString) return RuntimeHelpers::stringToNumber(static_cast<String &>(lhs).toQString()) == rhs; accumulator = lhs; lhs = QV4::Value::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(accumulator), PREFERREDTYPE_HINT)); goto redo; + } + + switch (lhs.quickType()) { case QV4::Value::QT_Empty: Q_UNREACHABLE(); case QV4::Value::QT_Null: @@ -413,26 +391,81 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs) d = val.toNumberImpl(); \ CHECK_EXCEPTION; \ } \ - i = Double::toInt32(d); \ + i = QJSNumberCoercion::toInteger(d); \ } \ } while (false) -ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) +struct AOTCompiledMetaMethod +{ +public: + AOTCompiledMetaMethod(const Function::AOTCompiledFunction *aotCompiledFunction) + : aotCompiledFunction(aotCompiledFunction) + {} + + int parameterCount() const { return aotCompiledFunction->types.size() - 1; } + QMetaType returnMetaType() const { return aotCompiledFunction->types[0]; } + QMetaType parameterMetaType(int i) const { return aotCompiledFunction->types[i + 1]; } + +private: + const Function::AOTCompiledFunction *aotCompiledFunction = nullptr; +}; + +void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine) +{ + qt_v4ResolvePendingBreakpointsHook(); + if (engine->checkStackLimits()) { + frame->setReturnValueUndefined(); + return; + } + ExecutionEngineCallDepthRecorder executionEngineCallDepthRecorder(engine); + + Function *function = frame->v4Function; + Q_ASSERT(function->aotCompiledCode); + Q_TRACE_SCOPE(QQmlV4_function_call, engine, function->name()->toQString(), + function->executableCompilationUnit()->fileName(), + function->compiledFunction->location.line(), + function->compiledFunction->location.column()); + Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling + + const AOTCompiledMetaMethod method(&function->aotCompiledFunction); + QV4::coerceAndCall( + engine, &method, frame->returnAndArgValues(), + frame->returnAndArgTypes(), frame->argc(), + [frame, engine, function](void **argv, int argc) { + Q_UNUSED(argc); + + QQmlPrivate::AOTCompiledContext aotContext; + if (auto context = QV4::ExecutionEngine::qmlContext(frame->context()->d())) { + QV4::Heap::QQmlContextWrapper *wrapper = static_cast<Heap::QmlContext *>(context)->qml(); + aotContext.qmlScopeObject = wrapper->scopeObject; + aotContext.qmlContext = wrapper->context; + } + + aotContext.engine = engine->jsEngine(); + aotContext.compilationUnit = function->executableCompilationUnit(); + function->aotCompiledCode(&aotContext, argv); + }); +} + +ReturnedValue VME::exec(JSTypesStackFrame *frame, ExecutionEngine *engine) { qt_v4ResolvePendingBreakpointsHook(); CHECK_STACK_LIMITS(engine); Function *function = frame->v4Function; Q_TRACE_SCOPE(QQmlV4_function_call, engine, function->name()->toQString(), - function->compilationUnit->fileName(), - function->compiledFunction->location.line, - function->compiledFunction->location.column); + function->executableCompilationUnit()->fileName(), + function->compiledFunction->location.line(), + function->compiledFunction->location.column()); Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling QV4::Debugging::Debugger *debugger = engine->debugger(); #if QT_CONFIG(qml_jit) if (debugger == nullptr) { - if (function->jittedCode == nullptr) { + // Check for codeRef here. In rare cases the JIT compilation may fail, which leaves us + // with a (useless) codeRef, but no jittedCode. In that case, don't try to JIT again every + // time we execute the function, but just interpret instead. + if (function->codeRef == nullptr) { if (engine->canJIT(function)) QV4::JIT::BaselineJIT(function).generate(); else @@ -446,6 +479,7 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) debugger->enteringFunction(); ReturnedValue result; + Q_ASSERT(function->kind != Function::AotCompiled); if (function->jittedCode != nullptr && debugger == nullptr) { result = function->jittedCode(frame, engine); } else { @@ -459,7 +493,7 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) return result; } -QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, const char *code) +QV4::ReturnedValue VME::interpret(JSTypesStackFrame *frame, ExecutionEngine *engine, const char *code) { QV4::Function *function = frame->v4Function; QV4::Value &accumulator = frame->jsFrame->accumulator.asValue<Value>(); @@ -521,14 +555,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(LoadImport) MOTH_BEGIN_INSTR(LoadLocal) - auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m()); + auto cc = static_cast<Heap::CallContext *>(STACK_VALUE(CallData::Context).m()); Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext); acc = cc->locals[index].asReturnedValue(); MOTH_END_INSTR(LoadLocal) MOTH_BEGIN_INSTR(StoreLocal) CHECK_EXCEPTION; - auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m()); + auto cc = static_cast<Heap::CallContext *>(STACK_VALUE(CallData::Context).m()); Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext); QV4::WriteBarrier::write(engine, cc, cc->locals.values[index].data_ptr(), acc); MOTH_END_INSTR(StoreLocal) @@ -611,6 +645,18 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, CHECK_EXCEPTION; MOTH_END_INSTR(LoadProperty) + MOTH_BEGIN_INSTR(LoadOptionalProperty) + STORE_IP(); + STORE_ACC(); + if (accumulator.isNullOrUndefined()) { + acc = Encode::undefined(); + code += offset; + } else { + acc = Runtime::LoadProperty::call(engine, accumulator, name); + } + CHECK_EXCEPTION; + MOTH_END_INSTR(LoadOptionalProperty) + MOTH_BEGIN_INSTR(GetLookup) STORE_IP(); STORE_ACC(); @@ -629,6 +675,20 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, CHECK_EXCEPTION; MOTH_END_INSTR(GetLookup) + MOTH_BEGIN_INSTR(GetOptionalLookup) + STORE_IP(); + STORE_ACC(); + + QV4::Lookup *l = function->executableCompilationUnit()->runtimeLookups + index; + + if (accumulator.isNullOrUndefined()) { + code += offset; + } else { + acc = l->getter(l, engine, accumulator); + } + CHECK_EXCEPTION; + MOTH_END_INSTR(GetOptionalLookup) + MOTH_BEGIN_INSTR(StoreProperty) STORE_IP(); STORE_ACC(); @@ -659,14 +719,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(StoreSuperProperty) MOTH_BEGIN_INSTR(Yield) - frame->yield = code; - frame->yieldIsIterator = false; + frame->setYield(code); + frame->setYieldIsIterator(false); return acc; MOTH_END_INSTR(Yield) MOTH_BEGIN_INSTR(YieldStar) - frame->yield = code; - frame->yieldIsIterator = true; + frame->setYield(code); + frame->setYieldIsIterator(true); return acc; MOTH_END_INSTR(YieldStar) @@ -686,7 +746,8 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(IteratorNextForYieldStar) STORE_ACC(); acc = Runtime::IteratorNextForYieldStar::call(engine, accumulator, STACK_VALUE(iterator), &STACK_VALUE(object)); - CHECK_EXCEPTION; + if (ACC.toBoolean()) + code += offset; MOTH_END_INSTR(IteratorNextForYieldStar) MOTH_BEGIN_INSTR(CallValue) @@ -714,7 +775,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(CallProperty) STORE_IP(); - acc = Runtime::CallProperty::call(engine, stack[base], name, stack + argv, argc); + acc = Runtime::CallProperty::call(engine, STACK_VALUE(base), name, stack + argv, argc); CHECK_EXCEPTION; MOTH_END_INSTR(CallProperty) @@ -722,35 +783,33 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, STORE_IP(); Lookup *l = function->executableCompilationUnit()->runtimeLookups + lookupIndex; - if (stack[base].isNullOrUndefined()) { + if (STACK_VALUE(base).isNullOrUndefined()) { QString message = QStringLiteral("Cannot call method '%1' of %2") .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString()) - .arg(stack[base].toQStringNoThrow()); + .arg(STACK_VALUE(base).toQStringNoThrow()); acc = engine->throwTypeError(message); goto handleUnwind; } // ok to have the value on the stack here - Value f = Value::fromReturnedValue(l->getter(l, engine, stack[base])); + Value f = Value::fromReturnedValue(l->getter(l, engine, STACK_VALUE(base))); - if (Q_UNLIKELY(!f.isFunctionObject())) { - QString message = QStringLiteral("Property '%1' of object %2 is not a function") - .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString()) - .arg(stack[base].toQStringNoThrow()); + if (Q_LIKELY(f.isFunctionObject())) { + acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc); + } else if (QmlSignalHandler *handler = f.as<QmlSignalHandler>()) { + acc = handler->call(stack + base, stack + argv, argc); + } else { + const QString message = QStringLiteral("Property '%1' of object %2 is not a function") + .arg(engine->currentStackFrame->v4Function->compilationUnit + ->runtimeStrings[l->nameIndex]->toQString()) + .arg(STACK_VALUE(base).toQStringNoThrow()); acc = engine->throwTypeError(message); goto handleUnwind; } - acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc); CHECK_EXCEPTION; MOTH_END_INSTR(CallPropertyLookup) - MOTH_BEGIN_INSTR(CallElement) - STORE_IP(); - acc = Runtime::CallElement::call(engine, stack[base], STACK_VALUE(index), stack + argv, argc); - CHECK_EXCEPTION; - MOTH_END_INSTR(CallElement) - MOTH_BEGIN_INSTR(CallName) STORE_IP(); acc = Runtime::CallName::call(engine, name, stack + argv, argc); @@ -864,7 +923,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(PushWithContext) STORE_IP(); STORE_ACC(); - acc = Runtime::PushWithContext::call(engine, stack[CallData::Accumulator]); + acc = Runtime::PushWithContext::call(engine, STACK_VALUE(CallData::Accumulator)); CHECK_EXCEPTION; MOTH_END_INSTR(PushWithContext) @@ -902,15 +961,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, STORE_IP(); STORE_ACC(); acc = Runtime::IteratorNext::call(engine, accumulator, &STACK_VALUE(value)); - STACK_VALUE(done) = acc; - CHECK_EXCEPTION; + if (ACC.toBoolean()) + code += offset; MOTH_END_INSTR(IteratorNext) MOTH_BEGIN_INSTR(IteratorClose) STORE_IP(); STORE_ACC(); - acc = Runtime::IteratorClose::call(engine, accumulator, STACK_VALUE(done)); - CHECK_EXCEPTION; + acc = Runtime::IteratorClose::call(engine, accumulator); MOTH_END_INSTR(IteratorClose) MOTH_BEGIN_INSTR(DestructureRestElement) @@ -971,12 +1029,13 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(ConvertThisToObject) STORE_ACC(); - stack[CallData::This] = Runtime::ConvertThisToObject::call(engine, stack[CallData::This]); + stack[CallData::This] = Runtime::ConvertThisToObject::call( + engine, STACK_VALUE(CallData::This)); CHECK_EXCEPTION; MOTH_END_INSTR(ConvertThisToObject) MOTH_BEGIN_INSTR(LoadSuperConstructor) - acc = Runtime::LoadSuperConstructor::call(engine, stack[CallData::Function]); + acc = Runtime::LoadSuperConstructor::call(engine, STACK_VALUE(CallData::Function)); CHECK_EXCEPTION; MOTH_END_INSTR(LoadSuperConstructor) @@ -1149,6 +1208,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(CmpStrictNotEqual) MOTH_BEGIN_INSTR(CmpIn) + STORE_IP(); STORE_ACC(); acc = Runtime::In::call(engine, STACK_VALUE(lhs), accumulator); CHECK_EXCEPTION; @@ -1244,14 +1304,17 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, } MOTH_END_INSTR(Sub) + MOTH_BEGIN_INSTR(As) + const Value left = STACK_VALUE(lhs); + STORE_ACC(); + acc = Runtime::As::call(engine, left, accumulator); + MOTH_END_INSTR(As) + MOTH_BEGIN_INSTR(Exp) const Value left = STACK_VALUE(lhs); double base = left.toNumber(); double exp = ACC.toNumber(); - if (qIsInf(exp) && (base == 1 || base == -1)) - acc = Encode(qQNaN()); - else - acc = Encode(pow(base,exp)); + acc = Encode(QQmlPrivate::jsExponentiate(base, exp)); MOTH_END_INSTR(Exp) MOTH_BEGIN_INSTR(Mul) @@ -1377,7 +1440,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, // We do start the exception handler in case of isInterrupted. The exception handler will // immediately abort, due to the same isInterrupted. We don't skip the exception handler // because the current behavior is easier to implement in the JIT. - Q_ASSERT(engine->hasException || engine->isInterrupted.loadAcquire() || frame->unwindLevel); + Q_ASSERT(engine->hasException || engine->isInterrupted.loadRelaxed() || frame->unwindLevel); if (!frame->unwindHandler) { acc = Encode::undefined(); return acc; diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h index b3944f5454..786fc3880d 100644 --- a/src/qml/jsruntime/qv4vme_moth_p.h +++ b/src/qml/jsruntime/qv4vme_moth_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4VME_MOTH_P_H #define QV4VME_MOTH_P_H @@ -66,8 +30,10 @@ public: QV4::Function *function; const QV4::ExecutionContext *scope; }; - static QV4::ReturnedValue exec(CppStackFrame *frame, ExecutionEngine *engine); - static QV4::ReturnedValue interpret(CppStackFrame *frame, ExecutionEngine *engine, const char *codeEntry); + + static void exec(MetaTypesStackFrame *frame, ExecutionEngine *engine); + static QV4::ReturnedValue exec(JSTypesStackFrame *frame, ExecutionEngine *engine); + static QV4::ReturnedValue interpret(JSTypesStackFrame *frame, ExecutionEngine *engine, const char *codeEntry); }; } // namespace Moth diff --git a/src/qml/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h index 9dda104cd1..0532fdc32d 100644 --- a/src/qml/jsruntime/qv4vtable_p.h +++ b/src/qml/jsruntime/qv4vtable_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4VTABLE_P_H #define QV4VTABLE_P_H @@ -51,14 +15,16 @@ // #include "qv4global_p.h" +#include <QtCore/qmetaobject.h> QT_BEGIN_NAMESPACE +class QObject; namespace QV4 { struct Lookup; -struct Q_QML_PRIVATE_EXPORT OwnPropertyKeyIterator { +struct Q_QML_EXPORT OwnPropertyKeyIterator { virtual ~OwnPropertyKeyIterator() = 0; virtual PropertyKey next(const Object *o, Property *p = nullptr, PropertyAttributes *attrs = nullptr) = 0; }; @@ -84,18 +50,21 @@ struct VTable typedef ReturnedValue (*InstanceOf)(const Object *typeObject, const Value &var); typedef ReturnedValue (*Call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + typedef void (*CallWithMetaTypes)(const FunctionObject *, QObject *, void **, const QMetaType *, int); typedef ReturnedValue (*CallAsConstructor)(const FunctionObject *, const Value *argv, int argc, const Value *newTarget); typedef ReturnedValue (*ResolveLookupGetter)(const Object *, ExecutionEngine *, Lookup *); typedef bool (*ResolveLookupSetter)(Object *, ExecutionEngine *, Lookup *, const Value &); + typedef int (*Metacall)(Object *, QMetaObject::Call, int, void **); + const VTable * const parent; quint16 inlinePropertyOffset; quint16 nInlineProperties; quint8 isExecutionContext; quint8 isString; quint8 isObject; - quint8 isFunctionObject; + quint8 isTailCallable; quint8 isErrorObject; quint8 isArrayData; quint8 isStringOrSymbol; @@ -123,11 +92,89 @@ struct VTable Call call; CallAsConstructor callAsConstructor; + CallWithMetaTypes callWithMetaTypes; ResolveLookupGetter resolveLookupGetter; ResolveLookupSetter resolveLookupSetter; + + Metacall metacall; }; +template<VTable::CallWithMetaTypes call> +struct VTableCallWithMetaTypesWrapper { constexpr static VTable::CallWithMetaTypes c = call; }; + +template<VTable::Call call> +struct VTableCallWrapper { constexpr static VTable::Call c = call; }; + +template<class Class> +constexpr VTable::CallWithMetaTypes vtableMetaTypesCallEntry() +{ + // If Class overrides virtualCallWithMetaTypes, return that. + // Otherwise, if it overrides virtualCall, return convertAndCall. + // Otherwise, just return whatever the base class had. + + // A simple == on methods is not considered constexpr, so we have to jump through some hoops. + + static_assert( + std::is_same_v< + VTableCallWithMetaTypesWrapper<Class::virtualCallWithMetaTypes>, + VTableCallWithMetaTypesWrapper<Class::SuperClass::virtualCallWithMetaTypes>> + || !std::is_same_v< + VTableCallWithMetaTypesWrapper<Class::virtualCallWithMetaTypes>, + VTableCallWithMetaTypesWrapper<nullptr>>, + "You mustn't override virtualCallWithMetaTypes with nullptr"); + + static_assert( + std::is_same_v< + VTableCallWithMetaTypesWrapper<Class::virtualConvertAndCall>, + VTableCallWithMetaTypesWrapper<Class::SuperClass::virtualConvertAndCall>> + || !std::is_same_v< + VTableCallWithMetaTypesWrapper<Class::virtualConvertAndCall>, + VTableCallWithMetaTypesWrapper<nullptr>>, + "You mustn't override virtualConvertAndCall with nullptr"); + + if constexpr ( + std::is_same_v< + VTableCallWithMetaTypesWrapper<Class::virtualCallWithMetaTypes>, + VTableCallWithMetaTypesWrapper<Class::SuperClass::virtualCallWithMetaTypes>> + && !std::is_same_v< + VTableCallWrapper<Class::virtualCall>, + VTableCallWrapper<Class::SuperClass::virtualCall>>) { + // Converting from metatypes to JS signature is easy. + return Class::virtualConvertAndCall; + } + + return Class::virtualCallWithMetaTypes; +} + +template<class Class> +constexpr VTable::Call vtableJsTypesCallEntry() +{ + // If Class overrides virtualCall, return that. + // Otherwise, if it overrides virtualCallWithMetaTypes, fail. + // (We cannot determine the target types to call virtualCallWithMetaTypes in that case) + // Otherwise, just return whatever the base class had. + + // A simple == on methods is not considered constexpr, so we have to jump through some hoops. + + static_assert( + !std::is_same_v< + VTableCallWrapper<Class::virtualCall>, + VTableCallWrapper<Class::SuperClass::virtualCall>> + || std::is_same_v< + VTableCallWithMetaTypesWrapper<Class::virtualCallWithMetaTypes>, + VTableCallWithMetaTypesWrapper<Class::SuperClass::virtualCallWithMetaTypes>>, + "If you override virtualCallWithMetaTypes, override virtualCall, too"); + + static_assert( + std::is_same_v< + VTableCallWrapper<Class::virtualCall>, + VTableCallWrapper<Class::SuperClass::virtualCall>> + || VTableCallWrapper<Class::virtualCall>::c != nullptr, + "You mustn't override virtualCall with nullptr"); + + return Class::virtualCall; +} struct VTableBase { protected: @@ -150,9 +197,19 @@ protected: static constexpr VTable::Call virtualCall = nullptr; static constexpr VTable::CallAsConstructor virtualCallAsConstructor = nullptr; + static constexpr VTable::CallWithMetaTypes virtualCallWithMetaTypes = nullptr; + static constexpr VTable::CallWithMetaTypes virtualConvertAndCall = nullptr; static constexpr VTable::ResolveLookupGetter virtualResolveLookupGetter = nullptr; static constexpr VTable::ResolveLookupSetter virtualResolveLookupSetter = nullptr; + + static constexpr VTable::Metacall virtualMetacall = nullptr; + + template<class Class> + friend constexpr VTable::CallWithMetaTypes vtableMetaTypesCallEntry(); + + template<class Class> + friend constexpr VTable::Call vtableJsTypesCallEntry(); }; #define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \ @@ -164,7 +221,7 @@ protected: classname::IsExecutionContext, \ classname::IsString, \ classname::IsObject, \ - classname::IsFunctionObject, \ + classname::IsTailCallable, \ classname::IsErrorObject, \ classname::IsArrayData, \ classname::IsStringOrSymbol, \ @@ -190,11 +247,13 @@ protected: classname::virtualOwnPropertyKeys, \ classname::virtualInstanceOf, \ \ - classname::virtualCall, \ - classname::virtualCallAsConstructor, \ + QV4::vtableJsTypesCallEntry<classname>(), \ + classname::virtualCallAsConstructor, \ + QV4::vtableMetaTypesCallEntry<classname>(), \ \ classname::virtualResolveLookupGetter, \ - classname::virtualResolveLookupSetter \ + classname::virtualResolveLookupSetter, \ + classname::virtualMetacall \ } #define DEFINE_MANAGED_VTABLE(classname) \ @@ -202,7 +261,7 @@ const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, #define V4_OBJECT2(DataClass, superClass) \ private: \ - DataClass() Q_DECL_EQ_DELETE; \ + DataClass() = delete; \ Q_DISABLE_COPY(DataClass) \ public: \ Q_MANAGED_CHECK \ @@ -217,7 +276,7 @@ const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, dptr->_checkIsInitialized(); \ return dptr; \ } \ - Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value); + Q_STATIC_ASSERT(std::is_trivial_v<QV4::Heap::DataClass>); #define V4_PROTOTYPE(p) \ static QV4::Object *defaultPrototype(QV4::ExecutionEngine *e) \ |