aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r--src/qml/jsruntime/jsruntime.pri174
-rw-r--r--src/qml/jsruntime/qv4argumentsobject.cpp66
-rw-r--r--src/qml/jsruntime/qv4argumentsobject_p.h45
-rw-r--r--src/qml/jsruntime/qv4arraybuffer.cpp133
-rw-r--r--src/qml/jsruntime/qv4arraybuffer_p.h119
-rw-r--r--src/qml/jsruntime/qv4arraydata.cpp162
-rw-r--r--src/qml/jsruntime/qv4arraydata_p.h112
-rw-r--r--src/qml/jsruntime/qv4arrayiterator.cpp49
-rw-r--r--src/qml/jsruntime/qv4arrayiterator_p.h45
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp265
-rw-r--r--src/qml/jsruntime/qv4arrayobject_p.h67
-rw-r--r--src/qml/jsruntime/qv4atomics.cpp50
-rw-r--r--src/qml/jsruntime/qv4atomics_p.h40
-rw-r--r--src/qml/jsruntime/qv4booleanobject.cpp45
-rw-r--r--src/qml/jsruntime/qv4booleanobject_p.h42
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper.cpp109
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper_p.h54
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper_unix.cpp63
-rw-r--r--src/qml/jsruntime/qv4compilationunitmapper_win.cpp75
-rw-r--r--src/qml/jsruntime/qv4context.cpp83
-rw-r--r--src/qml/jsruntime/qv4context_p.h52
-rw-r--r--src/qml/jsruntime/qv4dataview.cpp97
-rw-r--r--src/qml/jsruntime/qv4dataview_p.h44
-rw-r--r--src/qml/jsruntime/qv4dateobject.cpp384
-rw-r--r--src/qml/jsruntime/qv4dateobject_p.h223
-rw-r--r--src/qml/jsruntime/qv4debugging.cpp17
-rw-r--r--src/qml/jsruntime/qv4debugging_p.h46
-rw-r--r--src/qml/jsruntime/qv4domerrors.cpp35
-rw-r--r--src/qml/jsruntime/qv4domerrors_p.h57
-rw-r--r--src/qml/jsruntime/qv4engine.cpp1781
-rw-r--r--src/qml/jsruntime/qv4engine_p.h344
-rw-r--r--src/qml/jsruntime/qv4enginebase_p.h68
-rw-r--r--src/qml/jsruntime/qv4errorobject.cpp81
-rw-r--r--src/qml/jsruntime/qv4errorobject_p.h59
-rw-r--r--src/qml/jsruntime/qv4estable.cpp63
-rw-r--r--src/qml/jsruntime/qv4estable_p.h51
-rw-r--r--src/qml/jsruntime/qv4executableallocator.cpp58
-rw-r--r--src/qml/jsruntime/qv4executableallocator_p.h60
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit.cpp745
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit_p.h344
-rw-r--r--src/qml/jsruntime/qv4function.cpp235
-rw-r--r--src/qml/jsruntime/qv4function_p.h101
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp397
-rw-r--r--src/qml/jsruntime/qv4functionobject_p.h262
-rw-r--r--src/qml/jsruntime/qv4functiontable_noop.cpp40
-rw-r--r--src/qml/jsruntime/qv4functiontable_p.h42
-rw-r--r--src/qml/jsruntime/qv4functiontable_unix.cpp40
-rw-r--r--src/qml/jsruntime/qv4functiontable_win64.cpp46
-rw-r--r--src/qml/jsruntime/qv4generatorobject.cpp92
-rw-r--r--src/qml/jsruntime/qv4generatorobject_p.h50
-rw-r--r--src/qml/jsruntime/qv4global_p.h70
-rw-r--r--src/qml/jsruntime/qv4globalobject.cpp114
-rw-r--r--src/qml/jsruntime/qv4globalobject_p.h44
-rw-r--r--src/qml/jsruntime/qv4identifier.cpp207
-rw-r--r--src/qml/jsruntime/qv4identifier_p.h173
-rw-r--r--src/qml/jsruntime/qv4identifierhash.cpp190
-rw-r--r--src/qml/jsruntime/qv4identifierhash_p.h63
-rw-r--r--src/qml/jsruntime/qv4identifierhashdata_p.h86
-rw-r--r--src/qml/jsruntime/qv4identifiertable.cpp90
-rw-r--r--src/qml/jsruntime/qv4identifiertable_p.h51
-rw-r--r--src/qml/jsruntime/qv4include.cpp124
-rw-r--r--src/qml/jsruntime/qv4include_p.h54
-rw-r--r--src/qml/jsruntime/qv4internalclass.cpp351
-rw-r--r--src/qml/jsruntime/qv4internalclass_p.h146
-rw-r--r--src/qml/jsruntime/qv4iterator.cpp40
-rw-r--r--src/qml/jsruntime/qv4iterator_p.h41
-rw-r--r--src/qml/jsruntime/qv4jscall.cpp46
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h635
-rw-r--r--src/qml/jsruntime/qv4jsonobject.cpp272
-rw-r--r--src/qml/jsruntime/qv4jsonobject_p.h49
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp306
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h196
-rw-r--r--src/qml/jsruntime/qv4managed.cpp54
-rw-r--r--src/qml/jsruntime/qv4managed_p.h61
-rw-r--r--src/qml/jsruntime/qv4mapiterator.cpp40
-rw-r--r--src/qml/jsruntime/qv4mapiterator_p.h42
-rw-r--r--src/qml/jsruntime/qv4mapobject.cpp74
-rw-r--r--src/qml/jsruntime/qv4mapobject_p.h50
-rw-r--r--src/qml/jsruntime/qv4math_p.h49
-rw-r--r--src/qml/jsruntime/qv4mathobject.cpp168
-rw-r--r--src/qml/jsruntime/qv4mathobject_p.h40
-rw-r--r--src/qml/jsruntime/qv4memberdata.cpp42
-rw-r--r--src/qml/jsruntime/qv4memberdata_p.h44
-rw-r--r--src/qml/jsruntime/qv4module.cpp76
-rw-r--r--src/qml/jsruntime/qv4module_p.h40
-rw-r--r--src/qml/jsruntime/qv4numberobject.cpp51
-rw-r--r--src/qml/jsruntime/qv4numberobject_p.h42
-rw-r--r--src/qml/jsruntime/qv4object.cpp237
-rw-r--r--src/qml/jsruntime/qv4object_p.h56
-rw-r--r--src/qml/jsruntime/qv4objectiterator.cpp47
-rw-r--r--src/qml/jsruntime/qv4objectiterator_p.h40
-rw-r--r--src/qml/jsruntime/qv4objectproto.cpp98
-rw-r--r--src/qml/jsruntime/qv4objectproto_p.h44
-rw-r--r--src/qml/jsruntime/qv4persistent.cpp182
-rw-r--r--src/qml/jsruntime/qv4persistent_p.h82
-rw-r--r--src/qml/jsruntime/qv4profiling.cpp51
-rw-r--r--src/qml/jsruntime/qv4profiling_p.h95
-rw-r--r--src/qml/jsruntime/qv4promiseobject.cpp140
-rw-r--r--src/qml/jsruntime/qv4promiseobject_p.h46
-rw-r--r--src/qml/jsruntime/qv4property_p.h42
-rw-r--r--src/qml/jsruntime/qv4propertykey.cpp55
-rw-r--r--src/qml/jsruntime/qv4propertykey_p.h138
-rw-r--r--src/qml/jsruntime/qv4proxy.cpp204
-rw-r--r--src/qml/jsruntime/qv4proxy_p.h57
-rw-r--r--src/qml/jsruntime/qv4qmetaobjectwrapper.cpp111
-rw-r--r--src/qml/jsruntime/qv4qmetaobjectwrapper_p.h120
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp535
-rw-r--r--src/qml/jsruntime/qv4qmlcontext_p.h75
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp2878
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h400
-rw-r--r--src/qml/jsruntime/qv4referenceobject.cpp10
-rw-r--r--src/qml/jsruntime/qv4referenceobject_p.h171
-rw-r--r--src/qml/jsruntime/qv4reflect.cpp57
-rw-r--r--src/qml/jsruntime/qv4reflect_p.h40
-rw-r--r--src/qml/jsruntime/qv4regexp.cpp148
-rw-r--r--src/qml/jsruntime/qv4regexp_p.h46
-rw-r--r--src/qml/jsruntime/qv4regexpobject.cpp206
-rw-r--r--src/qml/jsruntime/qv4regexpobject_p.h88
-rw-r--r--src/qml/jsruntime/qv4resolvedtypereference.cpp98
-rw-r--r--src/qml/jsruntime/qv4resolvedtypereference_p.h113
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp563
-rw-r--r--src/qml/jsruntime/qv4runtime_p.h43
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h254
-rw-r--r--src/qml/jsruntime/qv4runtimecodegen.cpp44
-rw-r--r--src/qml/jsruntime/qv4runtimecodegen_p.h44
-rw-r--r--src/qml/jsruntime/qv4scopedvalue_p.h61
-rw-r--r--src/qml/jsruntime/qv4script.cpp85
-rw-r--r--src/qml/jsruntime/qv4script_p.h48
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp1188
-rw-r--r--src/qml/jsruntime/qv4sequenceobject_p.h172
-rw-r--r--src/qml/jsruntime/qv4setiterator.cpp40
-rw-r--r--src/qml/jsruntime/qv4setiterator_p.h42
-rw-r--r--src/qml/jsruntime/qv4setobject.cpp68
-rw-r--r--src/qml/jsruntime/qv4setobject_p.h48
-rw-r--r--src/qml/jsruntime/qv4sparsearray.cpp46
-rw-r--r--src/qml/jsruntime/qv4sparsearray_p.h92
-rw-r--r--src/qml/jsruntime/qv4sqlerrors.cpp27
-rw-r--r--src/qml/jsruntime/qv4sqlerrors_p.h38
-rw-r--r--src/qml/jsruntime/qv4stackframe.cpp96
-rw-r--r--src/qml/jsruntime/qv4stackframe_p.h328
-rw-r--r--src/qml/jsruntime/qv4string.cpp120
-rw-r--r--src/qml/jsruntime/qv4string_p.h107
-rw-r--r--src/qml/jsruntime/qv4stringiterator.cpp44
-rw-r--r--src/qml/jsruntime/qv4stringiterator_p.h42
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp180
-rw-r--r--src/qml/jsruntime/qv4stringobject_p.h44
-rw-r--r--src/qml/jsruntime/qv4symbol.cpp53
-rw-r--r--src/qml/jsruntime/qv4symbol_p.h44
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp584
-rw-r--r--src/qml/jsruntime/qv4typedarray_p.h70
-rw-r--r--src/qml/jsruntime/qv4urlobject.cpp1539
-rw-r--r--src/qml/jsruntime/qv4urlobject_p.h293
-rw-r--r--src/qml/jsruntime/qv4value.cpp192
-rw-r--r--src/qml/jsruntime/qv4value_p.h144
-rw-r--r--src/qml/jsruntime/qv4variantobject.cpp80
-rw-r--r--src/qml/jsruntime/qv4variantobject_p.h44
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp243
-rw-r--r--src/qml/jsruntime/qv4vme_moth_p.h46
-rw-r--r--src/qml/jsruntime/qv4vtable_p.h151
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 *>(&currentStackFrame->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 &param) {
+ 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> &parameters)
@@ -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 &param = 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 &parameterName : 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> &parameters);
@@ -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 &param = 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 &param = *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) \