2022-05-13 13:12:05 +00:00
|
|
|
// 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
|
2013-04-15 09:50:16 +00:00
|
|
|
#include <qv4engine_p.h>
|
2018-08-16 11:06:22 +00:00
|
|
|
|
|
|
|
#include <private/qv4compileddata_p.h>
|
|
|
|
#include <private/qv4codegen_p.h>
|
2019-05-23 12:57:09 +00:00
|
|
|
#include <private/qqmljsdiagnosticmessage_p.h>
|
2018-08-16 11:06:22 +00:00
|
|
|
|
|
|
|
#include <QtCore/QTextStream>
|
|
|
|
#include <QDateTime>
|
|
|
|
#include <QDir>
|
|
|
|
#include <QFileInfo>
|
2018-10-09 12:58:01 +00:00
|
|
|
#include <QLoggingCategory>
|
2019-02-13 14:54:14 +00:00
|
|
|
#if QT_CONFIG(regularexpression)
|
|
|
|
#include <QRegularExpression>
|
|
|
|
#endif
|
2020-09-07 14:01:30 +00:00
|
|
|
#include <QtCore/qiterable.h>
|
2018-08-16 11:06:22 +00:00
|
|
|
|
2016-11-28 14:33:48 +00:00
|
|
|
#include <qv4qmlcontext_p.h>
|
2015-02-14 21:46:41 +00:00
|
|
|
#include <qv4value_p.h>
|
2013-04-15 09:50:16 +00:00
|
|
|
#include <qv4object_p.h>
|
|
|
|
#include <qv4objectproto_p.h>
|
2013-09-30 11:48:05 +00:00
|
|
|
#include <qv4objectiterator_p.h>
|
2018-05-27 13:04:23 +00:00
|
|
|
#include <qv4setiterator_p.h>
|
2018-05-29 20:41:34 +00:00
|
|
|
#include <qv4mapiterator_p.h>
|
Add Array Iterator objects from ES6 (22.1.5)
And implement / expose them via:
22.1.3.4 - Array.prototype.entries()
22.1.3.13 - Array.prototype.keys()
22.1.3.29 - Array.prototype.values()
22.1.3.31 - Array.prototype[Symbol.iterator]
Most tests for Array iterators now pass.
At the same time, expose them on TypedArray's prototype:
- 22.2.3.15 %TypedArray%.prototype.keys
- 22.2.3.29 %TypedArray%.prototype.values
- 22.2.3.6 %TypedArray%.prototype.entries
- 22.2.3.31 %TypedArray%.prototype[Symbol.iterator]
For TypedArray, test coverage improves a tiny bit (3 passing tests), but the
vast majority fail as it seems like the object structure for TypedArray is
currently incomplete as far as ES6 expects.
It seems that ES6 expects the object structure to be:
* %TypedArray% (inherits FunctionObject)
(this is the TypedArray intrinsic object, and responsible for initializing
the TypedArray instances)
* All the TypedArray ctors (e.g. UInt8Array)
These inherit %TypedArray%, and make a super call to it to do their work
* %TypedArrayPrototype% (inherits Object)
(this is the initial prototype for %TypedArray%)
* All the ctors have their own separate instance of this
* The instances also make use it
So, for instance, a lot of the tests attempt to access the prototype like:
var proto = Object.getPrototypeOf(Int8Array)
var keys = proto.prototype.keys
As ES6 expects Int8Array.prototype to be %TypedArray% (22.2.5), this expands to:
Object.getPrototypeOf(%TypedArray%)
which it expects to be %TypedArrayPrototype%.
But since we have no intrinsic object, and the ctors inherit
FunctionObject, we instead return the wrong prototype into 'var proto'.
Change-Id: I5e1a95a0420ecb70a0e35a5df3f65557510c5925
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-02-09 18:58:50 +00:00
|
|
|
#include <qv4arrayiterator_p.h>
|
2013-04-15 09:50:16 +00:00
|
|
|
#include <qv4arrayobject_p.h>
|
|
|
|
#include <qv4booleanobject_p.h>
|
|
|
|
#include <qv4globalobject_p.h>
|
|
|
|
#include <qv4errorobject_p.h>
|
|
|
|
#include <qv4functionobject_p.h>
|
2013-05-08 12:24:30 +00:00
|
|
|
#include "qv4function_p.h"
|
2013-04-15 09:50:16 +00:00
|
|
|
#include <qv4mathobject_p.h>
|
|
|
|
#include <qv4numberobject_p.h>
|
|
|
|
#include <qv4regexpobject_p.h>
|
2013-11-21 13:26:08 +00:00
|
|
|
#include <qv4regexp_p.h>
|
2018-04-03 13:23:07 +00:00
|
|
|
#include "qv4symbol_p.h"
|
2018-05-27 13:04:23 +00:00
|
|
|
#include "qv4setobject_p.h"
|
2018-05-29 20:41:34 +00:00
|
|
|
#include "qv4mapobject_p.h"
|
2013-05-14 00:01:04 +00:00
|
|
|
#include <qv4variantobject_p.h>
|
2013-04-15 09:50:16 +00:00
|
|
|
#include <qv4runtime_p.h>
|
2015-02-12 20:16:42 +00:00
|
|
|
#include <private/qv4mm_p.h>
|
2013-04-15 09:50:16 +00:00
|
|
|
#include <qv4argumentsobject_p.h>
|
|
|
|
#include <qv4dateobject_p.h>
|
|
|
|
#include <qv4jsonobject_p.h>
|
|
|
|
#include <qv4stringobject_p.h>
|
2013-06-27 21:02:45 +00:00
|
|
|
#include <qv4identifiertable_p.h>
|
2013-04-15 09:50:16 +00:00
|
|
|
#include "qv4debugging_p.h"
|
2014-01-22 16:46:20 +00:00
|
|
|
#include "qv4profiling_p.h"
|
2013-04-15 09:50:16 +00:00
|
|
|
#include "qv4executableallocator_p.h"
|
Add Array Iterator objects from ES6 (22.1.5)
And implement / expose them via:
22.1.3.4 - Array.prototype.entries()
22.1.3.13 - Array.prototype.keys()
22.1.3.29 - Array.prototype.values()
22.1.3.31 - Array.prototype[Symbol.iterator]
Most tests for Array iterators now pass.
At the same time, expose them on TypedArray's prototype:
- 22.2.3.15 %TypedArray%.prototype.keys
- 22.2.3.29 %TypedArray%.prototype.values
- 22.2.3.6 %TypedArray%.prototype.entries
- 22.2.3.31 %TypedArray%.prototype[Symbol.iterator]
For TypedArray, test coverage improves a tiny bit (3 passing tests), but the
vast majority fail as it seems like the object structure for TypedArray is
currently incomplete as far as ES6 expects.
It seems that ES6 expects the object structure to be:
* %TypedArray% (inherits FunctionObject)
(this is the TypedArray intrinsic object, and responsible for initializing
the TypedArray instances)
* All the TypedArray ctors (e.g. UInt8Array)
These inherit %TypedArray%, and make a super call to it to do their work
* %TypedArrayPrototype% (inherits Object)
(this is the initial prototype for %TypedArray%)
* All the ctors have their own separate instance of this
* The instances also make use it
So, for instance, a lot of the tests attempt to access the prototype like:
var proto = Object.getPrototypeOf(Int8Array)
var keys = proto.prototype.keys
As ES6 expects Int8Array.prototype to be %TypedArray% (22.2.5), this expands to:
Object.getPrototypeOf(%TypedArray%)
which it expects to be %TypedArrayPrototype%.
But since we have no intrinsic object, and the ctors inherit
FunctionObject, we instead return the wrong prototype into 'var proto'.
Change-Id: I5e1a95a0420ecb70a0e35a5df3f65557510c5925
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-02-09 18:58:50 +00:00
|
|
|
#include "qv4iterator_p.h"
|
2018-04-26 11:12:38 +00:00
|
|
|
#include "qv4stringiterator_p.h"
|
2018-04-27 09:41:13 +00:00
|
|
|
#include "qv4generatorobject_p.h"
|
2018-06-15 21:00:32 +00:00
|
|
|
#include "qv4reflect_p.h"
|
2018-06-17 08:57:21 +00:00
|
|
|
#include "qv4proxy_p.h"
|
2018-06-27 12:18:17 +00:00
|
|
|
#include "qv4stackframe_p.h"
|
2018-08-24 13:57:31 +00:00
|
|
|
#include "qv4atomics_p.h"
|
2020-04-02 13:48:17 +00:00
|
|
|
#include "qv4urlobject_p.h"
|
2021-02-08 14:42:59 +00:00
|
|
|
#include "qv4jscall_p.h"
|
2021-04-20 08:43:11 +00:00
|
|
|
#include "qv4variantobject_p.h"
|
2013-05-21 15:06:36 +00:00
|
|
|
#include "qv4sequenceobject_p.h"
|
2013-06-07 09:21:18 +00:00
|
|
|
#include "qv4qobjectwrapper_p.h"
|
2014-03-31 13:48:02 +00:00
|
|
|
#include "qv4memberdata_p.h"
|
2014-09-10 12:50:28 +00:00
|
|
|
#include "qv4arraybuffer_p.h"
|
2014-09-10 14:39:23 +00:00
|
|
|
#include "qv4dataview_p.h"
|
2015-07-16 16:35:55 +00:00
|
|
|
#include "qv4promiseobject_p.h"
|
2014-09-11 13:37:31 +00:00
|
|
|
#include "qv4typedarray_p.h"
|
2015-01-02 13:37:26 +00:00
|
|
|
#include <private/qjsvalue_p.h>
|
|
|
|
#include <private/qqmltypewrapper_p.h>
|
|
|
|
#include <private/qqmlvaluetypewrapper_p.h>
|
|
|
|
#include <private/qqmlvaluetype_p.h>
|
|
|
|
#include <private/qqmllistwrapper_p.h>
|
|
|
|
#include <private/qqmllist_p.h>
|
2018-07-10 12:52:34 +00:00
|
|
|
#include <private/qqmltypeloader_p.h>
|
2019-04-05 07:59:10 +00:00
|
|
|
#include <private/qqmlbuiltinfunctions_p.h>
|
2017-12-18 18:43:56 +00:00
|
|
|
#if QT_CONFIG(qml_locale)
|
2015-01-02 13:37:26 +00:00
|
|
|
#include <private/qqmllocale_p.h>
|
2017-12-18 18:43:56 +00:00
|
|
|
#endif
|
2019-04-05 07:59:10 +00:00
|
|
|
#if QT_CONFIG(qml_xml_http_request)
|
|
|
|
#include <private/qv4domerrors_p.h>
|
|
|
|
#include <private/qqmlxmlhttprequest_p.h>
|
|
|
|
#endif
|
|
|
|
#include <private/qv4sqlerrors_p.h>
|
2018-07-10 12:52:34 +00:00
|
|
|
#include <qqmlfile.h>
|
2019-12-09 14:26:14 +00:00
|
|
|
#include <qmetatype.h>
|
2020-09-17 08:17:13 +00:00
|
|
|
#include <qsequentialiterable.h>
|
2013-05-28 07:36:04 +00:00
|
|
|
|
2021-02-08 14:42:59 +00:00
|
|
|
#include <private/qqmlengine_p.h>
|
|
|
|
|
2013-11-11 10:22:24 +00:00
|
|
|
#if USE(PTHREADS)
|
|
|
|
# include <pthread.h>
|
2017-01-26 10:46:16 +00:00
|
|
|
#if !defined(Q_OS_INTEGRITY)
|
2013-12-17 15:11:19 +00:00
|
|
|
# include <sys/resource.h>
|
2017-01-26 10:46:16 +00:00
|
|
|
#endif
|
2014-01-21 09:55:18 +00:00
|
|
|
#if HAVE(PTHREAD_NP_H)
|
|
|
|
# include <pthread_np.h>
|
|
|
|
#endif
|
2013-11-11 10:22:24 +00:00
|
|
|
#endif
|
|
|
|
|
2014-12-15 07:47:35 +00:00
|
|
|
#ifdef V4_USE_VALGRIND
|
|
|
|
#include <valgrind/memcheck.h>
|
|
|
|
#endif
|
|
|
|
|
2019-04-05 07:59:10 +00:00
|
|
|
Q_DECLARE_METATYPE(QList<int>)
|
|
|
|
|
2013-06-24 13:28:00 +00:00
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
2020-12-09 11:04:52 +00:00
|
|
|
DEFINE_BOOL_CONFIG_OPTION(disableDiskCache, QML_DISABLE_DISK_CACHE);
|
|
|
|
DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE);
|
|
|
|
|
2013-04-19 11:03:42 +00:00
|
|
|
using namespace QV4;
|
2012-11-17 20:54:26 +00:00
|
|
|
|
2022-03-04 10:46:58 +00:00
|
|
|
// 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.
|
2013-06-04 08:05:51 +00:00
|
|
|
static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1);
|
2022-03-04 10:46:58 +00:00
|
|
|
int ExecutionEngine::s_maxCallDepth = -1;
|
|
|
|
int ExecutionEngine::s_jitCallCountThreshold = 3;
|
|
|
|
int ExecutionEngine::s_maxJSStackSize = 4 * 1024 * 1024;
|
|
|
|
int ExecutionEngine::s_maxGCStackSize = 2 * 1024 * 1024;
|
2013-06-04 08:05:51 +00:00
|
|
|
|
2017-12-05 09:45:14 +00:00
|
|
|
ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
|
2013-09-18 07:30:45 +00:00
|
|
|
{
|
2017-08-04 16:53:51 +00:00
|
|
|
return b->engine()->throwTypeError();
|
2013-09-18 07:30:45 +00:00
|
|
|
}
|
|
|
|
|
2014-12-30 15:38:20 +00:00
|
|
|
|
2019-04-05 07:59:10 +00:00
|
|
|
template <typename ReturnType>
|
|
|
|
ReturnType convertJSValueToVariantType(const QJSValue &value)
|
|
|
|
{
|
2022-05-06 12:07:29 +00:00
|
|
|
const QVariant variant = value.toVariant();
|
|
|
|
return variant.metaType() == QMetaType::fromType<QJSValue>()
|
|
|
|
? ReturnType()
|
|
|
|
: variant.value<ReturnType>();
|
2019-04-05 07:59:10 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 14:26:14 +00:00
|
|
|
struct JSArrayIterator {
|
|
|
|
QJSValue const* data;
|
|
|
|
quint32 index;
|
|
|
|
};
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
void createNewIteratorIfNonExisting(void **iterator) {
|
|
|
|
if (*iterator == nullptr)
|
|
|
|
*iterator = new JSArrayIterator;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-07 14:01:30 +00:00
|
|
|
static QtMetaContainerPrivate::QMetaSequenceInterface emptySequenceInterface()
|
|
|
|
{
|
|
|
|
// set up some functions so that non-array QSequentialIterables do not crash
|
|
|
|
// but instead appear as an empty sequence
|
2019-12-09 14:26:14 +00:00
|
|
|
|
2020-09-07 14:01:30 +00:00
|
|
|
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;
|
|
|
|
}
|
2019-12-09 14:26:14 +00:00
|
|
|
|
2020-09-07 14:01:30 +00:00
|
|
|
static QtMetaContainerPrivate::QMetaSequenceInterface sequenceInterface()
|
|
|
|
{
|
|
|
|
using namespace QtMetaContainerPrivate;
|
|
|
|
QMetaSequenceInterface iface;
|
2020-09-17 08:15:49 +00:00
|
|
|
iface.valueMetaType = QtPrivate::qMetaTypeInterfaceForType<QVariant>();
|
2020-09-07 14:01:30 +00:00
|
|
|
iface.iteratorCapabilities = RandomAccessCapability | BiDirectionalCapability | ForwardCapability;
|
|
|
|
iface.addRemoveCapabilities = CanAddAtEnd;
|
|
|
|
iface.sizeFn = [](const void *p) -> qsizetype {
|
2019-12-09 14:26:14 +00:00
|
|
|
return static_cast<QJSValue const *>(p)->property(QString::fromLatin1("length")).toInt();
|
|
|
|
};
|
2020-09-07 14:01:30 +00:00
|
|
|
|
2019-12-09 14:26:14 +00:00
|
|
|
/* Lifetime management notes:
|
2020-09-07 14:01:30 +00:00
|
|
|
* 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
|
|
|
|
*/
|
2019-12-09 14:26:14 +00:00
|
|
|
|
2020-09-07 14:01:30 +00:00
|
|
|
iface.valueAtIndexFn = [](const void *iterable, qsizetype index, void *dataPtr) -> void {
|
2020-07-10 08:33:49 +00:00
|
|
|
auto *data = static_cast<QVariant *>(dataPtr);
|
|
|
|
*data = static_cast<QJSValue const *>(iterable)->property(quint32(index)).toVariant();
|
2019-12-09 14:26:14 +00:00
|
|
|
};
|
2020-09-07 14:01:30 +00:00
|
|
|
iface.createIteratorFn = [](void *iterable, QMetaSequenceInterface::Position pos) {
|
|
|
|
void *iterator = nullptr;
|
|
|
|
createNewIteratorIfNonExisting(&iterator);
|
|
|
|
auto jsArrayIterator = static_cast<JSArrayIterator *>(iterator);
|
2019-12-09 14:26:14 +00:00
|
|
|
jsArrayIterator->index = 0;
|
|
|
|
jsArrayIterator->data = reinterpret_cast<QJSValue const*>(iterable);
|
2020-09-07 14:01:30 +00:00
|
|
|
if (pos == QMetaSequenceInterface::AtEnd) {
|
|
|
|
auto length = static_cast<QJSValue const *>(iterable)->property(
|
|
|
|
QString::fromLatin1("length")).toInt();
|
2020-07-10 08:33:49 +00:00
|
|
|
jsArrayIterator->index = quint32(length);
|
|
|
|
}
|
2020-09-07 14:01:30 +00:00
|
|
|
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);
|
2019-12-09 14:26:14 +00:00
|
|
|
};
|
2020-09-07 14:01:30 +00:00
|
|
|
iface.advanceConstIteratorFn = [](void *iterator, qsizetype advanceBy) {
|
|
|
|
static_cast<JSArrayIterator *>(iterator)->index += quint32(advanceBy);
|
2019-12-09 14:26:14 +00:00
|
|
|
};
|
2020-09-07 14:01:30 +00:00
|
|
|
iface.valueAtIteratorFn = [](const void *iterator, void *dataPtr) -> void {
|
|
|
|
const auto *arrayIterator = static_cast<const JSArrayIterator *>(iterator);
|
|
|
|
const QJSValue *jsArray = arrayIterator->data;
|
2020-07-10 08:33:49 +00:00
|
|
|
auto *data = static_cast<QVariant *>(dataPtr);
|
|
|
|
*data = jsArray->property(arrayIterator->index).toVariant();
|
2019-12-09 14:26:14 +00:00
|
|
|
};
|
2020-09-07 14:01:30 +00:00
|
|
|
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;
|
2019-12-09 14:26:14 +00:00
|
|
|
};
|
2020-09-07 14:01:30 +00:00
|
|
|
iface.compareConstIteratorFn = [](const void *p, const void *other) {
|
|
|
|
auto this_ = static_cast<const JSArrayIterator *>(p);
|
|
|
|
auto that_ = static_cast<const JSArrayIterator *>(other);
|
2019-12-09 14:26:14 +00:00
|
|
|
return this_->index == that_->index && this_->data == that_->data;
|
|
|
|
};
|
2020-09-07 14:01:30 +00:00
|
|
|
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);
|
2019-12-09 14:26:14 +00:00
|
|
|
};
|
2020-09-07 14:01:30 +00:00
|
|
|
return iface;
|
|
|
|
}
|
|
|
|
|
|
|
|
static QSequentialIterable jsvalueToSequence (const QJSValue& value) {
|
|
|
|
using namespace QtMetaTypePrivate;
|
|
|
|
using namespace QtMetaContainerPrivate;
|
|
|
|
|
|
|
|
|
|
|
|
if (!value.isArray()) {
|
|
|
|
static QMetaSequenceInterface emptySequence = emptySequenceInterface();
|
|
|
|
return QSequentialIterable(QMetaSequence(&emptySequence), nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static QMetaSequenceInterface sequence = sequenceInterface();
|
|
|
|
return QSequentialIterable(QMetaSequence(&sequence), &value);
|
2019-12-09 14:26:14 +00:00
|
|
|
}
|
|
|
|
|
2022-03-04 10:46:58 +00:00
|
|
|
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 {
|
|
|
|
ok = false;
|
|
|
|
s_maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok);
|
|
|
|
if (!ok || s_maxCallDepth <= 0) {
|
|
|
|
#if defined(QT_NO_DEBUG) && !defined(__SANITIZE_ADDRESS__) && !__has_feature(address_sanitizer)
|
|
|
|
#ifdef Q_OS_QNX
|
|
|
|
s_maxCallDepth = 640; // QNX's stack is only 512k by default
|
2022-04-07 15:10:55 +00:00
|
|
|
#elif defined(Q_OS_ANDROID)
|
|
|
|
// In experiments, it started crashing at 1059.
|
|
|
|
s_maxCallDepth = 1000;
|
2022-03-04 10:46:58 +00:00
|
|
|
#else
|
|
|
|
s_maxCallDepth = 1234;
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
// no (tail call) optimization is done, so there'll be a lot mare stack frames active
|
2022-04-07 15:10:55 +00:00
|
|
|
#ifdef Q_OS_ANDROID
|
|
|
|
// Android's stack seems to be about 1mb.
|
|
|
|
// In experiments, it started crashing at 82.
|
|
|
|
s_maxCallDepth = 80;
|
|
|
|
#else
|
2022-03-04 10:46:58 +00:00
|
|
|
s_maxCallDepth = 200;
|
2022-04-07 15:10:55 +00:00
|
|
|
#endif
|
2022-03-04 10:46:58 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_ASSERT(s_maxCallDepth > 0);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-12-19 16:14:15 +00:00
|
|
|
ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
|
2017-05-12 07:33:25 +00:00
|
|
|
: executableAllocator(new QV4::ExecutableAllocator)
|
2013-05-12 10:07:15 +00:00
|
|
|
, regExpAllocator(new QV4::ExecutableAllocator)
|
2013-04-19 09:47:50 +00:00
|
|
|
, bumperPointerAllocator(new WTF::BumpPointerAllocator)
|
2013-09-03 10:40:07 +00:00
|
|
|
, jsStack(new WTF::PageAllocation)
|
2017-03-09 09:36:16 +00:00
|
|
|
, gcStack(new WTF::PageAllocation)
|
2018-02-21 09:41:54 +00:00
|
|
|
, globalCode(nullptr)
|
2017-12-19 16:14:15 +00:00
|
|
|
, publicEngine(jsEngine)
|
2022-03-04 10:46:58 +00:00
|
|
|
, m_engineId(engineSerial.fetchAndAddOrdered(2))
|
2018-02-21 09:41:54 +00:00
|
|
|
, regExpCache(nullptr)
|
|
|
|
, m_multiplyWrappedQObjects(nullptr)
|
2019-04-17 13:15:52 +00:00
|
|
|
#if QT_CONFIG(qml_jit)
|
2018-02-16 13:19:25 +00:00
|
|
|
, m_canAllocateExecutableMemory(OSAllocator::canAllocateExecutableMemory())
|
|
|
|
#endif
|
2019-04-05 07:59:10 +00:00
|
|
|
#if QT_CONFIG(qml_xml_http_request)
|
|
|
|
, m_xmlHttpRequestData(nullptr)
|
|
|
|
#endif
|
|
|
|
, m_qmlEngine(nullptr)
|
2012-11-17 20:54:26 +00:00
|
|
|
{
|
2022-03-04 10:46:58 +00:00
|
|
|
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();
|
2016-04-25 13:01:04 +00:00
|
|
|
}
|
2013-05-07 09:54:45 +00:00
|
|
|
}
|
2013-03-05 15:11:22 +00:00
|
|
|
|
2022-03-04 10:46:58 +00:00
|
|
|
memoryManager = new QV4::MemoryManager(this);
|
2013-11-11 10:22:24 +00:00
|
|
|
// reserve space for the JS stack
|
2019-06-27 11:43:04 +00:00
|
|
|
// we allow it to grow to a bit more than m_maxJSStackSize, as we can overshoot due to ScopedValues
|
2017-03-09 09:36:16 +00:00
|
|
|
// allocated outside of JIT'ed methods.
|
2022-03-04 10:46:58 +00:00
|
|
|
*jsStack = WTF::PageAllocation::allocate(
|
|
|
|
s_maxJSStackSize + 256*1024, WTF::OSAllocator::JSVMStackPages,
|
|
|
|
/* writable */ true, /* executable */ false, /* includesGuardPages */ true);
|
2014-01-24 21:55:39 +00:00
|
|
|
jsStackBase = (Value *)jsStack->base();
|
2016-05-26 15:22:34 +00:00
|
|
|
#ifdef V4_USE_VALGRIND
|
2019-06-27 11:43:04 +00:00
|
|
|
VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, m_maxJSStackSize + 256*1024);
|
2016-05-26 15:22:34 +00:00
|
|
|
#endif
|
|
|
|
|
2013-09-03 10:40:07 +00:00
|
|
|
jsStackTop = jsStackBase;
|
|
|
|
|
2022-03-04 10:46:58 +00:00
|
|
|
*gcStack = WTF::PageAllocation::allocate(s_maxGCStackSize, WTF::OSAllocator::JSVMStackPages,
|
2017-03-09 09:36:16 +00:00
|
|
|
/* writable */ true, /* executable */ false,
|
|
|
|
/* includesGuardPages */ true);
|
|
|
|
|
2015-03-13 16:21:18 +00:00
|
|
|
exceptionValue = jsAlloca(1);
|
2018-05-25 07:03:51 +00:00
|
|
|
*exceptionValue = Encode::undefined();
|
2015-03-13 16:21:18 +00:00
|
|
|
globalObject = static_cast<Object *>(jsAlloca(1));
|
2015-03-25 12:49:51 +00:00
|
|
|
jsObjects = jsAlloca(NJSObjects);
|
2015-03-25 20:06:18 +00:00
|
|
|
typedArrayPrototype = static_cast<Object *>(jsAlloca(NTypedArrayTypes));
|
|
|
|
typedArrayCtors = static_cast<FunctionObject *>(jsAlloca(NTypedArrayTypes));
|
2015-04-26 07:22:17 +00:00
|
|
|
jsStrings = jsAlloca(NJSStrings);
|
2018-04-08 14:43:10 +00:00
|
|
|
jsSymbols = jsAlloca(NJSSymbols);
|
2015-03-13 16:21:18 +00:00
|
|
|
|
2013-11-11 10:22:24 +00:00
|
|
|
// set up stack limits
|
2022-03-04 10:46:58 +00:00
|
|
|
jsStackLimit = jsStackBase + s_maxJSStackSize/sizeof(Value);
|
2013-11-11 10:22:24 +00:00
|
|
|
|
2013-06-27 06:57:47 +00:00
|
|
|
identifierTable = new IdentifierTable(this);
|
2013-01-30 13:56:40 +00:00
|
|
|
|
2018-01-05 14:30:23 +00:00
|
|
|
memset(classes, 0, sizeof(classes));
|
|
|
|
classes[Class_Empty] = memoryManager->allocIC<InternalClass>();
|
|
|
|
classes[Class_Empty]->init(this);
|
|
|
|
|
2018-04-04 07:31:10 +00:00
|
|
|
classes[Class_MemberData] = classes[Class_Empty]->changeVTable(QV4::MemberData::staticVTable());
|
|
|
|
classes[Class_SimpleArrayData] = classes[Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable());
|
|
|
|
classes[Class_SparseArrayData] = classes[Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable());
|
|
|
|
classes[Class_ExecutionContext] = classes[Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable());
|
|
|
|
classes[Class_CallContext] = classes[Class_Empty]->changeVTable(QV4::CallContext::staticVTable());
|
|
|
|
classes[Class_QmlContext] = classes[Class_Empty]->changeVTable(QV4::QmlContext::staticVTable());
|
2013-05-12 13:00:48 +00:00
|
|
|
|
2018-04-03 13:23:07 +00:00
|
|
|
Scope scope(this);
|
|
|
|
Scoped<InternalClass> ic(scope);
|
|
|
|
ic = classes[Class_Empty]->changeVTable(QV4::Object::staticVTable());
|
|
|
|
jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(ic->d());
|
|
|
|
classes[Class_Object] = ic->changePrototype(objectPrototype()->d());
|
|
|
|
classes[Class_QmlContextWrapper] = classes[Class_Object]->changeVTable(QV4::QQmlContextWrapper::staticVTable());
|
|
|
|
|
|
|
|
ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype());
|
2018-05-04 08:52:44 +00:00
|
|
|
jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(ic->d(), /*init =*/ false);
|
2018-04-03 13:23:07 +00:00
|
|
|
classes[Class_String] = classes[Class_Empty]->changeVTable(QV4::String::staticVTable())->changePrototype(stringPrototype()->d());
|
|
|
|
Q_ASSERT(stringPrototype()->d() && classes[Class_String]->prototype);
|
|
|
|
|
|
|
|
jsObjects[SymbolProto] = memoryManager->allocate<SymbolPrototype>();
|
|
|
|
classes[Class_Symbol] = classes[EngineBase::Class_Empty]->changeVTable(QV4::Symbol::staticVTable())->changePrototype(symbolPrototype()->d());
|
|
|
|
|
2015-04-26 07:22:17 +00:00
|
|
|
jsStrings[String_Empty] = newIdentifier(QString());
|
|
|
|
jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined"));
|
|
|
|
jsStrings[String_null] = newIdentifier(QStringLiteral("null"));
|
|
|
|
jsStrings[String_true] = newIdentifier(QStringLiteral("true"));
|
|
|
|
jsStrings[String_false] = newIdentifier(QStringLiteral("false"));
|
|
|
|
jsStrings[String_boolean] = newIdentifier(QStringLiteral("boolean"));
|
|
|
|
jsStrings[String_number] = newIdentifier(QStringLiteral("number"));
|
|
|
|
jsStrings[String_string] = newIdentifier(QStringLiteral("string"));
|
2018-05-27 20:58:32 +00:00
|
|
|
jsStrings[String_default] = newIdentifier(QStringLiteral("default"));
|
2018-04-08 12:28:40 +00:00
|
|
|
jsStrings[String_symbol] = newIdentifier(QStringLiteral("symbol"));
|
2015-04-26 07:22:17 +00:00
|
|
|
jsStrings[String_object] = newIdentifier(QStringLiteral("object"));
|
|
|
|
jsStrings[String_function] = newIdentifier(QStringLiteral("function"));
|
|
|
|
jsStrings[String_length] = newIdentifier(QStringLiteral("length"));
|
|
|
|
jsStrings[String_prototype] = newIdentifier(QStringLiteral("prototype"));
|
|
|
|
jsStrings[String_constructor] = newIdentifier(QStringLiteral("constructor"));
|
|
|
|
jsStrings[String_arguments] = newIdentifier(QStringLiteral("arguments"));
|
|
|
|
jsStrings[String_caller] = newIdentifier(QStringLiteral("caller"));
|
|
|
|
jsStrings[String_callee] = newIdentifier(QStringLiteral("callee"));
|
|
|
|
jsStrings[String_this] = newIdentifier(QStringLiteral("this"));
|
|
|
|
jsStrings[String___proto__] = newIdentifier(QStringLiteral("__proto__"));
|
|
|
|
jsStrings[String_enumerable] = newIdentifier(QStringLiteral("enumerable"));
|
|
|
|
jsStrings[String_configurable] = newIdentifier(QStringLiteral("configurable"));
|
|
|
|
jsStrings[String_writable] = newIdentifier(QStringLiteral("writable"));
|
|
|
|
jsStrings[String_value] = newIdentifier(QStringLiteral("value"));
|
|
|
|
jsStrings[String_get] = newIdentifier(QStringLiteral("get"));
|
|
|
|
jsStrings[String_set] = newIdentifier(QStringLiteral("set"));
|
|
|
|
jsStrings[String_eval] = newIdentifier(QStringLiteral("eval"));
|
|
|
|
jsStrings[String_uintMax] = newIdentifier(QStringLiteral("4294967295"));
|
|
|
|
jsStrings[String_name] = newIdentifier(QStringLiteral("name"));
|
|
|
|
jsStrings[String_index] = newIdentifier(QStringLiteral("index"));
|
|
|
|
jsStrings[String_input] = newIdentifier(QStringLiteral("input"));
|
|
|
|
jsStrings[String_toString] = newIdentifier(QStringLiteral("toString"));
|
2018-08-14 17:54:31 +00:00
|
|
|
jsStrings[String_toLocaleString] = newIdentifier(QStringLiteral("toLocaleString"));
|
2015-04-26 07:22:17 +00:00
|
|
|
jsStrings[String_destroy] = newIdentifier(QStringLiteral("destroy"));
|
|
|
|
jsStrings[String_valueOf] = newIdentifier(QStringLiteral("valueOf"));
|
|
|
|
jsStrings[String_byteLength] = newIdentifier(QStringLiteral("byteLength"));
|
|
|
|
jsStrings[String_byteOffset] = newIdentifier(QStringLiteral("byteOffset"));
|
|
|
|
jsStrings[String_buffer] = newIdentifier(QStringLiteral("buffer"));
|
|
|
|
jsStrings[String_lastIndex] = newIdentifier(QStringLiteral("lastIndex"));
|
2018-05-11 20:23:56 +00:00
|
|
|
jsStrings[String_next] = newIdentifier(QStringLiteral("next"));
|
|
|
|
jsStrings[String_done] = newIdentifier(QStringLiteral("done"));
|
2018-05-14 12:12:33 +00:00
|
|
|
jsStrings[String_return] = newIdentifier(QStringLiteral("return"));
|
2018-05-31 14:06:34 +00:00
|
|
|
jsStrings[String_throw] = newIdentifier(QStringLiteral("throw"));
|
2018-08-08 11:55:17 +00:00
|
|
|
jsStrings[String_global] = newIdentifier(QStringLiteral("global"));
|
|
|
|
jsStrings[String_ignoreCase] = newIdentifier(QStringLiteral("ignoreCase"));
|
|
|
|
jsStrings[String_multiline] = newIdentifier(QStringLiteral("multiline"));
|
|
|
|
jsStrings[String_unicode] = newIdentifier(QStringLiteral("unicode"));
|
|
|
|
jsStrings[String_sticky] = newIdentifier(QStringLiteral("sticky"));
|
2018-08-10 07:21:11 +00:00
|
|
|
jsStrings[String_source] = newIdentifier(QStringLiteral("source"));
|
|
|
|
jsStrings[String_flags] = newIdentifier(QStringLiteral("flags"));
|
2012-11-17 20:54:26 +00:00
|
|
|
|
2018-04-08 14:43:10 +00:00
|
|
|
jsSymbols[Symbol_hasInstance] = Symbol::create(this, QStringLiteral("@Symbol.hasInstance"));
|
|
|
|
jsSymbols[Symbol_isConcatSpreadable] = Symbol::create(this, QStringLiteral("@Symbol.isConcatSpreadable"));
|
|
|
|
jsSymbols[Symbol_iterator] = Symbol::create(this, QStringLiteral("@Symbol.iterator"));
|
|
|
|
jsSymbols[Symbol_match] = Symbol::create(this, QStringLiteral("@Symbol.match"));
|
|
|
|
jsSymbols[Symbol_replace] = Symbol::create(this, QStringLiteral("@Symbol.replace"));
|
|
|
|
jsSymbols[Symbol_search] = Symbol::create(this, QStringLiteral("@Symbol.search"));
|
|
|
|
jsSymbols[Symbol_species] = Symbol::create(this, QStringLiteral("@Symbol.species"));
|
|
|
|
jsSymbols[Symbol_split] = Symbol::create(this, QStringLiteral("@Symbol.split"));
|
|
|
|
jsSymbols[Symbol_toPrimitive] = Symbol::create(this, QStringLiteral("@Symbol.toPrimitive"));
|
|
|
|
jsSymbols[Symbol_toStringTag] = Symbol::create(this, QStringLiteral("@Symbol.toStringTag"));
|
|
|
|
jsSymbols[Symbol_unscopables] = Symbol::create(this, QStringLiteral("@Symbol.unscopables"));
|
2018-06-17 08:57:21 +00:00
|
|
|
jsSymbols[Symbol_revokableProxy] = Symbol::create(this, QStringLiteral("@Proxy.revokableProxy"));
|
2018-04-08 14:43:10 +00:00
|
|
|
|
2017-05-15 07:56:05 +00:00
|
|
|
ic = newInternalClass(ArrayPrototype::staticVTable(), objectPrototype());
|
2018-01-05 14:30:23 +00:00
|
|
|
Q_ASSERT(ic->d()->prototype);
|
2018-06-23 21:07:23 +00:00
|
|
|
ic = ic->addMember(id_length()->propertyKey(), Attr_NotConfigurable|Attr_NotEnumerable);
|
2018-01-05 14:30:23 +00:00
|
|
|
Q_ASSERT(ic->d()->prototype);
|
|
|
|
jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(ic->d());
|
2018-04-04 07:31:10 +00:00
|
|
|
classes[Class_ArrayObject] = ic->changePrototype(arrayPrototype()->d());
|
2018-04-10 20:45:27 +00:00
|
|
|
jsObjects[PropertyListProto] = memoryManager->allocate<PropertyListPrototype>();
|
2013-08-29 12:31:32 +00:00
|
|
|
|
2018-01-05 14:30:23 +00:00
|
|
|
Scoped<InternalClass> argsClass(scope);
|
|
|
|
argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype());
|
2018-06-23 21:07:23 +00:00
|
|
|
argsClass = argsClass->addMember(id_length()->propertyKey(), Attr_NotEnumerable);
|
|
|
|
argsClass = argsClass->addMember(symbol_iterator()->propertyKey(), Attr_Data|Attr_NotEnumerable);
|
|
|
|
classes[Class_ArgumentsObject] = argsClass->addMember(id_callee()->propertyKey(), Attr_Data|Attr_NotEnumerable);
|
2017-11-06 14:59:01 +00:00
|
|
|
argsClass = newInternalClass(StrictArgumentsObject::staticVTable(), objectPrototype());
|
2018-06-23 21:07:23 +00:00
|
|
|
argsClass = argsClass->addMember(id_length()->propertyKey(), Attr_NotEnumerable);
|
|
|
|
argsClass = argsClass->addMember(symbol_iterator()->propertyKey(), Attr_Data|Attr_NotEnumerable);
|
|
|
|
classes[Class_StrictArgumentsObject] = argsClass->addMember(id_callee()->propertyKey(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
|
2013-11-21 12:15:46 +00:00
|
|
|
|
2015-03-13 16:21:18 +00:00
|
|
|
*static_cast<Value *>(globalObject) = newObject();
|
2015-08-07 11:56:31 +00:00
|
|
|
Q_ASSERT(globalObject->d()->vtable());
|
2013-03-14 13:03:04 +00:00
|
|
|
initRootContext();
|
2013-03-04 10:48:49 +00:00
|
|
|
|
2017-05-15 07:56:05 +00:00
|
|
|
ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype());
|
2018-06-23 21:07:23 +00:00
|
|
|
ic = ic->addMember(id_length()->propertyKey(), Attr_ReadOnly);
|
2018-04-04 07:31:10 +00:00
|
|
|
classes[Class_StringObject] = ic->changePrototype(stringPrototype()->d());
|
2018-09-23 09:15:52 +00:00
|
|
|
Q_ASSERT(classes[Class_StringObject]->verifyIndex(id_length()->propertyKey(), Heap::StringObject::LengthPropertyIndex));
|
2017-05-15 07:56:05 +00:00
|
|
|
|
2018-04-08 14:43:10 +00:00
|
|
|
classes[Class_SymbolObject] = newInternalClass(QV4::SymbolObject::staticVTable(), symbolPrototype());
|
|
|
|
|
2018-04-10 20:45:27 +00:00
|
|
|
jsObjects[NumberProto] = memoryManager->allocate<NumberPrototype>();
|
|
|
|
jsObjects[BooleanProto] = memoryManager->allocate<BooleanPrototype>();
|
|
|
|
jsObjects[DateProto] = memoryManager->allocate<DatePrototype>();
|
2013-08-29 19:23:04 +00:00
|
|
|
|
2018-11-02 09:43:07 +00:00
|
|
|
#if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS)
|
2018-09-23 13:46:25 +00:00
|
|
|
InternalClassEntry *index = nullptr;
|
|
|
|
#else
|
|
|
|
InternalClassEntry _index;
|
|
|
|
auto *index = &_index;
|
|
|
|
#endif
|
2017-05-15 07:56:05 +00:00
|
|
|
ic = newInternalClass(QV4::FunctionPrototype::staticVTable(), objectPrototype());
|
2018-09-16 17:44:45 +00:00
|
|
|
auto addProtoHasInstance = [&] {
|
|
|
|
// Add an invalid prototype slot, so that all function objects have the same layout
|
|
|
|
// This helps speed up instanceof operations and other things where we need to query
|
|
|
|
// prototype property (as we always know it's location)
|
|
|
|
ic = ic->addMember(id_prototype()->propertyKey(), Attr_Invalid, index);
|
|
|
|
Q_ASSERT(index->index == Heap::FunctionObject::Index_Prototype);
|
|
|
|
// add an invalid @hasInstance slot, so that we can quickly track whether the
|
|
|
|
// hasInstance method has been reimplemented. This is required for a fast
|
|
|
|
// instanceof implementation
|
|
|
|
ic = ic->addMember(symbol_hasInstance()->propertyKey(), Attr_Invalid, index);
|
|
|
|
Q_ASSERT(index->index == Heap::FunctionObject::Index_HasInstance);
|
|
|
|
};
|
|
|
|
addProtoHasInstance();
|
2018-01-05 14:30:23 +00:00
|
|
|
jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(ic->d());
|
2017-05-15 07:56:05 +00:00
|
|
|
ic = newInternalClass(FunctionObject::staticVTable(), functionPrototype());
|
2018-09-16 17:44:45 +00:00
|
|
|
addProtoHasInstance();
|
2018-01-05 14:30:23 +00:00
|
|
|
classes[Class_FunctionObject] = ic->d();
|
2018-09-23 13:46:25 +00:00
|
|
|
ic = ic->addMember(id_name()->propertyKey(), Attr_ReadOnly, index);
|
|
|
|
Q_ASSERT(index->index == Heap::ArrowFunction::Index_Name);
|
|
|
|
ic = ic->addMember(id_length()->propertyKey(), Attr_ReadOnly_ButConfigurable, index);
|
|
|
|
Q_ASSERT(index->index == Heap::ArrowFunction::Index_Length);
|
2018-09-18 15:01:59 +00:00
|
|
|
classes[Class_ArrowFunction] = ic->changeVTable(ArrowFunction::staticVTable());
|
2018-06-22 10:32:13 +00:00
|
|
|
ic = ic->changeVTable(MemberFunction::staticVTable());
|
|
|
|
classes[Class_MemberFunction] = ic->d();
|
|
|
|
ic = ic->changeVTable(GeneratorFunction::staticVTable());
|
2018-04-27 09:41:13 +00:00
|
|
|
classes[Class_GeneratorFunction] = ic->d();
|
2018-07-03 09:07:15 +00:00
|
|
|
ic = ic->changeVTable(MemberGeneratorFunction::staticVTable());
|
|
|
|
classes[Class_MemberGeneratorFunction] = ic->d();
|
2018-09-19 13:27:41 +00:00
|
|
|
|
|
|
|
ic = ic->changeMember(id_prototype()->propertyKey(), Attr_NotConfigurable|Attr_NotEnumerable);
|
|
|
|
ic = ic->changeVTable(ScriptFunction::staticVTable());
|
|
|
|
classes[Class_ScriptFunction] = ic->d();
|
|
|
|
ic = ic->changeVTable(ConstructorFunction::staticVTable());
|
|
|
|
classes[Class_ConstructorFunction] = ic->d();
|
|
|
|
|
2018-09-23 13:46:25 +00:00
|
|
|
classes[Class_ObjectProto] = classes[Class_Object]->addMember(id_constructor()->propertyKey(), Attr_NotEnumerable, index);
|
|
|
|
Q_ASSERT(index->index == Heap::FunctionObject::Index_ProtoConstructor);
|
2013-08-29 19:23:04 +00:00
|
|
|
|
2018-04-27 09:41:13 +00:00
|
|
|
jsObjects[GeneratorProto] = memoryManager->allocObject<GeneratorPrototype>(classes[Class_Object]);
|
|
|
|
classes[Class_GeneratorObject] = newInternalClass(QV4::GeneratorObject::staticVTable(), generatorPrototype());
|
|
|
|
|
2015-08-27 12:38:24 +00:00
|
|
|
ScopedString str(scope);
|
2018-04-04 07:31:10 +00:00
|
|
|
classes[Class_RegExp] = classes[Class_Empty]->changeVTable(QV4::RegExp::staticVTable());
|
2017-05-15 07:56:05 +00:00
|
|
|
ic = newInternalClass(QV4::RegExpObject::staticVTable(), objectPrototype());
|
2018-09-23 13:46:25 +00:00
|
|
|
ic = ic->addMember(id_lastIndex()->propertyKey(), Attr_NotEnumerable|Attr_NotConfigurable, index);
|
|
|
|
Q_ASSERT(index->index == RegExpObject::Index_LastIndex);
|
2018-08-08 19:33:18 +00:00
|
|
|
jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(classes[Class_Object]);
|
2018-04-04 07:31:10 +00:00
|
|
|
classes[Class_RegExpObject] = ic->changePrototype(regExpPrototype()->d());
|
2015-08-27 12:38:24 +00:00
|
|
|
|
2018-09-23 13:46:25 +00:00
|
|
|
ic = classes[Class_ArrayObject]->addMember(id_index()->propertyKey(), Attr_Data, index);
|
|
|
|
Q_ASSERT(index->index == RegExpObject::Index_ArrayIndex);
|
|
|
|
classes[Class_RegExpExecArray] = ic->addMember(id_input()->propertyKey(), Attr_Data, index);
|
|
|
|
Q_ASSERT(index->index == RegExpObject::Index_ArrayInput);
|
2013-08-29 19:23:04 +00:00
|
|
|
|
2018-02-21 09:41:54 +00:00
|
|
|
ic = newInternalClass(ErrorObject::staticVTable(), nullptr);
|
2018-09-23 13:46:25 +00:00
|
|
|
ic = ic->addMember((str = newIdentifier(QStringLiteral("stack")))->propertyKey(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, index);
|
|
|
|
Q_ASSERT(index->index == ErrorObject::Index_Stack);
|
|
|
|
Q_ASSERT(index->setterIndex == ErrorObject::Index_StackSetter);
|
|
|
|
ic = ic->addMember((str = newIdentifier(QStringLiteral("fileName")))->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
|
|
|
|
Q_ASSERT(index->index == ErrorObject::Index_FileName);
|
|
|
|
ic = ic->addMember((str = newIdentifier(QStringLiteral("lineNumber")))->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
|
2018-01-05 14:30:23 +00:00
|
|
|
classes[Class_ErrorObject] = ic->d();
|
2018-09-23 13:46:25 +00:00
|
|
|
Q_ASSERT(index->index == ErrorObject::Index_LineNumber);
|
|
|
|
classes[Class_ErrorObjectWithMessage] = ic->addMember((str = newIdentifier(QStringLiteral("message")))->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
|
|
|
|
Q_ASSERT(index->index == ErrorObject::Index_Message);
|
2018-08-01 19:39:35 +00:00
|
|
|
ic = newInternalClass(Object::staticVTable(), objectPrototype());
|
2018-09-23 13:46:25 +00:00
|
|
|
ic = ic->addMember(id_constructor()->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
|
|
|
|
Q_ASSERT(index->index == ErrorPrototype::Index_Constructor);
|
|
|
|
ic = ic->addMember((str = newIdentifier(QStringLiteral("message")))->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
|
|
|
|
Q_ASSERT(index->index == ErrorPrototype::Index_Message);
|
|
|
|
classes[Class_ErrorProto] = ic->addMember(id_name()->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
|
|
|
|
Q_ASSERT(index->index == ErrorPrototype::Index_Name);
|
2015-08-27 20:47:23 +00:00
|
|
|
|
2018-06-17 08:57:21 +00:00
|
|
|
classes[Class_ProxyObject] = classes[Class_Empty]->changeVTable(ProxyObject::staticVTable());
|
2018-08-23 14:06:26 +00:00
|
|
|
classes[Class_ProxyFunctionObject] = classes[Class_Empty]->changeVTable(ProxyFunctionObject::staticVTable());
|
2018-06-17 08:57:21 +00:00
|
|
|
|
2018-04-26 09:48:04 +00:00
|
|
|
jsObjects[GetStack_Function] = FunctionObject::createBuiltinFunction(this, str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack, 0);
|
2015-08-27 20:47:23 +00:00
|
|
|
|
2018-04-04 07:31:10 +00:00
|
|
|
jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(classes[Class_ErrorProto]);
|
2018-01-05 14:30:23 +00:00
|
|
|
ic = classes[Class_ErrorProto]->changePrototype(errorPrototype()->d());
|
|
|
|
jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(ic->d());
|
|
|
|
jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(ic->d());
|
|
|
|
jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(ic->d());
|
|
|
|
jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(ic->d());
|
|
|
|
jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(ic->d());
|
|
|
|
jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(ic->d());
|
2015-01-10 19:35:18 +00:00
|
|
|
|
2018-04-10 20:45:27 +00:00
|
|
|
jsObjects[VariantProto] = memoryManager->allocate<VariantPrototype>();
|
2018-06-19 11:28:26 +00:00
|
|
|
Q_ASSERT(variantPrototype()->getPrototypeOf() == objectPrototype()->d());
|
2014-11-24 14:38:41 +00:00
|
|
|
|
2017-05-15 07:56:05 +00:00
|
|
|
ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this));
|
2018-01-05 14:30:23 +00:00
|
|
|
jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic->d()));
|
2014-06-13 12:30:03 +00:00
|
|
|
|
2015-08-24 14:47:48 +00:00
|
|
|
ExecutionContext *global = rootContext();
|
2018-04-03 13:23:07 +00:00
|
|
|
|
2018-04-10 20:45:27 +00:00
|
|
|
jsObjects[Object_Ctor] = memoryManager->allocate<ObjectCtor>(global);
|
|
|
|
jsObjects[String_Ctor] = memoryManager->allocate<StringCtor>(global);
|
2018-04-03 13:23:07 +00:00
|
|
|
jsObjects[Symbol_Ctor] = memoryManager->allocate<SymbolCtor>(global);
|
2018-04-10 20:45:27 +00:00
|
|
|
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);
|
2018-04-27 09:41:13 +00:00
|
|
|
jsObjects[GeneratorFunction_Ctor] = memoryManager->allocate<GeneratorFunctionCtor>(global);
|
2018-04-10 20:45:27 +00:00
|
|
|
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);
|
Add Array Iterator objects from ES6 (22.1.5)
And implement / expose them via:
22.1.3.4 - Array.prototype.entries()
22.1.3.13 - Array.prototype.keys()
22.1.3.29 - Array.prototype.values()
22.1.3.31 - Array.prototype[Symbol.iterator]
Most tests for Array iterators now pass.
At the same time, expose them on TypedArray's prototype:
- 22.2.3.15 %TypedArray%.prototype.keys
- 22.2.3.29 %TypedArray%.prototype.values
- 22.2.3.6 %TypedArray%.prototype.entries
- 22.2.3.31 %TypedArray%.prototype[Symbol.iterator]
For TypedArray, test coverage improves a tiny bit (3 passing tests), but the
vast majority fail as it seems like the object structure for TypedArray is
currently incomplete as far as ES6 expects.
It seems that ES6 expects the object structure to be:
* %TypedArray% (inherits FunctionObject)
(this is the TypedArray intrinsic object, and responsible for initializing
the TypedArray instances)
* All the TypedArray ctors (e.g. UInt8Array)
These inherit %TypedArray%, and make a super call to it to do their work
* %TypedArrayPrototype% (inherits Object)
(this is the initial prototype for %TypedArray%)
* All the ctors have their own separate instance of this
* The instances also make use it
So, for instance, a lot of the tests attempt to access the prototype like:
var proto = Object.getPrototypeOf(Int8Array)
var keys = proto.prototype.keys
As ES6 expects Int8Array.prototype to be %TypedArray% (22.2.5), this expands to:
Object.getPrototypeOf(%TypedArray%)
which it expects to be %TypedArrayPrototype%.
But since we have no intrinsic object, and the ctors inherit
FunctionObject, we instead return the wrong prototype into 'var proto'.
Change-Id: I5e1a95a0420ecb70a0e35a5df3f65557510c5925
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-02-09 18:58:50 +00:00
|
|
|
jsObjects[IteratorProto] = memoryManager->allocate<IteratorPrototype>();
|
2019-03-25 14:43:13 +00:00
|
|
|
|
|
|
|
ic = newInternalClass(ForInIteratorPrototype::staticVTable(), iteratorPrototype());
|
|
|
|
jsObjects[ForInIteratorProto] = memoryManager->allocObject<ForInIteratorPrototype>(ic);
|
|
|
|
ic = newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype());
|
|
|
|
jsObjects[MapIteratorProto] = memoryManager->allocObject<MapIteratorPrototype>(ic);
|
|
|
|
ic = newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype());
|
|
|
|
jsObjects[SetIteratorProto] = memoryManager->allocObject<SetIteratorPrototype>(ic);
|
|
|
|
ic = newInternalClass(ArrayIteratorPrototype::staticVTable(), iteratorPrototype());
|
|
|
|
jsObjects[ArrayIteratorProto] = memoryManager->allocObject<ArrayIteratorPrototype>(ic);
|
|
|
|
ic = newInternalClass(StringIteratorPrototype::staticVTable(), iteratorPrototype());
|
|
|
|
jsObjects[StringIteratorProto] = memoryManager->allocObject<StringIteratorPrototype>(ic);
|
2015-03-25 20:06:18 +00:00
|
|
|
|
2020-04-02 13:48:17 +00:00
|
|
|
//
|
|
|
|
// url
|
|
|
|
//
|
|
|
|
|
|
|
|
jsObjects[Url_Ctor] = memoryManager->allocate<UrlCtor>(global);
|
|
|
|
jsObjects[UrlProto] = memoryManager->allocate<UrlPrototype>();
|
2020-04-09 11:34:01 +00:00
|
|
|
jsObjects[UrlSearchParams_Ctor] = memoryManager->allocate<UrlSearchParamsCtor>(global);
|
|
|
|
jsObjects[UrlSearchParamsProto] = memoryManager->allocate<UrlSearchParamsPrototype>();
|
2020-04-02 13:48:17 +00:00
|
|
|
|
2018-05-14 21:07:45 +00:00
|
|
|
str = newString(QStringLiteral("get [Symbol.species]"));
|
|
|
|
jsObjects[GetSymbolSpecies] = FunctionObject::createBuiltinFunction(this, str, ArrayPrototype::method_get_species, 0);
|
|
|
|
|
2015-03-25 20:06:18 +00:00
|
|
|
static_cast<ObjectPrototype *>(objectPrototype())->init(this, objectCtor());
|
|
|
|
static_cast<StringPrototype *>(stringPrototype())->init(this, stringCtor());
|
2018-04-03 13:23:07 +00:00
|
|
|
static_cast<SymbolPrototype *>(symbolPrototype())->init(this, symbolCtor());
|
2015-03-25 20:06:18 +00:00
|
|
|
static_cast<NumberPrototype *>(numberPrototype())->init(this, numberCtor());
|
|
|
|
static_cast<BooleanPrototype *>(booleanPrototype())->init(this, booleanCtor());
|
|
|
|
static_cast<ArrayPrototype *>(arrayPrototype())->init(this, arrayCtor());
|
2022-07-05 14:24:05 +00:00
|
|
|
static_cast<PropertyListPrototype *>(propertyListPrototype())->init();
|
2015-03-25 20:06:18 +00:00
|
|
|
static_cast<DatePrototype *>(datePrototype())->init(this, dateCtor());
|
|
|
|
static_cast<FunctionPrototype *>(functionPrototype())->init(this, functionCtor());
|
2018-04-27 09:41:13 +00:00
|
|
|
static_cast<GeneratorPrototype *>(generatorPrototype())->init(this, generatorFunctionCtor());
|
2015-03-25 20:06:18 +00:00
|
|
|
static_cast<RegExpPrototype *>(regExpPrototype())->init(this, regExpCtor());
|
|
|
|
static_cast<ErrorPrototype *>(errorPrototype())->init(this, errorCtor());
|
|
|
|
static_cast<EvalErrorPrototype *>(evalErrorPrototype())->init(this, evalErrorCtor());
|
|
|
|
static_cast<RangeErrorPrototype *>(rangeErrorPrototype())->init(this, rangeErrorCtor());
|
|
|
|
static_cast<ReferenceErrorPrototype *>(referenceErrorPrototype())->init(this, referenceErrorCtor());
|
|
|
|
static_cast<SyntaxErrorPrototype *>(syntaxErrorPrototype())->init(this, syntaxErrorCtor());
|
|
|
|
static_cast<TypeErrorPrototype *>(typeErrorPrototype())->init(this, typeErrorCtor());
|
|
|
|
static_cast<URIErrorPrototype *>(uRIErrorPrototype())->init(this, uRIErrorCtor());
|
2020-04-02 13:48:17 +00:00
|
|
|
static_cast<UrlPrototype *>(urlPrototype())->init(this, urlCtor());
|
2020-04-09 11:34:01 +00:00
|
|
|
static_cast<UrlSearchParamsPrototype *>(urlSearchParamsPrototype())->init(this, urlSearchParamsCtor());
|
Add Array Iterator objects from ES6 (22.1.5)
And implement / expose them via:
22.1.3.4 - Array.prototype.entries()
22.1.3.13 - Array.prototype.keys()
22.1.3.29 - Array.prototype.values()
22.1.3.31 - Array.prototype[Symbol.iterator]
Most tests for Array iterators now pass.
At the same time, expose them on TypedArray's prototype:
- 22.2.3.15 %TypedArray%.prototype.keys
- 22.2.3.29 %TypedArray%.prototype.values
- 22.2.3.6 %TypedArray%.prototype.entries
- 22.2.3.31 %TypedArray%.prototype[Symbol.iterator]
For TypedArray, test coverage improves a tiny bit (3 passing tests), but the
vast majority fail as it seems like the object structure for TypedArray is
currently incomplete as far as ES6 expects.
It seems that ES6 expects the object structure to be:
* %TypedArray% (inherits FunctionObject)
(this is the TypedArray intrinsic object, and responsible for initializing
the TypedArray instances)
* All the TypedArray ctors (e.g. UInt8Array)
These inherit %TypedArray%, and make a super call to it to do their work
* %TypedArrayPrototype% (inherits Object)
(this is the initial prototype for %TypedArray%)
* All the ctors have their own separate instance of this
* The instances also make use it
So, for instance, a lot of the tests attempt to access the prototype like:
var proto = Object.getPrototypeOf(Int8Array)
var keys = proto.prototype.keys
As ES6 expects Int8Array.prototype to be %TypedArray% (22.2.5), this expands to:
Object.getPrototypeOf(%TypedArray%)
which it expects to be %TypedArrayPrototype%.
But since we have no intrinsic object, and the ctors inherit
FunctionObject, we instead return the wrong prototype into 'var proto'.
Change-Id: I5e1a95a0420ecb70a0e35a5df3f65557510c5925
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-02-09 18:58:50 +00:00
|
|
|
|
|
|
|
static_cast<IteratorPrototype *>(iteratorPrototype())->init(this);
|
2018-05-03 19:50:18 +00:00
|
|
|
static_cast<ForInIteratorPrototype *>(forInIteratorPrototype())->init(this);
|
2018-05-29 20:41:34 +00:00
|
|
|
static_cast<MapIteratorPrototype *>(mapIteratorPrototype())->init(this);
|
2018-05-27 13:04:23 +00:00
|
|
|
static_cast<SetIteratorPrototype *>(setIteratorPrototype())->init(this);
|
Add Array Iterator objects from ES6 (22.1.5)
And implement / expose them via:
22.1.3.4 - Array.prototype.entries()
22.1.3.13 - Array.prototype.keys()
22.1.3.29 - Array.prototype.values()
22.1.3.31 - Array.prototype[Symbol.iterator]
Most tests for Array iterators now pass.
At the same time, expose them on TypedArray's prototype:
- 22.2.3.15 %TypedArray%.prototype.keys
- 22.2.3.29 %TypedArray%.prototype.values
- 22.2.3.6 %TypedArray%.prototype.entries
- 22.2.3.31 %TypedArray%.prototype[Symbol.iterator]
For TypedArray, test coverage improves a tiny bit (3 passing tests), but the
vast majority fail as it seems like the object structure for TypedArray is
currently incomplete as far as ES6 expects.
It seems that ES6 expects the object structure to be:
* %TypedArray% (inherits FunctionObject)
(this is the TypedArray intrinsic object, and responsible for initializing
the TypedArray instances)
* All the TypedArray ctors (e.g. UInt8Array)
These inherit %TypedArray%, and make a super call to it to do their work
* %TypedArrayPrototype% (inherits Object)
(this is the initial prototype for %TypedArray%)
* All the ctors have their own separate instance of this
* The instances also make use it
So, for instance, a lot of the tests attempt to access the prototype like:
var proto = Object.getPrototypeOf(Int8Array)
var keys = proto.prototype.keys
As ES6 expects Int8Array.prototype to be %TypedArray% (22.2.5), this expands to:
Object.getPrototypeOf(%TypedArray%)
which it expects to be %TypedArrayPrototype%.
But since we have no intrinsic object, and the ctors inherit
FunctionObject, we instead return the wrong prototype into 'var proto'.
Change-Id: I5e1a95a0420ecb70a0e35a5df3f65557510c5925
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-02-09 18:58:50 +00:00
|
|
|
static_cast<ArrayIteratorPrototype *>(arrayIteratorPrototype())->init(this);
|
2018-04-26 11:12:38 +00:00
|
|
|
static_cast<StringIteratorPrototype *>(stringIteratorPrototype())->init(this);
|
|
|
|
|
2015-03-25 13:40:35 +00:00
|
|
|
static_cast<VariantPrototype *>(variantPrototype())->init();
|
2013-05-14 00:01:04 +00:00
|
|
|
|
2017-12-18 15:20:11 +00:00
|
|
|
sequencePrototype()->cast<SequencePrototype>()->init();
|
2014-09-10 12:50:28 +00:00
|
|
|
|
2018-08-26 13:07:50 +00:00
|
|
|
jsObjects[WeakMap_Ctor] = memoryManager->allocate<WeakMapCtor>(global);
|
|
|
|
jsObjects[WeakMapProto] = memoryManager->allocate<WeakMapPrototype>();
|
|
|
|
static_cast<WeakMapPrototype *>(weakMapPrototype())->init(this, weakMapCtor());
|
|
|
|
|
2018-05-29 20:41:34 +00:00
|
|
|
jsObjects[Map_Ctor] = memoryManager->allocate<MapCtor>(global);
|
|
|
|
jsObjects[MapProto] = memoryManager->allocate<MapPrototype>();
|
|
|
|
static_cast<MapPrototype *>(mapPrototype())->init(this, mapCtor());
|
|
|
|
|
2018-08-26 15:50:44 +00:00
|
|
|
jsObjects[WeakSet_Ctor] = memoryManager->allocate<WeakSetCtor>(global);
|
|
|
|
jsObjects[WeakSetProto] = memoryManager->allocate<WeakSetPrototype>();
|
|
|
|
static_cast<WeakSetPrototype *>(weakSetPrototype())->init(this, weakSetCtor());
|
|
|
|
|
2018-05-27 13:04:23 +00:00
|
|
|
jsObjects[Set_Ctor] = memoryManager->allocate<SetCtor>(global);
|
|
|
|
jsObjects[SetProto] = memoryManager->allocate<SetPrototype>();
|
|
|
|
static_cast<SetPrototype *>(setPrototype())->init(this, setCtor());
|
|
|
|
|
2015-07-16 16:35:55 +00:00
|
|
|
//
|
|
|
|
// promises
|
|
|
|
//
|
|
|
|
|
|
|
|
jsObjects[Promise_Ctor] = memoryManager->allocate<PromiseCtor>(global);
|
|
|
|
jsObjects[PromiseProto] = memoryManager->allocate<PromisePrototype>();
|
|
|
|
static_cast<PromisePrototype *>(promisePrototype())->init(this, promiseCtor());
|
|
|
|
|
2014-09-10 12:50:28 +00:00
|
|
|
// typed arrays
|
|
|
|
|
2018-08-24 10:03:12 +00:00
|
|
|
jsObjects[SharedArrayBuffer_Ctor] = memoryManager->allocate<SharedArrayBufferCtor>(global);
|
|
|
|
jsObjects[SharedArrayBufferProto] = memoryManager->allocate<SharedArrayBufferPrototype>();
|
|
|
|
static_cast<SharedArrayBufferPrototype *>(sharedArrayBufferPrototype())->init(this, sharedArrayBufferCtor());
|
|
|
|
|
2018-04-10 20:45:27 +00:00
|
|
|
jsObjects[ArrayBuffer_Ctor] = memoryManager->allocate<ArrayBufferCtor>(global);
|
|
|
|
jsObjects[ArrayBufferProto] = memoryManager->allocate<ArrayBufferPrototype>();
|
2015-03-25 20:06:18 +00:00
|
|
|
static_cast<ArrayBufferPrototype *>(arrayBufferPrototype())->init(this, arrayBufferCtor());
|
2014-09-10 12:50:28 +00:00
|
|
|
|
2018-04-10 20:45:27 +00:00
|
|
|
jsObjects[DataView_Ctor] = memoryManager->allocate<DataViewCtor>(global);
|
|
|
|
jsObjects[DataViewProto] = memoryManager->allocate<DataViewPrototype>();
|
2015-03-25 20:06:18 +00:00
|
|
|
static_cast<DataViewPrototype *>(dataViewPrototype())->init(this, dataViewCtor());
|
2018-02-21 09:41:54 +00:00
|
|
|
jsObjects[ValueTypeProto] = (Heap::Base *) nullptr;
|
|
|
|
jsObjects[SignalHandlerProto] = (Heap::Base *) nullptr;
|
2014-09-10 14:39:23 +00:00
|
|
|
|
2018-05-23 21:04:49 +00:00
|
|
|
jsObjects[IntrinsicTypedArray_Ctor] = memoryManager->allocate<IntrinsicTypedArrayCtor>(global);
|
|
|
|
jsObjects[IntrinsicTypedArrayProto] = memoryManager->allocate<IntrinsicTypedArrayPrototype>();
|
|
|
|
static_cast<IntrinsicTypedArrayPrototype *>(intrinsicTypedArrayPrototype())
|
|
|
|
->init(this, static_cast<IntrinsicTypedArrayCtor *>(intrinsicTypedArrayCtor()));
|
|
|
|
|
2018-08-24 11:56:37 +00:00
|
|
|
for (int i = 0; i < NTypedArrayTypes; ++i) {
|
2018-04-10 20:45:27 +00:00
|
|
|
static_cast<Value &>(typedArrayCtors[i]) = memoryManager->allocate<TypedArrayCtor>(global, Heap::TypedArray::Type(i));
|
|
|
|
static_cast<Value &>(typedArrayPrototype[i]) = memoryManager->allocate<TypedArrayPrototype>(Heap::TypedArray::Type(i));
|
2015-02-13 12:39:20 +00:00
|
|
|
typedArrayPrototype[i].as<TypedArrayPrototype>()->init(this, static_cast<TypedArrayCtor *>(typedArrayCtors[i].as<Object>()));
|
2014-09-11 13:37:31 +00:00
|
|
|
}
|
|
|
|
|
2012-11-17 20:54:26 +00:00
|
|
|
//
|
|
|
|
// set up the global object
|
|
|
|
//
|
2017-07-04 14:07:50 +00:00
|
|
|
rootContext()->d()->activation.set(scope.engine, globalObject->d());
|
2015-08-07 11:56:31 +00:00
|
|
|
Q_ASSERT(globalObject->d()->vtable());
|
2015-03-13 16:21:18 +00:00
|
|
|
|
2015-03-25 20:06:18 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("Object"), *objectCtor());
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("String"), *stringCtor());
|
2018-04-03 13:23:07 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("Symbol"), *symbolCtor());
|
2017-02-02 19:43:59 +00:00
|
|
|
FunctionObject *numberObject = numberCtor();
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("Number"), *numberObject);
|
2015-03-25 20:06:18 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("Boolean"), *booleanCtor());
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("Array"), *arrayCtor());
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("Function"), *functionCtor());
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("Date"), *dateCtor());
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("RegExp"), *regExpCtor());
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("Error"), *errorCtor());
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("EvalError"), *evalErrorCtor());
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("RangeError"), *rangeErrorCtor());
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("ReferenceError"), *referenceErrorCtor());
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("SyntaxError"), *syntaxErrorCtor());
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("TypeError"), *typeErrorCtor());
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("URIError"), *uRIErrorCtor());
|
2015-07-16 16:35:55 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("Promise"), *promiseCtor());
|
2020-04-02 13:48:17 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("URL"), *urlCtor());
|
2020-04-09 11:34:01 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("URLSearchParams"), *urlSearchParamsCtor());
|
2015-03-25 20:06:18 +00:00
|
|
|
|
2018-08-24 10:03:12 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("SharedArrayBuffer"), *sharedArrayBufferCtor());
|
2015-03-25 20:06:18 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("ArrayBuffer"), *arrayBufferCtor());
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("DataView"), *dataViewCtor());
|
2018-08-26 15:50:44 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("WeakSet"), *weakSetCtor());
|
2018-05-27 13:04:23 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("Set"), *setCtor());
|
2018-08-26 13:07:50 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("WeakMap"), *weakMapCtor());
|
2018-05-29 20:41:34 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("Map"), *mapCtor());
|
|
|
|
|
2018-08-24 11:56:37 +00:00
|
|
|
for (int i = 0; i < NTypedArrayTypes; ++i)
|
|
|
|
globalObject->defineDefaultProperty((str = typedArrayCtors[i].as<FunctionObject>()->name()), typedArrayCtors[i]);
|
2013-09-26 11:05:25 +00:00
|
|
|
ScopedObject o(scope);
|
2018-08-24 13:57:31 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("Atomics"), (o = memoryManager->allocate<Atomics>()));
|
2018-04-10 20:45:27 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocate<MathObject>()));
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocate<JsonObject>()));
|
2018-06-15 21:00:32 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("Reflect"), (o = memoryManager->allocate<Reflect>()));
|
2018-06-17 08:57:21 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("Proxy"), (o = memoryManager->allocate<Proxy>(rootContext())));
|
2013-09-18 10:31:55 +00:00
|
|
|
|
2018-09-11 09:07:32 +00:00
|
|
|
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));
|
2012-12-12 23:53:04 +00:00
|
|
|
|
2014-05-09 10:15:23 +00:00
|
|
|
|
2018-04-10 20:45:27 +00:00
|
|
|
jsObjects[Eval_Function] = memoryManager->allocate<EvalFunction>(global);
|
2015-03-25 20:31:16 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("eval"), *evalFunction());
|
2015-03-13 16:21:18 +00:00
|
|
|
|
2017-02-02 19:43:59 +00:00
|
|
|
// ES6: 20.1.2.12 & 20.1.2.13:
|
|
|
|
// parseInt and parseFloat must be the same FunctionObject on the global &
|
|
|
|
// Number object.
|
|
|
|
{
|
|
|
|
QString piString(QStringLiteral("parseInt"));
|
|
|
|
QString pfString(QStringLiteral("parseFloat"));
|
|
|
|
Scope scope(this);
|
|
|
|
ScopedString pi(scope, newIdentifier(piString));
|
|
|
|
ScopedString pf(scope, newIdentifier(pfString));
|
2018-04-26 09:48:04 +00:00
|
|
|
ScopedFunctionObject parseIntFn(scope, FunctionObject::createBuiltinFunction(this, pi, GlobalFunctions::method_parseInt, 2));
|
|
|
|
ScopedFunctionObject parseFloatFn(scope, FunctionObject::createBuiltinFunction(this, pf, GlobalFunctions::method_parseFloat, 1));
|
2017-02-02 19:43:59 +00:00
|
|
|
globalObject->defineDefaultProperty(piString, parseIntFn);
|
|
|
|
globalObject->defineDefaultProperty(pfString, parseFloatFn);
|
|
|
|
numberObject->defineDefaultProperty(piString, parseIntFn);
|
|
|
|
numberObject->defineDefaultProperty(pfString, parseFloatFn);
|
|
|
|
}
|
|
|
|
|
2015-03-13 16:21:18 +00:00
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1);
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1);
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1);
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("decodeURIComponent"), GlobalFunctions::method_decodeURIComponent, 1);
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("encodeURI"), GlobalFunctions::method_encodeURI, 1);
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("encodeURIComponent"), GlobalFunctions::method_encodeURIComponent, 1);
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("escape"), GlobalFunctions::method_escape, 1);
|
|
|
|
globalObject->defineDefaultProperty(QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1);
|
2013-09-18 07:30:45 +00:00
|
|
|
|
2018-05-15 21:29:33 +00:00
|
|
|
ScopedFunctionObject t(scope, memoryManager->allocate<FunctionObject>(rootContext(), nullptr, ::throwTypeError));
|
2018-09-11 09:07:32 +00:00
|
|
|
t->defineReadonlyProperty(id_length(), Value::fromInt32(0));
|
Clean up frozen(), sealed(), nonExtensible() and propertiesFrozen()
They all had some interesting bugs and duplicated each other:
a, propertiesFrozen() changed each property individually, creating a lot
of unnecessary intermediate classes. frozen() changed them all at once.
b, If a class happened to contain only properties that matched the
characteristics of being "sealed" or "frozen", sealed(), frozen() and
propertiesFrozen() would set the flags in place and return the same
class. This is bad because it violates the assumption that an
InternalClass is immutable and it breaks the recursive freezing
algorithm we rely on for the global object. It would stop freezing child
objects at any such class, even if the children were not frozen.
c, propertiesFrozen() did not set any of the flags even though it
effectively sealed and froze the class. Therefore, when requesting the
same class as frozen() it would iterate through all the properties
again.
d, frozen() implicitly also sealed the object and made it
non-extensible. sealed() also implicitly made it non-extensible. This is
impractical as we want to allow objects to be extensible even though all
their properties are frozen. Therefore we only set the flag that belongs
to each method now. We do know, however, that a frozen object is
implicitly sealed. Therefore we can short-circuit this transition.
Furthermore, we need to remove the assert in InternalClass::init() as
you can indeed use frozen objects as prototypes for others, but that
needs to be recorded in the original InternalClass via the isUsedAsProto
flag. In order to set this flag, we need to perform a transition and
therefore, derive from the old InternalClass.
The JavaScript isFrozen() method asks for an _implicitly_, "duck typed",
frozen state, which is different from what our "isFrozen" flag denotes.
Therefore we add a separate const method that just checks whether all
properties are frozen.
Task-number: QTBUG-76033
Change-Id: I375fef83fb99035d470490fdf2348766b090831e
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
2019-05-28 13:13:05 +00:00
|
|
|
t->setInternalClass(t->internalClass()->cryopreserved());
|
2018-05-15 21:29:33 +00:00
|
|
|
jsObjects[ThrowerObject] = t;
|
2018-03-15 15:40:36 +00:00
|
|
|
|
|
|
|
ScopedProperty pd(scope);
|
|
|
|
pd->value = thrower();
|
|
|
|
pd->set = thrower();
|
|
|
|
functionPrototype()->insertMember(id_caller(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable);
|
|
|
|
functionPrototype()->insertMember(id_arguments(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable);
|
2019-04-05 07:59:10 +00:00
|
|
|
|
|
|
|
QV4::QObjectWrapper::initializeBindings(this);
|
|
|
|
|
|
|
|
m_delayedCallQueue.init(this);
|
2012-11-17 20:54:26 +00:00
|
|
|
}
|
|
|
|
|
2012-12-04 10:30:26 +00:00
|
|
|
ExecutionEngine::~ExecutionEngine()
|
|
|
|
{
|
2018-07-10 12:52:34 +00:00
|
|
|
modules.clear();
|
2021-04-25 21:57:37 +00:00
|
|
|
for (auto val : nativeModules) {
|
|
|
|
PersistentValueStorage::free(val);
|
|
|
|
}
|
|
|
|
nativeModules.clear();
|
2019-04-05 07:59:10 +00:00
|
|
|
qDeleteAll(m_extensionData);
|
2013-06-04 12:28:13 +00:00
|
|
|
delete m_multiplyWrappedQObjects;
|
2018-02-21 09:41:54 +00:00
|
|
|
m_multiplyWrappedQObjects = nullptr;
|
2013-08-12 14:13:37 +00:00
|
|
|
delete identifierTable;
|
2013-05-29 12:58:52 +00:00
|
|
|
delete memoryManager;
|
2013-09-19 07:32:42 +00:00
|
|
|
|
2017-10-04 15:21:44 +00:00
|
|
|
while (!compilationUnits.isEmpty())
|
|
|
|
(*compilationUnits.begin())->unlink();
|
2013-09-19 07:32:42 +00:00
|
|
|
|
2013-04-19 09:47:50 +00:00
|
|
|
delete bumperPointerAllocator;
|
2013-03-30 16:43:03 +00:00
|
|
|
delete regExpCache;
|
2013-05-12 10:07:15 +00:00
|
|
|
delete regExpAllocator;
|
2013-03-25 16:31:38 +00:00
|
|
|
delete executableAllocator;
|
2013-09-03 10:40:07 +00:00
|
|
|
jsStack->deallocate();
|
|
|
|
delete jsStack;
|
2017-03-09 09:36:16 +00:00
|
|
|
gcStack->deallocate();
|
|
|
|
delete gcStack;
|
2019-04-05 07:59:10 +00:00
|
|
|
|
|
|
|
#if QT_CONFIG(qml_xml_http_request)
|
|
|
|
qt_rem_qmlxmlhttprequest(this, m_xmlHttpRequestData);
|
|
|
|
m_xmlHttpRequestData = nullptr;
|
|
|
|
#endif
|
2012-12-04 10:30:26 +00:00
|
|
|
}
|
|
|
|
|
2017-10-06 15:30:36 +00:00
|
|
|
#if QT_CONFIG(qml_debug)
|
2016-07-25 15:02:45 +00:00
|
|
|
void ExecutionEngine::setDebugger(Debugging::Debugger *debugger)
|
2013-07-24 08:29:04 +00:00
|
|
|
{
|
2016-07-25 15:02:45 +00:00
|
|
|
Q_ASSERT(!m_debugger);
|
2017-09-05 13:23:05 +00:00
|
|
|
m_debugger.reset(debugger);
|
2013-07-24 08:29:04 +00:00
|
|
|
}
|
|
|
|
|
2016-07-25 15:02:45 +00:00
|
|
|
void ExecutionEngine::setProfiler(Profiling::Profiler *profiler)
|
2014-01-22 16:46:20 +00:00
|
|
|
{
|
2016-07-25 15:02:45 +00:00
|
|
|
Q_ASSERT(!m_profiler);
|
2017-09-05 13:23:05 +00:00
|
|
|
m_profiler.reset(profiler);
|
2014-01-22 16:46:20 +00:00
|
|
|
}
|
2017-10-06 15:30:36 +00:00
|
|
|
#endif // QT_CONFIG(qml_debug)
|
2014-01-22 16:46:20 +00:00
|
|
|
|
2013-03-14 13:03:04 +00:00
|
|
|
void ExecutionEngine::initRootContext()
|
|
|
|
{
|
2014-11-13 20:53:27 +00:00
|
|
|
Scope scope(this);
|
2017-08-13 17:43:24 +00:00
|
|
|
Scoped<ExecutionContext> r(scope, memoryManager->allocManaged<ExecutionContext>(sizeof(ExecutionContext::Data)));
|
2017-07-04 20:04:49 +00:00
|
|
|
r->d_unchecked()->init(Heap::ExecutionContext::Type_GlobalContext);
|
|
|
|
r->d()->activation.set(this, globalObject->d());
|
2015-08-24 14:24:34 +00:00
|
|
|
jsObjects[RootContext] = r;
|
2018-05-04 13:16:08 +00:00
|
|
|
jsObjects[ScriptContext] = r;
|
2015-08-24 13:31:46 +00:00
|
|
|
jsObjects[IntegerNull] = Encode((int)0);
|
2013-03-14 13:03:04 +00:00
|
|
|
}
|
|
|
|
|
2018-01-05 14:30:23 +00:00
|
|
|
Heap::InternalClass *ExecutionEngine::newClass(Heap::InternalClass *other)
|
2013-05-06 11:29:24 +00:00
|
|
|
{
|
2018-01-05 14:30:23 +00:00
|
|
|
Heap::InternalClass *ic = memoryManager->allocIC<InternalClass>();
|
|
|
|
ic->init(other);
|
|
|
|
return ic;
|
2013-05-06 11:29:24 +00:00
|
|
|
}
|
|
|
|
|
2018-01-05 14:30:23 +00:00
|
|
|
Heap::InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype)
|
2017-05-15 07:56:05 +00:00
|
|
|
{
|
2018-01-05 14:30:23 +00:00
|
|
|
Scope scope(this);
|
|
|
|
Scoped<InternalClass> ic(scope, internalClasses(Class_Empty)->changeVTable(vtable));
|
|
|
|
return ic->changePrototype(prototype ? prototype->d() : nullptr);
|
2017-05-15 07:56:05 +00:00
|
|
|
}
|
|
|
|
|
2014-11-11 12:34:18 +00:00
|
|
|
Heap::Object *ExecutionEngine::newObject()
|
2012-11-17 20:54:26 +00:00
|
|
|
{
|
2018-04-10 20:45:27 +00:00
|
|
|
return memoryManager->allocate<Object>();
|
2012-11-17 20:54:26 +00:00
|
|
|
}
|
|
|
|
|
2018-01-05 14:30:23 +00:00
|
|
|
Heap::Object *ExecutionEngine::newObject(Heap::InternalClass *internalClass)
|
2013-04-23 05:31:02 +00:00
|
|
|
{
|
2018-01-08 10:40:39 +00:00
|
|
|
return memoryManager->allocObject<Object>(internalClass);
|
2013-04-23 05:31:02 +00:00
|
|
|
}
|
|
|
|
|
2014-11-11 14:08:30 +00:00
|
|
|
Heap::String *ExecutionEngine::newString(const QString &s)
|
2012-11-17 20:54:26 +00:00
|
|
|
{
|
2018-06-23 21:07:23 +00:00
|
|
|
return memoryManager->allocWithStringData<String>(s.length() * sizeof(QChar), s);
|
2012-11-17 20:54:26 +00:00
|
|
|
}
|
|
|
|
|
2014-11-11 14:08:30 +00:00
|
|
|
Heap::String *ExecutionEngine::newIdentifier(const QString &text)
|
2013-01-30 13:56:40 +00:00
|
|
|
{
|
2018-06-23 21:07:23 +00:00
|
|
|
Scope scope(this);
|
|
|
|
ScopedString s(scope, memoryManager->allocWithStringData<String>(text.length() * sizeof(QChar), text));
|
|
|
|
s->toPropertyKey();
|
|
|
|
return s->d();
|
2013-01-30 13:56:40 +00:00
|
|
|
}
|
|
|
|
|
2015-04-30 22:08:55 +00:00
|
|
|
Heap::Object *ExecutionEngine::newStringObject(const String *string)
|
2012-11-17 20:54:26 +00:00
|
|
|
{
|
2018-04-10 20:45:27 +00:00
|
|
|
return memoryManager->allocate<StringObject>(string);
|
2012-11-17 20:54:26 +00:00
|
|
|
}
|
|
|
|
|
2018-04-08 14:43:10 +00:00
|
|
|
Heap::Object *ExecutionEngine::newSymbolObject(const Symbol *symbol)
|
|
|
|
{
|
|
|
|
return memoryManager->allocObject<SymbolObject>(classes[Class_SymbolObject], symbol);
|
|
|
|
}
|
|
|
|
|
2015-01-16 11:44:51 +00:00
|
|
|
Heap::Object *ExecutionEngine::newNumberObject(double value)
|
2012-11-17 20:54:26 +00:00
|
|
|
{
|
2018-04-10 20:45:27 +00:00
|
|
|
return memoryManager->allocate<NumberObject>(value);
|
2012-11-17 20:54:26 +00:00
|
|
|
}
|
|
|
|
|
2015-01-15 20:54:12 +00:00
|
|
|
Heap::Object *ExecutionEngine::newBooleanObject(bool b)
|
2012-11-17 20:54:26 +00:00
|
|
|
{
|
2018-04-10 20:45:27 +00:00
|
|
|
return memoryManager->allocate<BooleanObject>(b);
|
2012-11-17 20:54:26 +00:00
|
|
|
}
|
|
|
|
|
2014-11-11 12:34:18 +00:00
|
|
|
Heap::ArrayObject *ExecutionEngine::newArrayObject(int count)
|
2012-11-17 20:54:26 +00:00
|
|
|
{
|
2014-05-09 12:14:02 +00:00
|
|
|
Scope scope(this);
|
2018-04-10 20:45:27 +00:00
|
|
|
ScopedArrayObject object(scope, memoryManager->allocate<ArrayObject>());
|
2013-05-31 14:40:12 +00:00
|
|
|
|
|
|
|
if (count) {
|
|
|
|
if (count < 0x1000)
|
|
|
|
object->arrayReserve(count);
|
|
|
|
object->setArrayLengthUnchecked(count);
|
|
|
|
}
|
2014-11-11 12:34:18 +00:00
|
|
|
return object->d();
|
2012-11-17 20:54:26 +00:00
|
|
|
}
|
|
|
|
|
2015-09-08 12:58:16 +00:00
|
|
|
Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int length)
|
|
|
|
{
|
|
|
|
Scope scope(this);
|
2018-04-10 20:45:27 +00:00
|
|
|
ScopedArrayObject a(scope, memoryManager->allocate<ArrayObject>());
|
2015-09-08 12:58:16 +00:00
|
|
|
|
|
|
|
if (length) {
|
|
|
|
size_t size = sizeof(Heap::ArrayData) + (length-1)*sizeof(Value);
|
|
|
|
Heap::SimpleArrayData *d = scope.engine->memoryManager->allocManaged<SimpleArrayData>(size);
|
2016-09-08 09:07:26 +00:00
|
|
|
d->init();
|
2015-09-08 12:58:16 +00:00
|
|
|
d->type = Heap::ArrayData::Simple;
|
|
|
|
d->offset = 0;
|
2017-01-27 08:57:00 +00:00
|
|
|
d->values.alloc = length;
|
|
|
|
d->values.size = length;
|
2017-02-03 19:58:22 +00:00
|
|
|
// this doesn't require a write barrier, things will be ok, when the new array data gets inserted into
|
|
|
|
// the parent object
|
|
|
|
memcpy(&d->values.values, values, length*sizeof(Value));
|
2017-01-31 13:03:51 +00:00
|
|
|
a->d()->arrayData.set(this, d);
|
2015-09-08 12:58:16 +00:00
|
|
|
a->setArrayLengthUnchecked(length);
|
|
|
|
}
|
|
|
|
return a->d();
|
|
|
|
}
|
|
|
|
|
2014-11-11 12:34:18 +00:00
|
|
|
Heap::ArrayObject *ExecutionEngine::newArrayObject(const QStringList &list)
|
2012-11-17 20:54:26 +00:00
|
|
|
{
|
2018-01-08 10:44:51 +00:00
|
|
|
return memoryManager->allocate<ArrayObject>(list);
|
2012-11-17 20:54:26 +00:00
|
|
|
}
|
|
|
|
|
2018-01-05 14:30:23 +00:00
|
|
|
Heap::ArrayObject *ExecutionEngine::newArrayObject(Heap::InternalClass *internalClass)
|
2013-09-02 12:25:15 +00:00
|
|
|
{
|
2018-01-08 10:44:51 +00:00
|
|
|
return memoryManager->allocObject<ArrayObject>(internalClass);
|
2013-09-02 12:25:15 +00:00
|
|
|
}
|
|
|
|
|
2015-03-04 19:57:14 +00:00
|
|
|
Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(const QByteArray &array)
|
|
|
|
{
|
2018-04-10 20:45:27 +00:00
|
|
|
return memoryManager->allocate<ArrayBuffer>(array);
|
2015-08-27 10:17:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(size_t length)
|
|
|
|
{
|
2018-04-10 20:45:27 +00:00
|
|
|
return memoryManager->allocate<ArrayBuffer>(length);
|
2015-03-04 19:57:14 +00:00
|
|
|
}
|
|
|
|
|
2013-09-02 12:25:15 +00:00
|
|
|
|
2015-01-15 10:36:57 +00:00
|
|
|
Heap::DateObject *ExecutionEngine::newDateObject(const Value &value)
|
2012-11-17 20:54:26 +00:00
|
|
|
{
|
2018-04-10 20:45:27 +00:00
|
|
|
return memoryManager->allocate<DateObject>(value);
|
2012-11-17 20:54:26 +00:00
|
|
|
}
|
|
|
|
|
2014-11-11 12:34:18 +00:00
|
|
|
Heap::DateObject *ExecutionEngine::newDateObject(const QDateTime &dt)
|
2013-05-02 20:33:47 +00:00
|
|
|
{
|
2014-05-09 12:14:02 +00:00
|
|
|
Scope scope(this);
|
2018-04-10 20:45:27 +00:00
|
|
|
Scoped<DateObject> object(scope, memoryManager->allocate<DateObject>(dt));
|
2014-11-11 12:34:18 +00:00
|
|
|
return object->d();
|
2013-05-02 20:33:47 +00:00
|
|
|
}
|
|
|
|
|
2020-07-30 13:41:03 +00:00
|
|
|
Heap::DateObject *ExecutionEngine::newDateObjectFromTime(QTime t)
|
V4: Handle QTime->DateObject conversion better
By specification, date conversion functions for dates before the epoch
are not DST corrected. We converted QTime to a QDateTime where we set
the date part to Jan. 1, 1970, and then convert that to msecs since the
epoch UTC. For places on Earth where they had DST on that day (e.g.
Hobart in Australia), strange things happen: conversion from a QTime to
DateObject will use DST (because it's after the epoch in local time),
but conversions from DateObject to QTime won't use the DST because it's
before the epoch (in UTC).
Now as everyone knows, a 24-hour clock time has no meaning without a
date, only "elapsed time" has. But users still expect to be able to pass
QTime to QML/JS. So, we do the conversion on day 0 of month 0 of year 0,
and all of it in local time. This gives a stable conversion in both
directions, and the values in both C++ and QML/JS are the same for any
timezone (with or without DST) on this planet.
Task-number: QTBUG-54378
Change-Id: I892e16a93f015e92d311c6cae3ae7768b7373f6a
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
2016-07-08 14:40:28 +00:00
|
|
|
{
|
|
|
|
Scope scope(this);
|
2018-04-10 20:45:27 +00:00
|
|
|
Scoped<DateObject> object(scope, memoryManager->allocate<DateObject>(t));
|
V4: Handle QTime->DateObject conversion better
By specification, date conversion functions for dates before the epoch
are not DST corrected. We converted QTime to a QDateTime where we set
the date part to Jan. 1, 1970, and then convert that to msecs since the
epoch UTC. For places on Earth where they had DST on that day (e.g.
Hobart in Australia), strange things happen: conversion from a QTime to
DateObject will use DST (because it's after the epoch in local time),
but conversions from DateObject to QTime won't use the DST because it's
before the epoch (in UTC).
Now as everyone knows, a 24-hour clock time has no meaning without a
date, only "elapsed time" has. But users still expect to be able to pass
QTime to QML/JS. So, we do the conversion on day 0 of month 0 of year 0,
and all of it in local time. This gives a stable conversion in both
directions, and the values in both C++ and QML/JS are the same for any
timezone (with or without DST) on this planet.
Task-number: QTBUG-54378
Change-Id: I892e16a93f015e92d311c6cae3ae7768b7373f6a
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
2016-07-08 14:40:28 +00:00
|
|
|
return object->d();
|
|
|
|
}
|
|
|
|
|
2014-11-11 12:34:18 +00:00
|
|
|
Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags)
|
2012-11-17 20:54:26 +00:00
|
|
|
{
|
2013-10-15 13:00:24 +00:00
|
|
|
Scope scope(this);
|
2018-08-08 11:15:40 +00:00
|
|
|
Scoped<RegExp> re(scope, RegExp::create(this, pattern, static_cast<CompiledData::RegExp::Flags>(flags)));
|
2017-08-08 10:39:54 +00:00
|
|
|
return newRegExpObject(re);
|
2012-12-12 07:28:08 +00:00
|
|
|
}
|
|
|
|
|
2017-08-08 10:39:54 +00:00
|
|
|
Heap::RegExpObject *ExecutionEngine::newRegExpObject(RegExp *re)
|
2012-12-12 07:28:08 +00:00
|
|
|
{
|
2018-04-10 20:45:27 +00:00
|
|
|
return memoryManager->allocate<RegExpObject>(re);
|
2012-11-17 20:54:26 +00:00
|
|
|
}
|
|
|
|
|
2019-02-13 14:54:14 +00:00
|
|
|
#if QT_CONFIG(regularexpression)
|
|
|
|
Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegularExpression &re)
|
|
|
|
{
|
|
|
|
return memoryManager->allocate<RegExpObject>(re);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-04-02 13:48:17 +00:00
|
|
|
Heap::UrlObject *ExecutionEngine::newUrlObject()
|
|
|
|
{
|
|
|
|
return memoryManager->allocate<UrlObject>();
|
|
|
|
}
|
|
|
|
|
2021-04-20 08:43:11 +00:00
|
|
|
Heap::UrlObject *ExecutionEngine::newUrlObject(const QUrl &url)
|
|
|
|
{
|
|
|
|
Scope scope(this);
|
|
|
|
Scoped<UrlObject> urlObject(scope, newUrlObject());
|
|
|
|
urlObject->setUrl(url);
|
|
|
|
return urlObject->d();
|
|
|
|
}
|
|
|
|
|
2020-04-09 11:34:01 +00:00
|
|
|
Heap::UrlSearchParamsObject *ExecutionEngine::newUrlSearchParamsObject()
|
|
|
|
{
|
|
|
|
return memoryManager->allocate<UrlSearchParamsObject>();
|
|
|
|
}
|
|
|
|
|
2015-01-15 10:36:57 +00:00
|
|
|
Heap::Object *ExecutionEngine::newErrorObject(const Value &value)
|
2012-11-17 20:54:26 +00:00
|
|
|
{
|
2018-08-04 08:39:09 +00:00
|
|
|
return ErrorObject::create<ErrorObject>(this, value, errorCtor());
|
2012-11-17 20:54:26 +00:00
|
|
|
}
|
|
|
|
|
2017-08-01 14:31:53 +00:00
|
|
|
Heap::Object *ExecutionEngine::newErrorObject(const QString &message)
|
|
|
|
{
|
|
|
|
return ErrorObject::create<ErrorObject>(this, message);
|
|
|
|
}
|
|
|
|
|
2014-11-11 12:34:18 +00:00
|
|
|
Heap::Object *ExecutionEngine::newSyntaxErrorObject(const QString &message)
|
2012-11-28 10:00:23 +00:00
|
|
|
{
|
2015-09-01 11:46:46 +00:00
|
|
|
return ErrorObject::create<SyntaxErrorObject>(this, message);
|
2012-11-28 10:00:23 +00:00
|
|
|
}
|
|
|
|
|
2014-11-11 12:34:18 +00:00
|
|
|
Heap::Object *ExecutionEngine::newSyntaxErrorObject(const QString &message, const QString &fileName, int line, int column)
|
2012-12-04 18:50:25 +00:00
|
|
|
{
|
2015-09-01 11:46:46 +00:00
|
|
|
return ErrorObject::create<SyntaxErrorObject>(this, message, fileName, line, column);
|
2012-12-04 18:50:25 +00:00
|
|
|
}
|
|
|
|
|
2013-05-02 19:37:20 +00:00
|
|
|
|
2014-11-11 12:34:18 +00:00
|
|
|
Heap::Object *ExecutionEngine::newReferenceErrorObject(const QString &message)
|
2013-05-02 19:37:20 +00:00
|
|
|
{
|
2015-09-01 11:46:46 +00:00
|
|
|
return ErrorObject::create<ReferenceErrorObject>(this, message);
|
2013-05-02 19:37:20 +00:00
|
|
|
}
|
|
|
|
|
2015-09-01 11:46:46 +00:00
|
|
|
Heap::Object *ExecutionEngine::newReferenceErrorObject(const QString &message, const QString &fileName, int line, int column)
|
2013-06-14 11:08:13 +00:00
|
|
|
{
|
2015-09-01 11:46:46 +00:00
|
|
|
return ErrorObject::create<ReferenceErrorObject>(this, message, fileName, line, column);
|
2013-06-14 11:08:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-11 12:34:18 +00:00
|
|
|
Heap::Object *ExecutionEngine::newTypeErrorObject(const QString &message)
|
2012-12-04 18:50:25 +00:00
|
|
|
{
|
2015-09-01 11:46:46 +00:00
|
|
|
return ErrorObject::create<TypeErrorObject>(this, message);
|
2012-12-04 18:50:25 +00:00
|
|
|
}
|
|
|
|
|
2014-11-11 12:34:18 +00:00
|
|
|
Heap::Object *ExecutionEngine::newRangeErrorObject(const QString &message)
|
2013-01-11 08:56:56 +00:00
|
|
|
{
|
2015-09-01 11:46:46 +00:00
|
|
|
return ErrorObject::create<RangeErrorObject>(this, message);
|
2013-01-11 08:56:56 +00:00
|
|
|
}
|
|
|
|
|
2015-01-15 10:36:57 +00:00
|
|
|
Heap::Object *ExecutionEngine::newURIErrorObject(const Value &message)
|
2013-01-22 14:13:15 +00:00
|
|
|
{
|
2018-08-04 08:39:09 +00:00
|
|
|
return ErrorObject::create<URIErrorObject>(this, message, uRIErrorCtor());
|
2013-01-22 14:13:15 +00:00
|
|
|
}
|
|
|
|
|
2015-07-16 16:35:55 +00:00
|
|
|
Heap::PromiseObject *ExecutionEngine::newPromiseObject()
|
|
|
|
{
|
|
|
|
if (!m_reactionHandler) {
|
|
|
|
m_reactionHandler.reset(new Promise::ReactionHandler);
|
|
|
|
}
|
|
|
|
|
|
|
|
Scope scope(this);
|
|
|
|
Scoped<PromiseObject> object(scope, memoryManager->allocate<PromiseObject>(this));
|
|
|
|
return object->d();
|
|
|
|
}
|
|
|
|
|
|
|
|
Heap::Object *ExecutionEngine::newPromiseObject(const QV4::FunctionObject *thisObject, const QV4::PromiseCapability *capability)
|
|
|
|
{
|
|
|
|
if (!m_reactionHandler) {
|
|
|
|
m_reactionHandler.reset(new Promise::ReactionHandler);
|
|
|
|
}
|
|
|
|
|
|
|
|
Scope scope(this);
|
|
|
|
Scoped<CapabilitiesExecutorWrapper> executor(scope, memoryManager->allocate<CapabilitiesExecutorWrapper>());
|
|
|
|
executor->d()->capabilities.set(this, capability->d());
|
|
|
|
executor->insertMember(id_length(), Primitive::fromInt32(2), Attr_NotWritable|Attr_NotEnumerable);
|
|
|
|
|
|
|
|
ScopedObject object(scope, thisObject->callAsConstructor(executor, 1));
|
|
|
|
return object->d();
|
|
|
|
}
|
|
|
|
|
|
|
|
Promise::ReactionHandler *ExecutionEngine::getPromiseReactionHandler()
|
|
|
|
{
|
|
|
|
Q_ASSERT(m_reactionHandler);
|
|
|
|
return m_reactionHandler.data();
|
|
|
|
}
|
|
|
|
|
2017-08-01 14:31:53 +00:00
|
|
|
Heap::Object *ExecutionEngine::newURIErrorObject(const QString &message)
|
|
|
|
{
|
|
|
|
return ErrorObject::create<URIErrorObject>(this, message);
|
|
|
|
}
|
|
|
|
|
|
|
|
Heap::Object *ExecutionEngine::newEvalErrorObject(const QString &message)
|
|
|
|
{
|
|
|
|
return ErrorObject::create<EvalErrorObject>(this, message);
|
|
|
|
}
|
|
|
|
|
2014-11-11 12:34:18 +00:00
|
|
|
Heap::Object *ExecutionEngine::newVariantObject(const QVariant &v)
|
2013-05-14 00:01:04 +00:00
|
|
|
{
|
2018-04-10 20:45:27 +00:00
|
|
|
return memoryManager->allocate<VariantObject>(v);
|
2013-05-14 00:01:04 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 19:50:18 +00:00
|
|
|
Heap::Object *ExecutionEngine::newForInIteratorObject(Object *o)
|
2012-11-17 20:54:26 +00:00
|
|
|
{
|
2014-05-09 13:23:18 +00:00
|
|
|
Scope scope(this);
|
2018-05-03 19:50:18 +00:00
|
|
|
ScopedObject obj(scope, memoryManager->allocate<ForInIteratorObject>(o));
|
2014-11-11 12:34:18 +00:00
|
|
|
return obj->d();
|
2013-05-03 14:51:32 +00:00
|
|
|
}
|
|
|
|
|
2018-05-29 20:41:34 +00:00
|
|
|
Heap::Object *ExecutionEngine::newMapIteratorObject(Object *o)
|
|
|
|
{
|
|
|
|
return memoryManager->allocate<MapIteratorObject>(o->d(), this);
|
|
|
|
}
|
|
|
|
|
2018-05-27 13:04:23 +00:00
|
|
|
Heap::Object *ExecutionEngine::newSetIteratorObject(Object *o)
|
|
|
|
{
|
|
|
|
return memoryManager->allocate<SetIteratorObject>(o->d(), this);
|
|
|
|
}
|
|
|
|
|
Add Array Iterator objects from ES6 (22.1.5)
And implement / expose them via:
22.1.3.4 - Array.prototype.entries()
22.1.3.13 - Array.prototype.keys()
22.1.3.29 - Array.prototype.values()
22.1.3.31 - Array.prototype[Symbol.iterator]
Most tests for Array iterators now pass.
At the same time, expose them on TypedArray's prototype:
- 22.2.3.15 %TypedArray%.prototype.keys
- 22.2.3.29 %TypedArray%.prototype.values
- 22.2.3.6 %TypedArray%.prototype.entries
- 22.2.3.31 %TypedArray%.prototype[Symbol.iterator]
For TypedArray, test coverage improves a tiny bit (3 passing tests), but the
vast majority fail as it seems like the object structure for TypedArray is
currently incomplete as far as ES6 expects.
It seems that ES6 expects the object structure to be:
* %TypedArray% (inherits FunctionObject)
(this is the TypedArray intrinsic object, and responsible for initializing
the TypedArray instances)
* All the TypedArray ctors (e.g. UInt8Array)
These inherit %TypedArray%, and make a super call to it to do their work
* %TypedArrayPrototype% (inherits Object)
(this is the initial prototype for %TypedArray%)
* All the ctors have their own separate instance of this
* The instances also make use it
So, for instance, a lot of the tests attempt to access the prototype like:
var proto = Object.getPrototypeOf(Int8Array)
var keys = proto.prototype.keys
As ES6 expects Int8Array.prototype to be %TypedArray% (22.2.5), this expands to:
Object.getPrototypeOf(%TypedArray%)
which it expects to be %TypedArrayPrototype%.
But since we have no intrinsic object, and the ctors inherit
FunctionObject, we instead return the wrong prototype into 'var proto'.
Change-Id: I5e1a95a0420ecb70a0e35a5df3f65557510c5925
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-02-09 18:58:50 +00:00
|
|
|
Heap::Object *ExecutionEngine::newArrayIteratorObject(Object *o)
|
|
|
|
{
|
|
|
|
return memoryManager->allocate<ArrayIteratorObject>(o->d(), this);
|
|
|
|
}
|
|
|
|
|
2016-11-28 14:33:48 +00:00
|
|
|
Heap::QmlContext *ExecutionEngine::qmlContext() const
|
|
|
|
{
|
2021-03-24 16:33:43 +00:00
|
|
|
return currentStackFrame
|
|
|
|
? static_cast<Heap::QmlContext *>(qmlContext(currentContext()->d()))
|
|
|
|
: nullptr;
|
2016-11-28 14:33:48 +00:00
|
|
|
}
|
|
|
|
|
2015-08-21 09:26:22 +00:00
|
|
|
QObject *ExecutionEngine::qmlScopeObject() const
|
2015-06-18 11:12:31 +00:00
|
|
|
{
|
|
|
|
Heap::QmlContext *ctx = qmlContext();
|
2015-06-19 12:18:13 +00:00
|
|
|
if (!ctx)
|
2018-02-21 09:41:54 +00:00
|
|
|
return nullptr;
|
2012-11-17 20:54:26 +00:00
|
|
|
|
2017-07-04 14:07:50 +00:00
|
|
|
return ctx->qml()->scopeObject;
|
2015-06-15 14:41:24 +00:00
|
|
|
}
|
|
|
|
|
2020-02-26 15:04:23 +00:00
|
|
|
QQmlRefPointer<QQmlContextData> ExecutionEngine::callingQmlContext() const
|
2015-06-15 14:41:24 +00:00
|
|
|
{
|
2015-08-21 09:26:22 +00:00
|
|
|
Heap::QmlContext *ctx = qmlContext();
|
|
|
|
if (!ctx)
|
2018-02-21 09:41:54 +00:00
|
|
|
return nullptr;
|
2015-06-15 14:41:24 +00:00
|
|
|
|
2020-02-26 15:04:23 +00:00
|
|
|
return ctx->qml()->context;
|
2015-06-15 14:41:24 +00:00
|
|
|
}
|
|
|
|
|
2017-08-04 08:03:03 +00:00
|
|
|
StackTrace ExecutionEngine::stackTrace(int frameLimit) const
|
2013-05-22 08:47:36 +00:00
|
|
|
{
|
2014-11-28 08:31:10 +00:00
|
|
|
Scope scope(const_cast<ExecutionEngine *>(this));
|
2014-03-04 12:19:27 +00:00
|
|
|
ScopedString name(scope);
|
2017-08-04 08:03:03 +00:00
|
|
|
StackTrace stack;
|
2013-05-22 08:47:36 +00:00
|
|
|
|
2017-08-09 15:14:11 +00:00
|
|
|
CppStackFrame *f = currentStackFrame;
|
2017-08-03 11:41:13 +00:00
|
|
|
while (f && frameLimit) {
|
2017-08-04 08:03:03 +00:00
|
|
|
QV4::StackFrame frame;
|
|
|
|
frame.source = f->source();
|
|
|
|
frame.function = f->function();
|
2017-10-24 11:56:09 +00:00
|
|
|
frame.line = qAbs(f->lineNumber());
|
2017-08-21 08:59:53 +00:00
|
|
|
frame.column = -1;
|
2013-05-25 13:31:23 +00:00
|
|
|
stack.append(frame);
|
Optimize stack frame setup for AOT compiled functions
When called via the metaobject system, parameters and return values are
passed as void*, with accompanying type information in the form of
QMetaType. The same format is expected when calling an AOT
compiled function.
Previously, we would first convert all the parameters to QV4::Value,
just to convert them back the moment we notice that there is an AOT
compiled function. This is wasteful.
This change provides a second call infrastructure that accepts void* and
QMetaType as parameter and return value format, and passes them as-is
all the way to any AOT compiled functions. If there is no AOT compiled
function, the conversion is done when detecting this, rather than when
initiating the call. This also passes the information "ignore return
value" all the way down to the actual function call. If the caller is
not interested in the return value, we don't have to marshal it back at
all.
For now, we only add the extra "callWithMetaTypes" vtable entry to
ArrowFunction. However, other callables could also receive variants
optimized for calling with void*/int rather than V4 values.
This required changing the way how function arguments are stored in the
property cache. We squeeze the return type into
QQmlPropertyCacheMethodArguments now, and we use QMetaType instead of
integers. In turn, we remove some unused bits.
Change-Id: I946e603e623d9d985c54d3a15f6f4b7c7b7d8c60
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2021-03-12 22:25:30 +00:00
|
|
|
if (f->isJSTypesFrame()) {
|
|
|
|
if (static_cast<JSTypesStackFrame *>(f)->isTailCalling()) {
|
|
|
|
QV4::StackFrame frame;
|
|
|
|
frame.function = QStringLiteral("[elided tail calls]");
|
|
|
|
stack.append(frame);
|
|
|
|
}
|
2019-01-14 13:21:21 +00:00
|
|
|
}
|
2017-08-03 11:41:13 +00:00
|
|
|
--frameLimit;
|
Optimize stack frame setup for AOT compiled functions
When called via the metaobject system, parameters and return values are
passed as void*, with accompanying type information in the form of
QMetaType. The same format is expected when calling an AOT
compiled function.
Previously, we would first convert all the parameters to QV4::Value,
just to convert them back the moment we notice that there is an AOT
compiled function. This is wasteful.
This change provides a second call infrastructure that accepts void* and
QMetaType as parameter and return value format, and passes them as-is
all the way to any AOT compiled functions. If there is no AOT compiled
function, the conversion is done when detecting this, rather than when
initiating the call. This also passes the information "ignore return
value" all the way down to the actual function call. If the caller is
not interested in the return value, we don't have to marshal it back at
all.
For now, we only add the extra "callWithMetaTypes" vtable entry to
ArrowFunction. However, other callables could also receive variants
optimized for calling with void*/int rather than V4 values.
This required changing the way how function arguments are stored in the
property cache. We squeeze the return type into
QQmlPropertyCacheMethodArguments now, and we use QMetaType instead of
integers. In turn, we remove some unused bits.
Change-Id: I946e603e623d9d985c54d3a15f6f4b7c7b7d8c60
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2021-03-12 22:25:30 +00:00
|
|
|
f = f->parentFrame();
|
2013-05-25 13:31:23 +00:00
|
|
|
}
|
2013-05-22 08:47:36 +00:00
|
|
|
|
2017-08-03 11:41:13 +00:00
|
|
|
return stack;
|
2013-05-22 08:47:36 +00:00
|
|
|
}
|
|
|
|
|
2013-12-20 14:38:37 +00:00
|
|
|
/* Helper and "C" linkage exported function to format a GDBMI stacktrace for
|
|
|
|
* invocation by a debugger.
|
|
|
|
* Sample GDB invocation: print qt_v4StackTrace((void*)0x7fffffffb290)
|
|
|
|
* Sample CDB invocation: .call Qt5Qmld!qt_v4StackTrace(0x7fffffffb290) ; gh
|
|
|
|
* Note: The helper is there to suppress MSVC warning 4190 about anything
|
|
|
|
* with UDT return types in a "C" linkage function. */
|
|
|
|
|
|
|
|
static inline char *v4StackTrace(const ExecutionContext *context)
|
|
|
|
{
|
|
|
|
QString result;
|
|
|
|
QTextStream str(&result);
|
|
|
|
str << "stack=[";
|
2017-05-19 13:50:22 +00:00
|
|
|
if (context && context->engine()) {
|
|
|
|
const QVector<StackFrame> stackTrace = context->engine()->stackTrace(20);
|
2013-12-20 14:38:37 +00:00
|
|
|
for (int i = 0; i < stackTrace.size(); ++i) {
|
|
|
|
if (i)
|
|
|
|
str << ',';
|
|
|
|
const QUrl url(stackTrace.at(i).source);
|
|
|
|
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\"}";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
str << ']';
|
|
|
|
return qstrdup(result.toLocal8Bit().constData());
|
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" Q_QML_EXPORT char *qt_v4StackTrace(void *executionContext)
|
|
|
|
{
|
|
|
|
return v4StackTrace(reinterpret_cast<const ExecutionContext *>(executionContext));
|
|
|
|
}
|
|
|
|
|
2019-03-29 15:02:09 +00:00
|
|
|
extern "C" Q_QML_EXPORT char *qt_v4StackTraceForEngine(void *executionEngine)
|
|
|
|
{
|
|
|
|
auto engine = (reinterpret_cast<const ExecutionEngine *>(executionEngine));
|
|
|
|
return v4StackTrace(engine->currentContext());
|
|
|
|
}
|
|
|
|
|
2013-05-29 07:59:40 +00:00
|
|
|
QUrl ExecutionEngine::resolvedUrl(const QString &file)
|
|
|
|
{
|
|
|
|
QUrl src(file);
|
|
|
|
if (!src.isRelative())
|
|
|
|
return src;
|
|
|
|
|
|
|
|
QUrl base;
|
2017-08-09 15:14:11 +00:00
|
|
|
CppStackFrame *f = currentStackFrame;
|
2017-08-07 06:37:45 +00:00
|
|
|
while (f) {
|
|
|
|
if (f->v4Function) {
|
2018-01-30 09:42:34 +00:00
|
|
|
base = f->v4Function->finalUrl();
|
2013-05-29 07:59:40 +00:00
|
|
|
break;
|
|
|
|
}
|
Optimize stack frame setup for AOT compiled functions
When called via the metaobject system, parameters and return values are
passed as void*, with accompanying type information in the form of
QMetaType. The same format is expected when calling an AOT
compiled function.
Previously, we would first convert all the parameters to QV4::Value,
just to convert them back the moment we notice that there is an AOT
compiled function. This is wasteful.
This change provides a second call infrastructure that accepts void* and
QMetaType as parameter and return value format, and passes them as-is
all the way to any AOT compiled functions. If there is no AOT compiled
function, the conversion is done when detecting this, rather than when
initiating the call. This also passes the information "ignore return
value" all the way down to the actual function call. If the caller is
not interested in the return value, we don't have to marshal it back at
all.
For now, we only add the extra "callWithMetaTypes" vtable entry to
ArrowFunction. However, other callables could also receive variants
optimized for calling with void*/int rather than V4 values.
This required changing the way how function arguments are stored in the
property cache. We squeeze the return type into
QQmlPropertyCacheMethodArguments now, and we use QMetaType instead of
integers. In turn, we remove some unused bits.
Change-Id: I946e603e623d9d985c54d3a15f6f4b7c7b7d8c60
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2021-03-12 22:25:30 +00:00
|
|
|
f = f->parentFrame();
|
2013-05-29 07:59:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (base.isEmpty() && globalCode)
|
2017-12-11 11:44:47 +00:00
|
|
|
base = globalCode->finalUrl();
|
2013-05-29 07:59:40 +00:00
|
|
|
|
|
|
|
if (base.isEmpty())
|
|
|
|
return src;
|
|
|
|
|
|
|
|
return base.resolved(src);
|
|
|
|
}
|
|
|
|
|
2017-03-09 09:36:16 +00:00
|
|
|
void ExecutionEngine::markObjects(MarkStack *markStack)
|
2013-01-28 15:46:09 +00:00
|
|
|
{
|
2020-02-17 14:30:54 +00:00
|
|
|
for (int i = 0; i < NClasses; ++i) {
|
|
|
|
if (Heap::InternalClass *c = classes[i])
|
|
|
|
c->mark(markStack);
|
|
|
|
}
|
2013-08-29 11:24:38 +00:00
|
|
|
|
2018-04-06 14:24:59 +00:00
|
|
|
identifierTable->markObjects(markStack);
|
|
|
|
|
2020-02-17 14:30:54 +00:00
|
|
|
for (auto compilationUnit: compilationUnits)
|
2017-10-15 19:30:20 +00:00
|
|
|
compilationUnit->markObjects(markStack);
|
2013-01-28 15:46:09 +00:00
|
|
|
}
|
2013-05-25 13:31:23 +00:00
|
|
|
|
2015-01-15 10:36:57 +00:00
|
|
|
ReturnedValue ExecutionEngine::throwError(const Value &value)
|
2013-10-01 14:19:28 +00:00
|
|
|
{
|
2013-10-22 11:26:08 +00:00
|
|
|
// we can get in here with an exception already set, as the runtime
|
|
|
|
// doesn't check after every operation that can throw.
|
|
|
|
// in this case preserve the first exception to give correct error
|
|
|
|
// information
|
|
|
|
if (hasException)
|
|
|
|
return Encode::undefined();
|
|
|
|
|
2013-10-01 14:19:28 +00:00
|
|
|
hasException = true;
|
2015-03-13 16:21:18 +00:00
|
|
|
*exceptionValue = value;
|
2013-10-01 14:19:28 +00:00
|
|
|
QV4::Scope scope(this);
|
|
|
|
QV4::Scoped<ErrorObject> error(scope, value);
|
|
|
|
if (!!error)
|
2016-09-09 08:06:31 +00:00
|
|
|
exceptionStackTrace = *error->d()->stackTrace;
|
2013-10-01 14:19:28 +00:00
|
|
|
else
|
|
|
|
exceptionStackTrace = stackTrace();
|
|
|
|
|
2016-07-25 15:02:45 +00:00
|
|
|
if (QV4::Debugging::Debugger *debug = debugger())
|
|
|
|
debug->aboutToThrow();
|
2013-10-01 14:19:28 +00:00
|
|
|
|
2013-10-21 07:57:58 +00:00
|
|
|
return Encode::undefined();
|
2013-10-01 14:19:28 +00:00
|
|
|
}
|
|
|
|
|
2014-11-12 12:55:55 +00:00
|
|
|
ReturnedValue ExecutionEngine::catchException(StackTrace *trace)
|
2013-10-01 14:19:28 +00:00
|
|
|
{
|
2013-10-16 12:03:48 +00:00
|
|
|
Q_ASSERT(hasException);
|
2013-10-01 14:19:28 +00:00
|
|
|
if (trace)
|
|
|
|
*trace = exceptionStackTrace;
|
|
|
|
exceptionStackTrace.clear();
|
|
|
|
hasException = false;
|
2015-03-13 16:21:18 +00:00
|
|
|
ReturnedValue res = exceptionValue->asReturnedValue();
|
2018-09-11 09:07:32 +00:00
|
|
|
*exceptionValue = Value::emptyValue();
|
2013-10-01 14:19:28 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2014-07-28 08:07:57 +00:00
|
|
|
ReturnedValue ExecutionEngine::throwError(const QString &message)
|
|
|
|
{
|
|
|
|
Scope scope(this);
|
|
|
|
ScopedValue v(scope, newString(message));
|
|
|
|
v = newErrorObject(v);
|
|
|
|
return throwError(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
ReturnedValue ExecutionEngine::throwSyntaxError(const QString &message, const QString &fileName, int line, int column)
|
|
|
|
{
|
|
|
|
Scope scope(this);
|
2014-12-31 18:37:47 +00:00
|
|
|
ScopedObject error(scope, newSyntaxErrorObject(message, fileName, line, column));
|
2014-07-28 08:07:57 +00:00
|
|
|
return throwError(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
ReturnedValue ExecutionEngine::throwSyntaxError(const QString &message)
|
|
|
|
{
|
|
|
|
Scope scope(this);
|
2014-12-31 18:37:47 +00:00
|
|
|
ScopedObject error(scope, newSyntaxErrorObject(message));
|
2014-07-28 08:07:57 +00:00
|
|
|
return throwError(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ReturnedValue ExecutionEngine::throwTypeError()
|
|
|
|
{
|
|
|
|
Scope scope(this);
|
2014-12-31 18:37:47 +00:00
|
|
|
ScopedObject error(scope, newTypeErrorObject(QStringLiteral("Type error")));
|
2014-07-28 08:07:57 +00:00
|
|
|
return throwError(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
ReturnedValue ExecutionEngine::throwTypeError(const QString &message)
|
|
|
|
{
|
|
|
|
Scope scope(this);
|
2014-12-31 18:37:47 +00:00
|
|
|
ScopedObject error(scope, newTypeErrorObject(message));
|
2014-07-28 08:07:57 +00:00
|
|
|
return throwError(error);
|
|
|
|
}
|
|
|
|
|
2019-01-02 15:09:56 +00:00
|
|
|
ReturnedValue ExecutionEngine::throwReferenceError(const QString &name)
|
|
|
|
{
|
|
|
|
Scope scope(this);
|
|
|
|
QString msg = name + QLatin1String(" is not defined");
|
|
|
|
ScopedObject error(scope, newReferenceErrorObject(msg));
|
2014-07-28 08:07:57 +00:00
|
|
|
return throwError(error);
|
|
|
|
}
|
|
|
|
|
2015-01-15 10:36:57 +00:00
|
|
|
ReturnedValue ExecutionEngine::throwReferenceError(const Value &value)
|
2014-07-28 08:07:57 +00:00
|
|
|
{
|
|
|
|
Scope scope(this);
|
2015-01-15 10:36:57 +00:00
|
|
|
ScopedString s(scope, value.toString(this));
|
2016-06-07 14:15:07 +00:00
|
|
|
QString msg = s->toQString() + QLatin1String(" is not defined");
|
2014-12-31 18:37:47 +00:00
|
|
|
ScopedObject error(scope, newReferenceErrorObject(msg));
|
2014-07-28 08:07:57 +00:00
|
|
|
return throwError(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
ReturnedValue ExecutionEngine::throwReferenceError(const QString &message, const QString &fileName, int line, int column)
|
|
|
|
{
|
|
|
|
Scope scope(this);
|
|
|
|
QString msg = message;
|
2014-12-31 18:37:47 +00:00
|
|
|
ScopedObject error(scope, newReferenceErrorObject(msg, fileName, line, column));
|
2014-07-28 08:07:57 +00:00
|
|
|
return throwError(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
ReturnedValue ExecutionEngine::throwRangeError(const QString &message)
|
|
|
|
{
|
|
|
|
Scope scope(this);
|
|
|
|
ScopedObject error(scope, newRangeErrorObject(message));
|
|
|
|
return throwError(error);
|
|
|
|
}
|
|
|
|
|
2015-01-15 10:36:57 +00:00
|
|
|
ReturnedValue ExecutionEngine::throwRangeError(const Value &value)
|
2014-07-28 08:07:57 +00:00
|
|
|
{
|
|
|
|
Scope scope(this);
|
2015-01-15 10:36:57 +00:00
|
|
|
ScopedString s(scope, value.toString(this));
|
2016-06-07 14:15:07 +00:00
|
|
|
QString msg = s->toQString() + QLatin1String(" out of range");
|
2014-07-28 08:07:57 +00:00
|
|
|
ScopedObject error(scope, newRangeErrorObject(msg));
|
|
|
|
return throwError(error);
|
|
|
|
}
|
|
|
|
|
2015-01-15 10:36:57 +00:00
|
|
|
ReturnedValue ExecutionEngine::throwURIError(const Value &msg)
|
2014-07-28 08:07:57 +00:00
|
|
|
{
|
|
|
|
Scope scope(this);
|
|
|
|
ScopedObject error(scope, newURIErrorObject(msg));
|
|
|
|
return throwError(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
ReturnedValue ExecutionEngine::throwUnimplemented(const QString &message)
|
|
|
|
{
|
|
|
|
Scope scope(this);
|
2016-08-24 07:49:13 +00:00
|
|
|
ScopedValue v(scope, newString(QLatin1String("Unimplemented ") + message));
|
2014-07-28 08:07:57 +00:00
|
|
|
v = newErrorObject(v);
|
|
|
|
return throwError(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-12 12:55:55 +00:00
|
|
|
QQmlError ExecutionEngine::catchExceptionAsQmlError()
|
2013-10-17 06:56:51 +00:00
|
|
|
{
|
|
|
|
QV4::StackTrace trace;
|
2014-11-12 12:55:55 +00:00
|
|
|
QV4::Scope scope(this);
|
|
|
|
QV4::ScopedValue exception(scope, catchException(&trace));
|
2013-10-17 06:56:51 +00:00
|
|
|
QQmlError error;
|
|
|
|
if (!trace.isEmpty()) {
|
2016-01-05 15:30:34 +00:00
|
|
|
QV4::StackFrame frame = trace.constFirst();
|
2013-10-17 06:56:51 +00:00
|
|
|
error.setUrl(QUrl(frame.source));
|
|
|
|
error.setLine(frame.line);
|
|
|
|
error.setColumn(frame.column);
|
|
|
|
}
|
|
|
|
QV4::Scoped<QV4::ErrorObject> errorObj(scope, exception);
|
2018-04-10 10:47:53 +00:00
|
|
|
error.setDescription(exception->toQStringNoThrow());
|
2013-10-17 06:56:51 +00:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2015-01-02 14:07:35 +00:00
|
|
|
// Variant conversion code
|
|
|
|
|
|
|
|
typedef QSet<QV4::Heap::Object *> V4ObjectSet;
|
2021-03-24 14:36:37 +00:00
|
|
|
static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjects, V4ObjectSet *visitedObjects);
|
2020-03-04 15:46:42 +00:00
|
|
|
static QObject *qtObjectFromJS(const QV4::Value &value);
|
2018-02-21 09:41:54 +00:00
|
|
|
static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V4ObjectSet *visitedObjects = nullptr);
|
2021-03-22 12:27:05 +00:00
|
|
|
static bool convertToNativeQObject(const QV4::Value &value, QMetaType targetType, void **result);
|
2015-01-02 14:07:35 +00:00
|
|
|
static QV4::ReturnedValue variantListToJS(QV4::ExecutionEngine *v4, const QVariantList &lst);
|
QV4Engine: Unify fromValue and metaTypeToJS
Somehow, we ended up with two codepaths doing essentially the same
thing: constructing a JS value from a QVariant. metaTypeToJS is invoked
from QJSEngine::toScriptValue, whereas fromVariant() is used in various
places internally.
metaTypeToJS lacks proper handling for a number of cases, such as
builtin types like QPointF, which lead to toScriptValue(QPointF)
(incorrectly, and uselessly) constructing a VariantObject which couldn't
then do anything useful.
[ChangeLog][QtQml] QJSEngine::toScriptValue will now return correct
JavaScript objects in more cases, for example, for gadget types like
QPointF.
[ChangeLog][QtQml] QJSEngine::toScriptValue now uses the same
behavior as the rest of the engine when building JavaScript values, which
will cause the types of some returned JavaScript objects to change. For
instance, string lists are now returned as sequence objects, not array
objects, and QChar now constructs a JavaScript string.
Change-Id: I0290eb7c9c46e7b508d497cc33cd61d9378f3872
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2019-02-12 13:37:23 +00:00
|
|
|
static QV4::ReturnedValue sequentialIterableToJS(QV4::ExecutionEngine *v4, const QSequentialIterable &lst);
|
2015-01-02 14:07:35 +00:00
|
|
|
static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVariantMap &vmap);
|
|
|
|
static QV4::ReturnedValue variantToJS(QV4::ExecutionEngine *v4, const QVariant &value)
|
|
|
|
{
|
2021-02-19 11:29:58 +00:00
|
|
|
return v4->metaTypeToJS(value.metaType(), value.constData());
|
2015-01-02 14:07:35 +00:00
|
|
|
}
|
|
|
|
|
2021-03-24 14:36:37 +00:00
|
|
|
static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMetaType metaType, bool createJSValueForObjects, V4ObjectSet *visitedObjects)
|
2015-01-02 13:37:26 +00:00
|
|
|
{
|
2015-01-15 10:36:57 +00:00
|
|
|
Q_ASSERT (!value.isEmpty());
|
2015-01-02 13:37:26 +00:00
|
|
|
QV4::Scope scope(e);
|
|
|
|
|
2015-02-13 09:02:28 +00:00
|
|
|
if (const QV4::VariantObject *v = value.as<QV4::VariantObject>())
|
2016-10-05 10:18:20 +00:00
|
|
|
return v->d()->data();
|
2015-01-02 13:37:26 +00:00
|
|
|
|
2021-06-07 13:28:54 +00:00
|
|
|
if (metaType == QMetaType::fromType<bool>())
|
2015-01-15 10:36:57 +00:00
|
|
|
return QVariant(value.toBoolean());
|
2015-01-02 13:37:26 +00:00
|
|
|
|
2021-06-07 13:28:54 +00:00
|
|
|
if (metaType == QMetaType::fromType<QJsonValue>())
|
2015-01-02 13:37:26 +00:00
|
|
|
return QVariant::fromValue(QV4::JsonObject::toJsonValue(value));
|
|
|
|
|
2021-03-24 14:36:37 +00:00
|
|
|
if (metaType == QMetaType::fromType<QJSValue>())
|
2020-03-04 15:46:42 +00:00
|
|
|
return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(value.asReturnedValue()));
|
2015-01-02 13:37:26 +00:00
|
|
|
|
2017-01-26 10:46:16 +00:00
|
|
|
if (value.as<QV4::Object>()) {
|
2015-01-02 13:37:26 +00:00
|
|
|
QV4::ScopedObject object(scope, value);
|
2021-06-07 13:28:54 +00:00
|
|
|
if (metaType == QMetaType::fromType<QJsonObject>()
|
2015-02-13 12:56:05 +00:00
|
|
|
&& !value.as<ArrayObject>() && !value.as<FunctionObject>()) {
|
2015-01-02 13:37:26 +00:00
|
|
|
return QVariant::fromValue(QV4::JsonObject::toJsonObject(object));
|
|
|
|
} else if (QV4::QObjectWrapper *wrapper = object->as<QV4::QObjectWrapper>()) {
|
2019-04-05 07:42:17 +00:00
|
|
|
return QVariant::fromValue<QObject *>(wrapper->object());
|
2017-05-04 08:56:01 +00:00
|
|
|
} else if (object->as<QV4::QQmlContextWrapper>()) {
|
2015-01-02 13:37:26 +00:00
|
|
|
return QVariant();
|
2017-05-03 09:00:41 +00:00
|
|
|
} else if (QV4::QQmlTypeWrapper *w = object->as<QV4::QQmlTypeWrapper>()) {
|
2015-01-02 13:37:26 +00:00
|
|
|
return w->toVariant();
|
|
|
|
} else if (QV4::QQmlValueTypeWrapper *v = object->as<QV4::QQmlValueTypeWrapper>()) {
|
|
|
|
return v->toVariant();
|
|
|
|
} else if (QV4::QmlListWrapper *l = object->as<QV4::QmlListWrapper>()) {
|
|
|
|
return l->toVariant();
|
2022-05-17 13:50:06 +00:00
|
|
|
} else if (QV4::Sequence *s = object->as<QV4::Sequence>()) {
|
|
|
|
return QV4::SequencePrototype::toVariant(s);
|
2017-12-18 15:20:11 +00:00
|
|
|
}
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
|
2015-02-13 09:42:01 +00:00
|
|
|
if (value.as<ArrayObject>()) {
|
2015-01-02 13:37:26 +00:00
|
|
|
QV4::ScopedArrayObject a(scope, value);
|
2021-06-07 13:28:54 +00:00
|
|
|
if (metaType == QMetaType::fromType<QList<QObject *>>()) {
|
2015-01-02 13:37:26 +00:00
|
|
|
QList<QObject *> list;
|
|
|
|
uint length = a->getLength();
|
|
|
|
QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope);
|
|
|
|
for (uint ii = 0; ii < length; ++ii) {
|
2018-06-22 20:59:43 +00:00
|
|
|
qobjectWrapper = a->get(ii);
|
2015-01-02 13:37:26 +00:00
|
|
|
if (!!qobjectWrapper) {
|
|
|
|
list << qobjectWrapper->object();
|
|
|
|
} else {
|
|
|
|
list << 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-05 07:42:17 +00:00
|
|
|
return QVariant::fromValue<QList<QObject*> >(list);
|
2021-06-07 13:28:54 +00:00
|
|
|
} else if (metaType == QMetaType::fromType<QJsonArray>()) {
|
2015-01-02 13:37:26 +00:00
|
|
|
return QVariant::fromValue(QV4::JsonObject::toJsonArray(a));
|
|
|
|
}
|
|
|
|
|
2020-01-02 13:42:12 +00:00
|
|
|
QVariant retn;
|
2015-01-02 13:37:26 +00:00
|
|
|
bool succeeded = false;
|
2021-06-07 13:28:54 +00:00
|
|
|
retn = QV4::SequencePrototype::toVariant(value, metaType, &succeeded);
|
2015-01-02 13:37:26 +00:00
|
|
|
if (succeeded)
|
|
|
|
return retn;
|
2022-01-10 15:53:05 +00:00
|
|
|
|
2021-03-24 14:36:37 +00:00
|
|
|
if (metaType.isValid()) {
|
2020-07-10 08:33:49 +00:00
|
|
|
retn = QVariant(metaType, nullptr);
|
2020-09-07 14:01:30 +00:00
|
|
|
auto retnAsIterable = retn.value<QSequentialIterable>();
|
2020-09-17 08:17:13 +00:00
|
|
|
if (retnAsIterable.metaContainer().canAddValue()) {
|
|
|
|
QMetaType valueMetaType = retnAsIterable.metaContainer().valueMetaType();
|
2020-02-07 08:57:02 +00:00
|
|
|
auto const length = a->getLength();
|
|
|
|
QV4::ScopedValue arrayValue(scope);
|
|
|
|
for (qint64 i = 0; i < length; ++i) {
|
|
|
|
arrayValue = a->get(i);
|
2020-05-11 11:16:10 +00:00
|
|
|
QVariant asVariant;
|
2020-09-17 08:17:13 +00:00
|
|
|
if (QMetaType::canConvert(QMetaType::fromType<QJSValue>(), valueMetaType)) {
|
2020-05-11 11:16:10 +00:00
|
|
|
// before attempting a conversion from the concrete types,
|
|
|
|
// check if there exists a conversion from QJSValue -> out type
|
|
|
|
// prefer that one for compatibility reasons
|
2020-09-07 14:01:30 +00:00
|
|
|
asVariant = QVariant::fromValue(QJSValuePrivate::fromReturnedValue(
|
|
|
|
arrayValue->asReturnedValue()));
|
|
|
|
if (asVariant.convert(valueMetaType)) {
|
2020-09-17 08:17:13 +00:00
|
|
|
retnAsIterable.metaContainer().addValue(retn.data(), asVariant.constData());
|
2020-05-11 11:16:10 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2021-06-16 13:01:00 +00:00
|
|
|
|
|
|
|
asVariant = toVariant(e, arrayValue, valueMetaType, false, visitedObjects);
|
|
|
|
if (valueMetaType == QMetaType::fromType<QVariant>()) {
|
|
|
|
retnAsIterable.metaContainer().addValue(retn.data(), &asVariant);
|
|
|
|
} else {
|
2020-09-07 14:01:30 +00:00
|
|
|
auto originalType = asVariant.metaType();
|
|
|
|
bool couldConvert = asVariant.convert(valueMetaType);
|
|
|
|
if (!couldConvert) {
|
|
|
|
qWarning() << 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()));
|
|
|
|
// create default constructed value
|
|
|
|
asVariant = QVariant(valueMetaType, nullptr);
|
|
|
|
}
|
2021-06-16 13:01:00 +00:00
|
|
|
retnAsIterable.metaContainer().addValue(retn.data(), asVariant.constData());
|
2020-02-07 08:57:02 +00:00
|
|
|
}
|
2020-01-02 13:42:12 +00:00
|
|
|
}
|
2020-02-07 08:57:02 +00:00
|
|
|
return retn;
|
2020-01-02 13:42:12 +00:00
|
|
|
}
|
|
|
|
}
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
|
2015-01-15 10:36:57 +00:00
|
|
|
if (value.isUndefined())
|
2015-01-02 13:37:26 +00:00
|
|
|
return QVariant();
|
2015-01-15 10:36:57 +00:00
|
|
|
if (value.isNull())
|
2016-08-05 08:02:52 +00:00
|
|
|
return QVariant::fromValue(nullptr);
|
2015-01-15 10:36:57 +00:00
|
|
|
if (value.isBoolean())
|
|
|
|
return value.booleanValue();
|
|
|
|
if (value.isInteger())
|
|
|
|
return value.integerValue();
|
|
|
|
if (value.isNumber())
|
|
|
|
return value.asDouble();
|
2016-11-24 15:05:14 +00:00
|
|
|
if (String *s = value.stringValue()) {
|
|
|
|
const QString &str = s->toQString();
|
2016-06-02 10:08:29 +00:00
|
|
|
// QChars are stored as a strings
|
2021-06-07 13:28:54 +00:00
|
|
|
if (metaType == QMetaType::fromType<QChar>() && str.size() == 1)
|
2016-06-02 10:08:29 +00:00
|
|
|
return str.at(0);
|
|
|
|
return str;
|
|
|
|
}
|
2017-12-18 18:43:56 +00:00
|
|
|
#if QT_CONFIG(qml_locale)
|
2015-02-13 09:02:28 +00:00
|
|
|
if (const QV4::QQmlLocaleData *ld = value.as<QV4::QQmlLocaleData>())
|
2016-09-09 08:06:31 +00:00
|
|
|
return *ld->d()->locale;
|
2017-12-18 18:43:56 +00:00
|
|
|
#endif
|
Make QDate handling consistent in its use of UTC
Due to the quirk of ECMAScript's Date.parse() spec [0] stipulating the
use of UTC for date-only strings, in contrast to most other ways of
creating a Date using local time, reasonable users get surprised by
the behavior of QDate properties initialized from strings. This can't
be avoided without breaking other uses of Date, so document the
work-around needed to cope with it (use UTC-specific methods to access
the Date object).
[0] https://tc39.es/ecma262/#sec-date.parse
Make conversions back to QDate from Date work round the possibility
that the Date, seen as a QDateTime(,, LocalTime), needs to be handled
as UTC when extracting the date, and catch two more places that
conversion from QDate neglected to use UTC's start of day, for
consistency.
Revised tests to call UTC-specific methods instead of the local-time
ones, where appropriate. Drive-by: some tests were (entirely bogusly)
constructing a fresh Date using the UTC-fields of the Date they had,
in order to then test the non-UTC fields of this fresh Date; instead,
simply test that the UTC fields are as expected.
[ChangeLog][QML][Behavior change] Where a QDate is represented in
QML's JavaScript as a Date, it is now more consistently associated
with the start of the UTC day it describes. Previously cases where it
was represented as the start of local time's day could lead to a Date
turning into a QDate for the preceding day. Inconsistencies in the
specified behavior of Date preclude eliminating such problems
entirely, but they should now be limited to cases where (perversely
for a date property or parameter) the date is specified with a local
time late enough to make it coincide with the start of the next UTC
day (in which case that next day's QDate will be its C++
representation).
Fixes: QTBUG-92466
Change-Id: I2306dd9ecef0d5c2d59b562762392e51bb6d66ca
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2021-05-12 09:05:16 +00:00
|
|
|
if (const QV4::DateObject *d = value.as<DateObject>()) {
|
|
|
|
auto dt = d->toQDateTime();
|
|
|
|
// See ExecutionEngine::metaTypeFromJS()'s handling of QMetaType::Date:
|
2021-06-07 13:28:54 +00:00
|
|
|
if (metaType == QMetaType::fromType<QDate>()) {
|
Make QDate handling consistent in its use of UTC
Due to the quirk of ECMAScript's Date.parse() spec [0] stipulating the
use of UTC for date-only strings, in contrast to most other ways of
creating a Date using local time, reasonable users get surprised by
the behavior of QDate properties initialized from strings. This can't
be avoided without breaking other uses of Date, so document the
work-around needed to cope with it (use UTC-specific methods to access
the Date object).
[0] https://tc39.es/ecma262/#sec-date.parse
Make conversions back to QDate from Date work round the possibility
that the Date, seen as a QDateTime(,, LocalTime), needs to be handled
as UTC when extracting the date, and catch two more places that
conversion from QDate neglected to use UTC's start of day, for
consistency.
Revised tests to call UTC-specific methods instead of the local-time
ones, where appropriate. Drive-by: some tests were (entirely bogusly)
constructing a fresh Date using the UTC-fields of the Date they had,
in order to then test the non-UTC fields of this fresh Date; instead,
simply test that the UTC fields are as expected.
[ChangeLog][QML][Behavior change] Where a QDate is represented in
QML's JavaScript as a Date, it is now more consistently associated
with the start of the UTC day it describes. Previously cases where it
was represented as the start of local time's day could lead to a Date
turning into a QDate for the preceding day. Inconsistencies in the
specified behavior of Date preclude eliminating such problems
entirely, but they should now be limited to cases where (perversely
for a date property or parameter) the date is specified with a local
time late enough to make it coincide with the start of the next UTC
day (in which case that next day's QDate will be its C++
representation).
Fixes: QTBUG-92466
Change-Id: I2306dd9ecef0d5c2d59b562762392e51bb6d66ca
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2021-05-12 09:05:16 +00:00
|
|
|
const auto utc = dt.toUTC();
|
|
|
|
if (utc.date() != dt.date() && utc.addSecs(-1).date() == dt.date())
|
|
|
|
dt = utc;
|
|
|
|
}
|
|
|
|
return dt;
|
|
|
|
}
|
2021-04-20 08:43:11 +00:00
|
|
|
if (const QV4::UrlObject *d = value.as<UrlObject>())
|
|
|
|
return d->toQUrl();
|
2015-12-28 12:22:29 +00:00
|
|
|
if (const ArrayBuffer *d = value.as<ArrayBuffer>())
|
2015-05-23 12:10:36 +00:00
|
|
|
return d->asByteArray();
|
2015-01-02 13:37:26 +00:00
|
|
|
// NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)!
|
|
|
|
|
|
|
|
QV4::ScopedObject o(scope, value);
|
|
|
|
Q_ASSERT(o);
|
|
|
|
|
2019-02-13 14:54:14 +00:00
|
|
|
#if QT_CONFIG(regularexpression)
|
2020-04-02 09:07:14 +00:00
|
|
|
if (QV4::RegExpObject *re = o->as<QV4::RegExpObject>())
|
|
|
|
return re->toQRegularExpression();
|
2019-02-13 14:54:14 +00:00
|
|
|
#endif
|
2015-01-02 13:37:26 +00:00
|
|
|
|
|
|
|
if (createJSValueForObjects)
|
2020-03-04 15:46:42 +00:00
|
|
|
return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue()));
|
2015-01-02 13:37:26 +00:00
|
|
|
|
|
|
|
return objectToVariant(e, o, visitedObjects);
|
|
|
|
}
|
|
|
|
|
2021-03-24 14:36:37 +00:00
|
|
|
|
|
|
|
QVariant ExecutionEngine::toVariant(const Value &value, QMetaType typeHint, bool createJSValueForObjects)
|
|
|
|
{
|
|
|
|
return ::toVariant(this, value, typeHint, createJSValueForObjects, nullptr);
|
|
|
|
}
|
|
|
|
|
2015-04-26 20:58:49 +00:00
|
|
|
static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V4ObjectSet *visitedObjects)
|
2015-01-02 13:37:26 +00:00
|
|
|
{
|
|
|
|
Q_ASSERT(o);
|
|
|
|
|
|
|
|
V4ObjectSet recursionGuardSet;
|
|
|
|
if (!visitedObjects) {
|
|
|
|
visitedObjects = &recursionGuardSet;
|
|
|
|
} else if (visitedObjects->contains(o->d())) {
|
|
|
|
// Avoid recursion.
|
|
|
|
// For compatibility with QVariant{List,Map} conversion, we return an
|
|
|
|
// empty object (and no error is thrown).
|
2015-02-13 09:42:01 +00:00
|
|
|
if (o->as<ArrayObject>())
|
2015-01-02 13:37:26 +00:00
|
|
|
return QVariantList();
|
|
|
|
return QVariantMap();
|
|
|
|
}
|
|
|
|
visitedObjects->insert(o->d());
|
|
|
|
|
|
|
|
QVariant result;
|
|
|
|
|
2015-02-13 09:42:01 +00:00
|
|
|
if (o->as<ArrayObject>()) {
|
2015-01-02 13:37:26 +00:00
|
|
|
QV4::Scope scope(e);
|
|
|
|
QV4::ScopedArrayObject a(scope, o->asReturnedValue());
|
|
|
|
QV4::ScopedValue v(scope);
|
|
|
|
QVariantList list;
|
|
|
|
|
|
|
|
int length = a->getLength();
|
|
|
|
for (int ii = 0; ii < length; ++ii) {
|
2018-06-22 20:59:43 +00:00
|
|
|
v = a->get(ii);
|
2021-03-24 14:36:37 +00:00
|
|
|
list << ::toVariant(e, v, QMetaType {}, /*createJSValueForObjects*/false, visitedObjects);
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
result = list;
|
2021-10-19 13:22:25 +00:00
|
|
|
} else if (const FunctionObject *f = o->as<FunctionObject>()) {
|
|
|
|
// If it's a FunctionObject, we can only save it as QJSValue.
|
|
|
|
result = QVariant::fromValue(QJSValuePrivate::fromReturnedValue(f->asReturnedValue()));
|
|
|
|
} else {
|
2015-01-02 13:37:26 +00:00
|
|
|
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();
|
2021-03-24 14:36:37 +00:00
|
|
|
map.insert(key, ::toVariant(e, val, /*type hint*/ QMetaType {}, /*createJSValueForObjects*/false, visitedObjects));
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
result = map;
|
|
|
|
}
|
|
|
|
|
|
|
|
visitedObjects->remove(o->d());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-03-11 10:07:58 +00:00
|
|
|
/*!
|
|
|
|
\internal
|
|
|
|
|
|
|
|
Transform the given \a metaType and \a ptr into a JavaScript representation. You can pass an
|
|
|
|
optional \a variant in order to avoid the construction of a new QVariant in case the value
|
|
|
|
has to be stored as a variant object. In that case, the contents of \a variant have to be
|
|
|
|
exactly the same as \a metaType and \a ptr.
|
|
|
|
*/
|
|
|
|
QV4::ReturnedValue ExecutionEngine::fromData(
|
2021-06-07 13:28:54 +00:00
|
|
|
QMetaType metaType, const void *ptr, const QVariant *variant)
|
2021-03-11 10:07:58 +00:00
|
|
|
{
|
|
|
|
const int type = metaType.id();
|
2015-01-02 13:37:26 +00:00
|
|
|
if (type < QMetaType::User) {
|
|
|
|
switch (QMetaType::Type(type)) {
|
|
|
|
case QMetaType::UnknownType:
|
|
|
|
case QMetaType::Void:
|
|
|
|
return QV4::Encode::undefined();
|
2016-08-05 08:02:52 +00:00
|
|
|
case QMetaType::Nullptr:
|
2015-01-02 13:37:26 +00:00
|
|
|
case QMetaType::VoidStar:
|
|
|
|
return QV4::Encode::null();
|
|
|
|
case QMetaType::Bool:
|
|
|
|
return QV4::Encode(*reinterpret_cast<const bool*>(ptr));
|
|
|
|
case QMetaType::Int:
|
|
|
|
return QV4::Encode(*reinterpret_cast<const int*>(ptr));
|
|
|
|
case QMetaType::UInt:
|
|
|
|
return QV4::Encode(*reinterpret_cast<const uint*>(ptr));
|
|
|
|
case QMetaType::LongLong:
|
|
|
|
return QV4::Encode((double)*reinterpret_cast<const qlonglong*>(ptr));
|
|
|
|
case QMetaType::ULongLong:
|
|
|
|
return QV4::Encode((double)*reinterpret_cast<const qulonglong*>(ptr));
|
|
|
|
case QMetaType::Double:
|
|
|
|
return QV4::Encode(*reinterpret_cast<const double*>(ptr));
|
|
|
|
case QMetaType::QString:
|
2015-01-02 14:07:35 +00:00
|
|
|
return newString(*reinterpret_cast<const QString*>(ptr))->asReturnedValue();
|
2015-12-28 12:22:29 +00:00
|
|
|
case QMetaType::QByteArray:
|
|
|
|
return newArrayBuffer(*reinterpret_cast<const QByteArray*>(ptr))->asReturnedValue();
|
2015-01-02 13:37:26 +00:00
|
|
|
case QMetaType::Float:
|
|
|
|
return QV4::Encode(*reinterpret_cast<const float*>(ptr));
|
|
|
|
case QMetaType::Short:
|
|
|
|
return QV4::Encode((int)*reinterpret_cast<const short*>(ptr));
|
|
|
|
case QMetaType::UShort:
|
|
|
|
return QV4::Encode((int)*reinterpret_cast<const unsigned short*>(ptr));
|
|
|
|
case QMetaType::Char:
|
2016-06-02 10:08:29 +00:00
|
|
|
return QV4::Encode((int)*reinterpret_cast<const char*>(ptr));
|
2015-01-02 13:37:26 +00:00
|
|
|
case QMetaType::UChar:
|
2016-06-02 10:08:29 +00:00
|
|
|
return QV4::Encode((int)*reinterpret_cast<const unsigned char*>(ptr));
|
2015-01-02 13:37:26 +00:00
|
|
|
case QMetaType::QChar:
|
2015-03-12 14:33:50 +00:00
|
|
|
return newString(*reinterpret_cast<const QChar *>(ptr))->asReturnedValue();
|
2020-05-14 12:05:53 +00:00
|
|
|
case QMetaType::Char16:
|
2020-11-16 19:13:28 +00:00
|
|
|
return newString(QChar(*reinterpret_cast<const char16_t *>(ptr)))->asReturnedValue();
|
2015-01-02 13:37:26 +00:00
|
|
|
case QMetaType::QDateTime:
|
2015-01-02 14:07:35 +00:00
|
|
|
return QV4::Encode(newDateObject(*reinterpret_cast<const QDateTime *>(ptr)));
|
2015-01-02 13:37:26 +00:00
|
|
|
case QMetaType::QDate:
|
2020-07-30 13:37:09 +00:00
|
|
|
return QV4::Encode(newDateObject(reinterpret_cast<const QDate *>(ptr)->startOfDay(Qt::UTC)));
|
2015-01-02 13:37:26 +00:00
|
|
|
case QMetaType::QTime:
|
V4: Handle QTime->DateObject conversion better
By specification, date conversion functions for dates before the epoch
are not DST corrected. We converted QTime to a QDateTime where we set
the date part to Jan. 1, 1970, and then convert that to msecs since the
epoch UTC. For places on Earth where they had DST on that day (e.g.
Hobart in Australia), strange things happen: conversion from a QTime to
DateObject will use DST (because it's after the epoch in local time),
but conversions from DateObject to QTime won't use the DST because it's
before the epoch (in UTC).
Now as everyone knows, a 24-hour clock time has no meaning without a
date, only "elapsed time" has. But users still expect to be able to pass
QTime to QML/JS. So, we do the conversion on day 0 of month 0 of year 0,
and all of it in local time. This gives a stable conversion in both
directions, and the values in both C++ and QML/JS are the same for any
timezone (with or without DST) on this planet.
Task-number: QTBUG-54378
Change-Id: I892e16a93f015e92d311c6cae3ae7768b7373f6a
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
2016-07-08 14:40:28 +00:00
|
|
|
return QV4::Encode(newDateObjectFromTime(*reinterpret_cast<const QTime *>(ptr)));
|
2019-02-13 14:54:14 +00:00
|
|
|
#if QT_CONFIG(regularexpression)
|
|
|
|
case QMetaType::QRegularExpression:
|
|
|
|
return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegularExpression *>(ptr)));
|
|
|
|
#endif
|
2015-01-02 13:37:26 +00:00
|
|
|
case QMetaType::QObjectStar:
|
2015-01-02 14:07:35 +00:00
|
|
|
return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr));
|
2015-01-02 13:37:26 +00:00
|
|
|
case QMetaType::QStringList:
|
|
|
|
{
|
|
|
|
bool succeeded = false;
|
2015-01-02 14:07:35 +00:00
|
|
|
QV4::Scope scope(this);
|
2021-03-11 10:07:58 +00:00
|
|
|
QV4::ScopedValue retn(
|
|
|
|
scope, QV4::SequencePrototype::fromData(this, metaType, ptr, &succeeded));
|
2015-01-02 13:37:26 +00:00
|
|
|
if (succeeded)
|
2015-01-15 20:28:01 +00:00
|
|
|
return retn->asReturnedValue();
|
2015-01-02 14:07:35 +00:00
|
|
|
return QV4::Encode(newArrayObject(*reinterpret_cast<const QStringList *>(ptr)));
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
case QMetaType::QVariantList:
|
QV4Engine: Unify fromValue and metaTypeToJS
Somehow, we ended up with two codepaths doing essentially the same
thing: constructing a JS value from a QVariant. metaTypeToJS is invoked
from QJSEngine::toScriptValue, whereas fromVariant() is used in various
places internally.
metaTypeToJS lacks proper handling for a number of cases, such as
builtin types like QPointF, which lead to toScriptValue(QPointF)
(incorrectly, and uselessly) constructing a VariantObject which couldn't
then do anything useful.
[ChangeLog][QtQml] QJSEngine::toScriptValue will now return correct
JavaScript objects in more cases, for example, for gadget types like
QPointF.
[ChangeLog][QtQml] QJSEngine::toScriptValue now uses the same
behavior as the rest of the engine when building JavaScript values, which
will cause the types of some returned JavaScript objects to change. For
instance, string lists are now returned as sequence objects, not array
objects, and QChar now constructs a JavaScript string.
Change-Id: I0290eb7c9c46e7b508d497cc33cd61d9378f3872
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2019-02-12 13:37:23 +00:00
|
|
|
return variantListToJS(this, *reinterpret_cast<const QVariantList *>(ptr));
|
2015-01-02 13:37:26 +00:00
|
|
|
case QMetaType::QVariantMap:
|
QV4Engine: Unify fromValue and metaTypeToJS
Somehow, we ended up with two codepaths doing essentially the same
thing: constructing a JS value from a QVariant. metaTypeToJS is invoked
from QJSEngine::toScriptValue, whereas fromVariant() is used in various
places internally.
metaTypeToJS lacks proper handling for a number of cases, such as
builtin types like QPointF, which lead to toScriptValue(QPointF)
(incorrectly, and uselessly) constructing a VariantObject which couldn't
then do anything useful.
[ChangeLog][QtQml] QJSEngine::toScriptValue will now return correct
JavaScript objects in more cases, for example, for gadget types like
QPointF.
[ChangeLog][QtQml] QJSEngine::toScriptValue now uses the same
behavior as the rest of the engine when building JavaScript values, which
will cause the types of some returned JavaScript objects to change. For
instance, string lists are now returned as sequence objects, not array
objects, and QChar now constructs a JavaScript string.
Change-Id: I0290eb7c9c46e7b508d497cc33cd61d9378f3872
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2019-02-12 13:37:23 +00:00
|
|
|
return variantMapToJS(this, *reinterpret_cast<const QVariantMap *>(ptr));
|
2015-01-02 13:37:26 +00:00
|
|
|
case QMetaType::QJsonValue:
|
2015-01-02 14:07:35 +00:00
|
|
|
return QV4::JsonObject::fromJsonValue(this, *reinterpret_cast<const QJsonValue *>(ptr));
|
2015-01-02 13:37:26 +00:00
|
|
|
case QMetaType::QJsonObject:
|
2015-01-02 14:07:35 +00:00
|
|
|
return QV4::JsonObject::fromJsonObject(this, *reinterpret_cast<const QJsonObject *>(ptr));
|
2015-01-02 13:37:26 +00:00
|
|
|
case QMetaType::QJsonArray:
|
2015-01-02 14:07:35 +00:00
|
|
|
return QV4::JsonObject::fromJsonArray(this, *reinterpret_cast<const QJsonArray *>(ptr));
|
2017-12-18 18:43:56 +00:00
|
|
|
#if QT_CONFIG(qml_locale)
|
2015-01-02 13:37:26 +00:00
|
|
|
case QMetaType::QLocale:
|
2015-01-02 14:07:35 +00:00
|
|
|
return QQmlLocale::wrap(this, *reinterpret_cast<const QLocale*>(ptr));
|
2017-12-18 18:43:56 +00:00
|
|
|
#endif
|
2019-04-12 08:42:56 +00:00
|
|
|
case QMetaType::QPixmap:
|
|
|
|
case QMetaType::QImage:
|
|
|
|
// Scarce value types
|
2021-03-11 10:07:58 +00:00
|
|
|
return QV4::Encode(newVariantObject(variant ? *variant : QVariant(metaType, ptr)));
|
2015-01-02 13:37:26 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-06-07 13:24:42 +00:00
|
|
|
if (const QMetaObject *vtmo = QQmlMetaType::metaObjectForValueType(metaType))
|
2021-03-11 10:07:58 +00:00
|
|
|
return QV4::QQmlValueTypeWrapper::create(this, ptr, vtmo, metaType);
|
2015-01-02 13:37:26 +00:00
|
|
|
} else {
|
2015-01-02 14:07:35 +00:00
|
|
|
QV4::Scope scope(this);
|
2021-03-22 13:28:07 +00:00
|
|
|
if (metaType == QMetaType::fromType<QQmlListReference>()) {
|
2015-01-02 13:37:26 +00:00
|
|
|
typedef QQmlListReferencePrivate QDLRP;
|
2015-03-07 22:35:47 +00:00
|
|
|
QDLRP *p = QDLRP::get((QQmlListReference*)const_cast<void *>(ptr));
|
2021-03-23 13:16:15 +00:00
|
|
|
if (p->object)
|
2015-01-02 13:37:26 +00:00
|
|
|
return QV4::QmlListWrapper::create(scope.engine, p->property, p->propertyType);
|
2021-03-23 13:16:15 +00:00
|
|
|
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
|
2015-01-02 13:37:26 +00:00
|
|
|
return QV4::Encode::null();
|
2021-03-22 13:28:07 +00:00
|
|
|
} else if (metaType == QMetaType::fromType<QJSValue>()) {
|
2020-03-04 15:46:42 +00:00
|
|
|
return QJSValuePrivate::convertToReturnedValue(
|
|
|
|
this, *reinterpret_cast<const QJSValue *>(ptr));
|
2021-03-22 13:28:07 +00:00
|
|
|
} else if (metaType == QMetaType::fromType<QList<QObject *> >()) {
|
2015-01-02 13:37:26 +00:00
|
|
|
// XXX Can this be made more by using Array as a prototype and implementing
|
|
|
|
// directly against QList<QObject*>?
|
2015-03-07 22:35:47 +00:00
|
|
|
const QList<QObject *> &list = *(const QList<QObject *>*)ptr;
|
2015-01-02 14:07:35 +00:00
|
|
|
QV4::ScopedArrayObject a(scope, newArrayObject());
|
2015-01-02 13:37:26 +00:00
|
|
|
a->arrayReserve(list.count());
|
|
|
|
QV4::ScopedValue v(scope);
|
|
|
|
for (int ii = 0; ii < list.count(); ++ii)
|
2015-01-02 14:07:35 +00:00
|
|
|
a->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(this, list.at(ii))));
|
2015-01-02 13:37:26 +00:00
|
|
|
a->setArrayLengthUnchecked(list.count());
|
|
|
|
return a.asReturnedValue();
|
2021-03-04 19:23:23 +00:00
|
|
|
} else if (auto flags = metaType.flags(); flags & QMetaType::PointerToQObject) {
|
2021-02-20 16:33:09 +00:00
|
|
|
QV4::ReturnedValue ret = QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr));
|
|
|
|
if (!flags.testFlag(QMetaType::IsConst))
|
|
|
|
return ret;
|
|
|
|
QV4::ScopedValue v(scope, ret);
|
|
|
|
if (auto obj = v->as<Object>()) {
|
|
|
|
obj->setInternalClass(obj->internalClass()->cryopreserved());
|
|
|
|
return obj->asReturnedValue();
|
|
|
|
} else {
|
|
|
|
return ret;
|
|
|
|
}
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool succeeded = false;
|
2021-03-11 10:07:58 +00:00
|
|
|
QV4::ScopedValue retn(scope, QV4::SequencePrototype::fromData(this, metaType, ptr, &succeeded));
|
2015-01-02 13:37:26 +00:00
|
|
|
if (succeeded)
|
2015-01-15 20:28:01 +00:00
|
|
|
return retn->asReturnedValue();
|
2021-03-11 10:07:58 +00:00
|
|
|
|
|
|
|
if (QMetaType::canConvert(metaType, QMetaType::fromType<QSequentialIterable>())) {
|
|
|
|
QSequentialIterable lst;
|
|
|
|
QMetaType::convert(metaType, ptr, QMetaType::fromType<QSequentialIterable>(), &lst);
|
QV4Engine: Unify fromValue and metaTypeToJS
Somehow, we ended up with two codepaths doing essentially the same
thing: constructing a JS value from a QVariant. metaTypeToJS is invoked
from QJSEngine::toScriptValue, whereas fromVariant() is used in various
places internally.
metaTypeToJS lacks proper handling for a number of cases, such as
builtin types like QPointF, which lead to toScriptValue(QPointF)
(incorrectly, and uselessly) constructing a VariantObject which couldn't
then do anything useful.
[ChangeLog][QtQml] QJSEngine::toScriptValue will now return correct
JavaScript objects in more cases, for example, for gadget types like
QPointF.
[ChangeLog][QtQml] QJSEngine::toScriptValue now uses the same
behavior as the rest of the engine when building JavaScript values, which
will cause the types of some returned JavaScript objects to change. For
instance, string lists are now returned as sequence objects, not array
objects, and QChar now constructs a JavaScript string.
Change-Id: I0290eb7c9c46e7b508d497cc33cd61d9378f3872
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2019-02-12 13:37:23 +00:00
|
|
|
return sequentialIterableToJS(this, lst);
|
|
|
|
}
|
|
|
|
|
2021-06-07 13:24:42 +00:00
|
|
|
if (const QMetaObject *vtmo = QQmlMetaType::metaObjectForValueType(metaType))
|
2021-03-11 10:07:58 +00:00
|
|
|
return QV4::QQmlValueTypeWrapper::create(this, ptr, vtmo, metaType);
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// XXX TODO: To be compatible, we still need to handle:
|
|
|
|
// + QObjectList
|
|
|
|
// + QList<int>
|
|
|
|
|
2021-05-05 10:02:07 +00:00
|
|
|
// Enumeration types can just be treated as integers for now
|
|
|
|
if (metaType.flags() & QMetaType::IsEnumeration)
|
|
|
|
return QV4::Encode(*reinterpret_cast<const int *>(ptr));
|
|
|
|
|
2021-03-11 10:07:58 +00:00
|
|
|
return QV4::Encode(newVariantObject(variant ? *variant : QVariant(metaType, ptr)));
|
|
|
|
}
|
|
|
|
|
|
|
|
QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
|
|
|
|
{
|
|
|
|
return fromData(variant.metaType(), variant.constData(), &variant);
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
|
2015-04-26 20:58:49 +00:00
|
|
|
QVariantMap ExecutionEngine::variantMapFromJS(const Object *o)
|
2015-01-02 13:37:26 +00:00
|
|
|
{
|
2015-01-02 14:07:35 +00:00
|
|
|
return objectToVariant(this, o).toMap();
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 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.
|
2015-01-02 14:07:35 +00:00
|
|
|
static QV4::ReturnedValue variantListToJS(QV4::ExecutionEngine *v4, const QVariantList &lst)
|
2015-01-02 13:37:26 +00:00
|
|
|
{
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2018-09-24 13:05:13 +00:00
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2015-01-02 13:37:26 +00:00
|
|
|
// Converts a QVariantMap to JS.
|
|
|
|
// The result is a new Object object with property names being
|
|
|
|
// the keys of the QVariantMap, and values being the values of
|
|
|
|
// the QVariantMap converted to JS, recursively.
|
2015-01-02 14:07:35 +00:00
|
|
|
static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVariantMap &vmap)
|
2015-01-02 13:37:26 +00:00
|
|
|
{
|
|
|
|
QV4::Scope scope(v4);
|
|
|
|
QV4::ScopedObject o(scope, v4->newObject());
|
|
|
|
QV4::ScopedString s(scope);
|
2018-06-23 21:47:25 +00:00
|
|
|
QV4::ScopedPropertyKey key(scope);
|
2015-01-02 13:37:26 +00:00
|
|
|
QV4::ScopedValue v(scope);
|
2015-02-23 12:44:12 +00:00
|
|
|
for (QVariantMap::const_iterator it = vmap.constBegin(), cend = vmap.constEnd(); it != cend; ++it) {
|
2015-01-02 13:37:26 +00:00
|
|
|
s = v4->newIdentifier(it.key());
|
2018-06-23 21:47:25 +00:00
|
|
|
key = s->propertyKey();
|
2015-01-02 13:37:26 +00:00
|
|
|
v = variantToJS(v4, it.value());
|
2019-03-29 08:24:27 +00:00
|
|
|
if (key->isArrayIndex())
|
|
|
|
o->arraySet(key->asArrayIndex(), v);
|
2015-01-02 13:37:26 +00:00
|
|
|
else
|
|
|
|
o->insertMember(s, v);
|
|
|
|
}
|
|
|
|
return o.asReturnedValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Converts the meta-type defined by the given type and data to JS.
|
|
|
|
// Returns the value if conversion succeeded, an empty handle otherwise.
|
2021-02-19 11:29:58 +00:00
|
|
|
QV4::ReturnedValue ExecutionEngine::metaTypeToJS(QMetaType type, const void *data)
|
2015-01-02 13:37:26 +00:00
|
|
|
{
|
2018-02-21 09:41:54 +00:00
|
|
|
Q_ASSERT(data != nullptr);
|
2015-01-02 13:37:26 +00:00
|
|
|
|
2021-03-11 10:07:58 +00:00
|
|
|
if (type == QMetaType::fromType<QVariant>()) {
|
QV4Engine: Unify fromValue and metaTypeToJS
Somehow, we ended up with two codepaths doing essentially the same
thing: constructing a JS value from a QVariant. metaTypeToJS is invoked
from QJSEngine::toScriptValue, whereas fromVariant() is used in various
places internally.
metaTypeToJS lacks proper handling for a number of cases, such as
builtin types like QPointF, which lead to toScriptValue(QPointF)
(incorrectly, and uselessly) constructing a VariantObject which couldn't
then do anything useful.
[ChangeLog][QtQml] QJSEngine::toScriptValue will now return correct
JavaScript objects in more cases, for example, for gadget types like
QPointF.
[ChangeLog][QtQml] QJSEngine::toScriptValue now uses the same
behavior as the rest of the engine when building JavaScript values, which
will cause the types of some returned JavaScript objects to change. For
instance, string lists are now returned as sequence objects, not array
objects, and QChar now constructs a JavaScript string.
Change-Id: I0290eb7c9c46e7b508d497cc33cd61d9378f3872
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2019-02-12 13:37:23 +00:00
|
|
|
// unwrap it: this is tested in QJSEngine, and makes the most sense for
|
|
|
|
// end-user code too.
|
2021-03-11 10:07:58 +00:00
|
|
|
return fromVariant(*reinterpret_cast<const QVariant*>(data));
|
2021-04-20 08:43:11 +00:00
|
|
|
} else if (type == QMetaType::fromType<QUrl>()) {
|
|
|
|
// Create a proper URL object here, rather than a variant.
|
|
|
|
return newUrlObject(*reinterpret_cast<const QUrl *>(data))->asReturnedValue();
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
2021-03-11 10:07:58 +00:00
|
|
|
|
|
|
|
return fromData(type, data);
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
|
2019-06-27 11:43:04 +00:00
|
|
|
int ExecutionEngine::maxJSStackSize() const
|
|
|
|
{
|
2022-03-04 10:46:58 +00:00
|
|
|
return s_maxJSStackSize;
|
2019-06-27 11:43:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int ExecutionEngine::maxGCStackSize() const
|
|
|
|
{
|
2022-03-04 10:46:58 +00:00
|
|
|
return s_maxGCStackSize;
|
2019-06-27 11:43:04 +00:00
|
|
|
}
|
|
|
|
|
2017-12-19 10:14:27 +00:00
|
|
|
ReturnedValue ExecutionEngine::global()
|
|
|
|
{
|
|
|
|
return globalObject->asReturnedValue();
|
|
|
|
}
|
|
|
|
|
2019-05-07 10:47:33 +00:00
|
|
|
QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compileModule(const QUrl &url)
|
2018-07-10 12:52:34 +00:00
|
|
|
{
|
2019-08-26 09:48:48 +00:00
|
|
|
QQmlMetaType::CachedUnitLookupError cacheError = QQmlMetaType::CachedUnitLookupError::NoError;
|
2020-12-09 11:04:52 +00:00
|
|
|
if (const QQmlPrivate::CachedQmlUnit *cachedUnit = diskCacheEnabled()
|
|
|
|
? QQmlMetaType::findCachedCompilationUnit(url, &cacheError)
|
|
|
|
: nullptr) {
|
2019-09-12 06:01:21 +00:00
|
|
|
return ExecutableCompilationUnit::create(
|
2020-12-09 11:04:52 +00:00
|
|
|
QV4::CompiledData::CompilationUnit(
|
|
|
|
cachedUnit->qmlData, cachedUnit->aotCompiledFunctions,
|
|
|
|
url.fileName(), url.toString()));
|
2019-08-26 09:48:48 +00:00
|
|
|
}
|
|
|
|
|
2018-07-10 12:52:34 +00:00
|
|
|
QFile f(QQmlFile::urlToLocalFileOrQrc(url));
|
2018-08-13 12:16:21 +00:00
|
|
|
if (!f.open(QIODevice::ReadOnly)) {
|
|
|
|
throwError(QStringLiteral("Could not open module %1 for reading").arg(url.toString()));
|
2018-07-10 12:52:34 +00:00
|
|
|
return nullptr;
|
2018-08-13 12:16:21 +00:00
|
|
|
}
|
2018-07-10 12:52:34 +00:00
|
|
|
|
2018-08-16 09:45:27 +00:00
|
|
|
const QDateTime timeStamp = QFileInfo(f).lastModified();
|
|
|
|
|
2018-07-10 12:52:34 +00:00
|
|
|
const QString sourceCode = QString::fromUtf8(f.readAll());
|
|
|
|
f.close();
|
|
|
|
|
2018-08-16 09:45:27 +00:00
|
|
|
return compileModule(url, sourceCode, timeStamp);
|
2018-07-10 12:52:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-05-07 10:47:33 +00:00
|
|
|
QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compileModule(
|
|
|
|
const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp)
|
2018-07-10 12:52:34 +00:00
|
|
|
{
|
|
|
|
QList<QQmlJS::DiagnosticMessage> diagnostics;
|
2019-05-08 11:29:50 +00:00
|
|
|
auto unit = Compiler::Codegen::compileModule(/*debugMode*/debugger() != nullptr, url.toString(),
|
|
|
|
sourceCode, sourceTimeStamp, &diagnostics);
|
2018-07-10 12:52:34 +00:00
|
|
|
for (const QQmlJS::DiagnosticMessage &m : diagnostics) {
|
|
|
|
if (m.isError()) {
|
2020-02-27 09:49:14 +00:00
|
|
|
throwSyntaxError(m.message, url.toString(), m.loc.startLine, m.loc.startColumn);
|
2018-07-10 12:52:34 +00:00
|
|
|
return nullptr;
|
|
|
|
} else {
|
2020-02-27 09:49:14 +00:00
|
|
|
qWarning() << url << ':' << m.loc.startLine << ':' << m.loc.startColumn
|
2018-07-10 12:52:34 +00:00
|
|
|
<< ": warning: " << m.message;
|
|
|
|
}
|
|
|
|
}
|
2019-05-07 10:47:33 +00:00
|
|
|
|
|
|
|
return ExecutableCompilationUnit::create(std::move(unit));
|
2018-07-10 12:52:34 +00:00
|
|
|
}
|
|
|
|
|
2019-05-07 10:47:33 +00:00
|
|
|
void ExecutionEngine::injectModule(const QQmlRefPointer<ExecutableCompilationUnit> &moduleUnit)
|
2018-07-10 12:52:34 +00:00
|
|
|
{
|
2018-08-16 08:57:25 +00:00
|
|
|
// 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);
|
2018-07-10 12:52:34 +00:00
|
|
|
modules.insert(moduleUnit->finalUrl(), moduleUnit);
|
|
|
|
}
|
|
|
|
|
2019-05-07 10:47:33 +00:00
|
|
|
QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::moduleForUrl(const QUrl &_url, const ExecutableCompilationUnit *referrer) const
|
2018-08-16 08:57:25 +00:00
|
|
|
{
|
|
|
|
QUrl url = QQmlTypeLoader::normalize(_url);
|
|
|
|
if (referrer)
|
|
|
|
url = referrer->finalUrl().resolved(url);
|
|
|
|
|
|
|
|
QMutexLocker moduleGuard(&moduleMutex);
|
|
|
|
auto existingModule = modules.find(url);
|
|
|
|
if (existingModule == modules.end())
|
|
|
|
return nullptr;
|
|
|
|
return *existingModule;
|
|
|
|
}
|
|
|
|
|
2019-05-07 10:47:33 +00:00
|
|
|
QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::loadModule(const QUrl &_url, const ExecutableCompilationUnit *referrer)
|
2018-07-10 12:52:34 +00:00
|
|
|
{
|
|
|
|
QUrl url = QQmlTypeLoader::normalize(_url);
|
|
|
|
if (referrer)
|
|
|
|
url = referrer->finalUrl().resolved(url);
|
|
|
|
|
2018-08-16 08:57:25 +00:00
|
|
|
QMutexLocker moduleGuard(&moduleMutex);
|
2018-07-10 12:52:34 +00:00
|
|
|
auto existingModule = modules.find(url);
|
|
|
|
if (existingModule != modules.end())
|
|
|
|
return *existingModule;
|
|
|
|
|
2018-08-16 08:57:25 +00:00
|
|
|
moduleGuard.unlock();
|
|
|
|
|
2018-07-10 12:52:34 +00:00
|
|
|
auto newModule = compileModule(url);
|
2018-08-16 08:57:25 +00:00
|
|
|
if (newModule) {
|
|
|
|
moduleGuard.relock();
|
2018-07-10 12:52:34 +00:00
|
|
|
modules.insert(url, newModule);
|
2018-08-16 08:57:25 +00:00
|
|
|
}
|
2018-07-10 12:52:34 +00:00
|
|
|
|
|
|
|
return newModule;
|
|
|
|
}
|
|
|
|
|
2021-04-25 21:57:37 +00:00
|
|
|
void ExecutionEngine::registerModule(const QString &_name, const QJSValue &module)
|
|
|
|
{
|
|
|
|
const QUrl url(_name);
|
|
|
|
QMutexLocker moduleGuard(&moduleMutex);
|
|
|
|
const auto existingModule = nativeModules.find(url);
|
|
|
|
if (existingModule != nativeModules.end())
|
|
|
|
return;
|
|
|
|
|
|
|
|
QV4::Value* val = this->memoryManager->m_persistentValues->allocate();
|
|
|
|
*val = QJSValuePrivate::asReturnedValue(&module);
|
|
|
|
nativeModules.insert(url, val);
|
|
|
|
}
|
|
|
|
|
2020-12-09 11:04:52 +00:00
|
|
|
bool ExecutionEngine::diskCacheEnabled() const
|
|
|
|
{
|
|
|
|
return (!disableDiskCache() && !debugger()) || forceDiskCache();
|
|
|
|
}
|
|
|
|
|
2022-04-08 13:49:24 +00:00
|
|
|
void ExecutionEngine::callInContext(QV4::Function *function, QObject *self,
|
|
|
|
QV4::ExecutionContext *context, int argc, void **args,
|
2021-05-03 09:04:39 +00:00
|
|
|
QMetaType *types)
|
2021-02-08 14:42:59 +00:00
|
|
|
{
|
2021-05-03 09:04:39 +00:00
|
|
|
if (!args) {
|
|
|
|
Q_ASSERT(argc == 0);
|
|
|
|
void *dummyArgs[] = { nullptr };
|
|
|
|
QMetaType dummyTypes[] = { QMetaType::fromType<void>() };
|
2022-04-08 13:49:24 +00:00
|
|
|
function->call(self, dummyArgs, dummyTypes, argc, context);
|
2021-05-03 09:04:39 +00:00
|
|
|
return;
|
|
|
|
}
|
2022-04-08 13:49:24 +00:00
|
|
|
Q_ASSERT(types); // both args and types must be present
|
2021-05-03 09:04:39 +00:00
|
|
|
// implicitly sets the return value, which is args[0]
|
2022-04-08 13:49:24 +00:00
|
|
|
function->call(self, args, types, argc, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2021-02-08 14:42:59 +00:00
|
|
|
}
|
|
|
|
|
2019-04-05 07:59:10 +00:00
|
|
|
void ExecutionEngine::initQmlGlobalObject()
|
|
|
|
{
|
|
|
|
initializeGlobal();
|
|
|
|
freezeObject(*globalObject);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ExecutionEngine::initializeGlobal()
|
|
|
|
{
|
QML: Rewrite Qt object in actual C++
Quite obviously, the Qt object is a singleton, extended with a
namespace, backed by a member of the JavaScript global object.
Defining all the methods as JavaScript functions is unnecessary and
duplicates the general type transformation code. Also, it makes it
hard to use those same methods from a C++ context as we cannot
properly set up the arguments outside the JS engine.
Rewriting the Qt object reveals some deficiencies in the old
implementation that we need to fix now:
1. The enums of the Qt type were listed as properties of the Qt object,
which means you could iterate them with a for..in loop in in JavaScript.
This is just wrong. Enums are not properties. This functionality
is deleted and the test adapted to check for each enum value separately.
The commit message for the change that introduced the iterability
already mentioned that the author had failed to find any occurrence of
this in the real world.
2. Parsing time objects from strings was done by parsing the string as a
date/time and then picking the time from that. We still support that for
now, but output a (categorized) warning. Parsing the time directly is
preferred where possible.
3. Previously you could create (invalid) dates and times from various
kinds of QML types, like int and color. This does not work anymore as we
now validate the types before calling the functions.
4. Passing more arguments to a function than the function accepted was
unconditionally ignored before. Now, a Q_CLASSINFO on the surrounding
class can specify that the arguments should be checked, in which case a
JavaScript error is thrown if too many arguments are passed. In order
for this to work correctly we also have to ignore JS undefined values as
trailing arguments for overload resolution. This way, if a method
matching the defined arguments exists, it will be preferred over a
method that matches the full argument count, but possibly cannot accept
undefined as parameter.
Consequently a number of error messages change, which is reflected in
the qqmlqt test.
[ChangeLog][QtQMl][Important Behavior Changes] You can not iterate the
enumerations of the Qt object in JavaScript anymore. This does not work
with any other enumeration type either. You can of course still access
them by name, for example as Qt.LeftButton or similar.
[ChangeLog][QtQMl][Important Behavior Changes] The time formatting
functions of the Qt object in QML now allow you to pass an actual time
string, rather than a date/time string as argument. Passing a date/time
string results in a warning now.
[ChangeLog][QtQml][Important Behavior Changes] Functions in the Qt
object for formatting date and time will now throw a JavaScript error
when presented with a value of an incompatible type, such as int or
color.
[ChangeLog][QtQml][Important Behavior Changes] The Qt.resolvedUrl()
function now returns a URL rather than a string. This follows the
documentation.
[ChangeLog][QtQml][Important Behavior Changes] The GlobalColor enum of
the Qt namespace is not exposed to QML anymore. It did not make any
sense before as the enum values could not be used as colors.
Change-Id: I7fc2f24377eb2fde8f63a1ffac5548d652de7b12
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2020-10-29 18:34:24 +00:00
|
|
|
createQtObject();
|
2019-04-05 07:59:10 +00:00
|
|
|
|
2020-01-22 12:37:48 +00:00
|
|
|
QV4::GlobalExtensions::init(globalObject, QJSEngine::AllExtensions);
|
|
|
|
|
2019-04-05 07:59:10 +00:00
|
|
|
#if QT_CONFIG(qml_locale)
|
|
|
|
QQmlLocale::registerStringLocaleCompare(this);
|
|
|
|
QQmlDateExtension::registerExtension(this);
|
|
|
|
QQmlNumberExtension::registerExtension(this);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if QT_CONFIG(qml_xml_http_request)
|
|
|
|
qt_add_domexceptions(this);
|
|
|
|
m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
qt_add_sqlexceptions(this);
|
|
|
|
|
|
|
|
{
|
|
|
|
for (uint i = 0; i < globalObject->internalClass()->size; ++i) {
|
|
|
|
if (globalObject->internalClass()->nameMap.at(i).isString()) {
|
|
|
|
QV4::PropertyKey id = globalObject->internalClass()->nameMap.at(i);
|
|
|
|
m_illegalNames.insert(id.toQString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
QML: Rewrite Qt object in actual C++
Quite obviously, the Qt object is a singleton, extended with a
namespace, backed by a member of the JavaScript global object.
Defining all the methods as JavaScript functions is unnecessary and
duplicates the general type transformation code. Also, it makes it
hard to use those same methods from a C++ context as we cannot
properly set up the arguments outside the JS engine.
Rewriting the Qt object reveals some deficiencies in the old
implementation that we need to fix now:
1. The enums of the Qt type were listed as properties of the Qt object,
which means you could iterate them with a for..in loop in in JavaScript.
This is just wrong. Enums are not properties. This functionality
is deleted and the test adapted to check for each enum value separately.
The commit message for the change that introduced the iterability
already mentioned that the author had failed to find any occurrence of
this in the real world.
2. Parsing time objects from strings was done by parsing the string as a
date/time and then picking the time from that. We still support that for
now, but output a (categorized) warning. Parsing the time directly is
preferred where possible.
3. Previously you could create (invalid) dates and times from various
kinds of QML types, like int and color. This does not work anymore as we
now validate the types before calling the functions.
4. Passing more arguments to a function than the function accepted was
unconditionally ignored before. Now, a Q_CLASSINFO on the surrounding
class can specify that the arguments should be checked, in which case a
JavaScript error is thrown if too many arguments are passed. In order
for this to work correctly we also have to ignore JS undefined values as
trailing arguments for overload resolution. This way, if a method
matching the defined arguments exists, it will be preferred over a
method that matches the full argument count, but possibly cannot accept
undefined as parameter.
Consequently a number of error messages change, which is reflected in
the qqmlqt test.
[ChangeLog][QtQMl][Important Behavior Changes] You can not iterate the
enumerations of the Qt object in JavaScript anymore. This does not work
with any other enumeration type either. You can of course still access
them by name, for example as Qt.LeftButton or similar.
[ChangeLog][QtQMl][Important Behavior Changes] The time formatting
functions of the Qt object in QML now allow you to pass an actual time
string, rather than a date/time string as argument. Passing a date/time
string results in a warning now.
[ChangeLog][QtQml][Important Behavior Changes] Functions in the Qt
object for formatting date and time will now throw a JavaScript error
when presented with a value of an incompatible type, such as int or
color.
[ChangeLog][QtQml][Important Behavior Changes] The Qt.resolvedUrl()
function now returns a URL rather than a string. This follows the
documentation.
[ChangeLog][QtQml][Important Behavior Changes] The GlobalColor enum of
the Qt namespace is not exposed to QML anymore. It did not make any
sense before as the enum values could not be used as colors.
Change-Id: I7fc2f24377eb2fde8f63a1ffac5548d652de7b12
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2020-10-29 18:34:24 +00:00
|
|
|
void ExecutionEngine::createQtObject()
|
|
|
|
{
|
|
|
|
QV4::Scope scope(this);
|
|
|
|
QtObject *qtObject = new QtObject(this);
|
|
|
|
QJSEngine::setObjectOwnership(qtObject, QJSEngine::JavaScriptOwnership);
|
2022-06-24 07:56:01 +00:00
|
|
|
|
|
|
|
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);
|
QML: Rewrite Qt object in actual C++
Quite obviously, the Qt object is a singleton, extended with a
namespace, backed by a member of the JavaScript global object.
Defining all the methods as JavaScript functions is unnecessary and
duplicates the general type transformation code. Also, it makes it
hard to use those same methods from a C++ context as we cannot
properly set up the arguments outside the JS engine.
Rewriting the Qt object reveals some deficiencies in the old
implementation that we need to fix now:
1. The enums of the Qt type were listed as properties of the Qt object,
which means you could iterate them with a for..in loop in in JavaScript.
This is just wrong. Enums are not properties. This functionality
is deleted and the test adapted to check for each enum value separately.
The commit message for the change that introduced the iterability
already mentioned that the author had failed to find any occurrence of
this in the real world.
2. Parsing time objects from strings was done by parsing the string as a
date/time and then picking the time from that. We still support that for
now, but output a (categorized) warning. Parsing the time directly is
preferred where possible.
3. Previously you could create (invalid) dates and times from various
kinds of QML types, like int and color. This does not work anymore as we
now validate the types before calling the functions.
4. Passing more arguments to a function than the function accepted was
unconditionally ignored before. Now, a Q_CLASSINFO on the surrounding
class can specify that the arguments should be checked, in which case a
JavaScript error is thrown if too many arguments are passed. In order
for this to work correctly we also have to ignore JS undefined values as
trailing arguments for overload resolution. This way, if a method
matching the defined arguments exists, it will be preferred over a
method that matches the full argument count, but possibly cannot accept
undefined as parameter.
Consequently a number of error messages change, which is reflected in
the qqmlqt test.
[ChangeLog][QtQMl][Important Behavior Changes] You can not iterate the
enumerations of the Qt object in JavaScript anymore. This does not work
with any other enumeration type either. You can of course still access
them by name, for example as Qt.LeftButton or similar.
[ChangeLog][QtQMl][Important Behavior Changes] The time formatting
functions of the Qt object in QML now allow you to pass an actual time
string, rather than a date/time string as argument. Passing a date/time
string results in a warning now.
[ChangeLog][QtQml][Important Behavior Changes] Functions in the Qt
object for formatting date and time will now throw a JavaScript error
when presented with a value of an incompatible type, such as int or
color.
[ChangeLog][QtQml][Important Behavior Changes] The Qt.resolvedUrl()
function now returns a URL rather than a string. This follows the
documentation.
[ChangeLog][QtQml][Important Behavior Changes] The GlobalColor enum of
the Qt namespace is not exposed to QML anymore. It did not make any
sense before as the enum values could not be used as colors.
Change-Id: I7fc2f24377eb2fde8f63a1ffac5548d652de7b12
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2020-10-29 18:34:24 +00:00
|
|
|
}
|
|
|
|
|
2019-04-05 07:59:10 +00:00
|
|
|
const QSet<QString> &ExecutionEngine::illegalNames() const
|
|
|
|
{
|
|
|
|
return m_illegalNames;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ExecutionEngine::setQmlEngine(QQmlEngine *engine)
|
|
|
|
{
|
|
|
|
m_qmlEngine = engine;
|
|
|
|
initQmlGlobalObject();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object)
|
|
|
|
{
|
2022-07-25 11:50:10 +00:00
|
|
|
if (object->as<QV4::QObjectWrapper>() || object->internalClass()->isFrozen())
|
2019-04-05 07:59:10 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
QV4::Scope scope(v4);
|
|
|
|
|
|
|
|
bool instanceOfObject = false;
|
|
|
|
QV4::ScopedObject p(scope, object->getPrototypeOf());
|
|
|
|
while (p) {
|
|
|
|
if (p->d() == v4->objectPrototype()->d()) {
|
|
|
|
instanceOfObject = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
p = p->getPrototypeOf();
|
|
|
|
}
|
|
|
|
if (!instanceOfObject)
|
|
|
|
return;
|
|
|
|
|
Clean up frozen(), sealed(), nonExtensible() and propertiesFrozen()
They all had some interesting bugs and duplicated each other:
a, propertiesFrozen() changed each property individually, creating a lot
of unnecessary intermediate classes. frozen() changed them all at once.
b, If a class happened to contain only properties that matched the
characteristics of being "sealed" or "frozen", sealed(), frozen() and
propertiesFrozen() would set the flags in place and return the same
class. This is bad because it violates the assumption that an
InternalClass is immutable and it breaks the recursive freezing
algorithm we rely on for the global object. It would stop freezing child
objects at any such class, even if the children were not frozen.
c, propertiesFrozen() did not set any of the flags even though it
effectively sealed and froze the class. Therefore, when requesting the
same class as frozen() it would iterate through all the properties
again.
d, frozen() implicitly also sealed the object and made it
non-extensible. sealed() also implicitly made it non-extensible. This is
impractical as we want to allow objects to be extensible even though all
their properties are frozen. Therefore we only set the flag that belongs
to each method now. We do know, however, that a frozen object is
implicitly sealed. Therefore we can short-circuit this transition.
Furthermore, we need to remove the assert in InternalClass::init() as
you can indeed use frozen objects as prototypes for others, but that
needs to be recorded in the original InternalClass via the isUsedAsProto
flag. In order to set this flag, we need to perform a transition and
therefore, derive from the old InternalClass.
The JavaScript isFrozen() method asks for an _implicitly_, "duck typed",
frozen state, which is different from what our "isFrozen" flag denotes.
Therefore we add a separate const method that just checks whether all
properties are frozen.
Task-number: QTBUG-76033
Change-Id: I375fef83fb99035d470490fdf2348766b090831e
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
2019-05-28 13:13:05 +00:00
|
|
|
Heap::InternalClass *frozen = object->internalClass()->frozen();
|
|
|
|
object->setInternalClass(frozen); // Immediately assign frozen to prevent it from getting GC'd
|
2019-04-05 07:59:10 +00:00
|
|
|
|
|
|
|
QV4::ScopedObject o(scope);
|
|
|
|
for (uint i = 0; i < frozen->size; ++i) {
|
|
|
|
if (!frozen->nameMap.at(i).isStringOrSymbol())
|
|
|
|
continue;
|
|
|
|
o = *object->propertyData(i);
|
|
|
|
if (o)
|
|
|
|
freeze_recursive(v4, o);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ExecutionEngine::freezeObject(const QV4::Value &value)
|
|
|
|
{
|
|
|
|
QV4::Scope scope(this);
|
|
|
|
QV4::ScopedObject o(scope, value);
|
|
|
|
freeze_recursive(this, o);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ExecutionEngine::startTimer(const QString &timerName)
|
|
|
|
{
|
|
|
|
if (!m_time.isValid())
|
|
|
|
m_time.start();
|
|
|
|
m_startedTimers[timerName] = m_time.elapsed();
|
|
|
|
}
|
|
|
|
|
|
|
|
qint64 ExecutionEngine::stopTimer(const QString &timerName, bool *wasRunning)
|
|
|
|
{
|
|
|
|
if (!m_startedTimers.contains(timerName)) {
|
|
|
|
*wasRunning = false;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
*wasRunning = true;
|
|
|
|
qint64 startedAt = m_startedTimers.take(timerName);
|
|
|
|
return m_time.elapsed() - startedAt;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ExecutionEngine::consoleCountHelper(const QString &file, quint16 line, quint16 column)
|
|
|
|
{
|
|
|
|
const QString key = file + QString::number(line) + QString::number(column);
|
|
|
|
int number = m_consoleCount.value(key, 0);
|
|
|
|
number++;
|
|
|
|
m_consoleCount.insert(key, number);
|
|
|
|
return number;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ExecutionEngine::setExtensionData(int index, Deletable *data)
|
|
|
|
{
|
|
|
|
if (m_extensionData.count() <= index)
|
|
|
|
m_extensionData.resize(index + 1);
|
|
|
|
|
|
|
|
if (m_extensionData.at(index))
|
|
|
|
delete m_extensionData.at(index);
|
|
|
|
|
|
|
|
m_extensionData[index] = data;
|
|
|
|
}
|
|
|
|
|
2015-01-02 13:37:26 +00:00
|
|
|
// 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.
|
2021-03-22 13:11:17 +00:00
|
|
|
bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, void *data)
|
2015-01-02 13:37:26 +00:00
|
|
|
{
|
|
|
|
// check if it's one of the types we know
|
2021-03-22 13:11:17 +00:00
|
|
|
switch (metaType.id()) {
|
2015-01-02 13:37:26 +00:00
|
|
|
case QMetaType::Bool:
|
2020-03-04 15:46:42 +00:00
|
|
|
*reinterpret_cast<bool*>(data) = value.toBoolean();
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
case QMetaType::Int:
|
2020-03-04 15:46:42 +00:00
|
|
|
*reinterpret_cast<int*>(data) = value.toInt32();
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
case QMetaType::UInt:
|
2020-03-04 15:46:42 +00:00
|
|
|
*reinterpret_cast<uint*>(data) = value.toUInt32();
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
case QMetaType::LongLong:
|
2020-03-04 15:46:42 +00:00
|
|
|
*reinterpret_cast<qlonglong*>(data) = qlonglong(value.toInteger());
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
case QMetaType::ULongLong:
|
2020-03-04 15:46:42 +00:00
|
|
|
*reinterpret_cast<qulonglong*>(data) = qulonglong(value.toInteger());
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
case QMetaType::Double:
|
2020-03-04 15:46:42 +00:00
|
|
|
*reinterpret_cast<double*>(data) = value.toNumber();
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
case QMetaType::QString:
|
2020-03-04 15:46:42 +00:00
|
|
|
if (value.isUndefined() || value.isNull())
|
2015-01-02 13:37:26 +00:00
|
|
|
*reinterpret_cast<QString*>(data) = QString();
|
|
|
|
else
|
2020-03-04 15:46:42 +00:00
|
|
|
*reinterpret_cast<QString*>(data) = value.toQString();
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
2015-12-28 12:22:29 +00:00
|
|
|
case QMetaType::QByteArray:
|
2020-03-04 15:46:42 +00:00
|
|
|
if (const ArrayBuffer *ab = value.as<ArrayBuffer>())
|
2015-12-28 12:22:29 +00:00
|
|
|
*reinterpret_cast<QByteArray*>(data) = ab->asByteArray();
|
2022-07-25 14:14:49 +00:00
|
|
|
else if (const String *string = value.as<String>())
|
|
|
|
*reinterpret_cast<QByteArray*>(data) = string->toQString().toUtf8();
|
2015-12-28 12:22:29 +00:00
|
|
|
else
|
|
|
|
*reinterpret_cast<QByteArray*>(data) = QByteArray();
|
|
|
|
return true;
|
2015-01-02 13:37:26 +00:00
|
|
|
case QMetaType::Float:
|
2020-03-04 15:46:42 +00:00
|
|
|
*reinterpret_cast<float*>(data) = value.toNumber();
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
case QMetaType::Short:
|
2020-03-04 15:46:42 +00:00
|
|
|
*reinterpret_cast<short*>(data) = short(value.toInt32());
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
case QMetaType::UShort:
|
2020-03-04 15:46:42 +00:00
|
|
|
*reinterpret_cast<unsigned short*>(data) = value.toUInt16();
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
case QMetaType::Char:
|
2020-03-04 15:46:42 +00:00
|
|
|
*reinterpret_cast<char*>(data) = char(value.toInt32());
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
case QMetaType::UChar:
|
2020-03-04 15:46:42 +00:00
|
|
|
*reinterpret_cast<unsigned char*>(data) = (unsigned char)(value.toInt32());
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
case QMetaType::QChar:
|
2020-03-04 15:46:42 +00:00
|
|
|
if (String *s = value.stringValue()) {
|
2016-11-24 15:05:14 +00:00
|
|
|
QString str = s->toQString();
|
2015-01-02 13:37:26 +00:00
|
|
|
*reinterpret_cast<QChar*>(data) = str.isEmpty() ? QChar() : str.at(0);
|
|
|
|
} else {
|
2020-03-04 15:46:42 +00:00
|
|
|
*reinterpret_cast<QChar*>(data) = QChar(ushort(value.toUInt16()));
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
case QMetaType::QDateTime:
|
2020-03-04 15:46:42 +00:00
|
|
|
if (const QV4::DateObject *d = value.as<DateObject>()) {
|
2015-01-02 13:37:26 +00:00
|
|
|
*reinterpret_cast<QDateTime *>(data) = d->toQDateTime();
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case QMetaType::QDate:
|
2020-03-04 15:46:42 +00:00
|
|
|
if (const QV4::DateObject *d = value.as<DateObject>()) {
|
Make QDate handling consistent in its use of UTC
Due to the quirk of ECMAScript's Date.parse() spec [0] stipulating the
use of UTC for date-only strings, in contrast to most other ways of
creating a Date using local time, reasonable users get surprised by
the behavior of QDate properties initialized from strings. This can't
be avoided without breaking other uses of Date, so document the
work-around needed to cope with it (use UTC-specific methods to access
the Date object).
[0] https://tc39.es/ecma262/#sec-date.parse
Make conversions back to QDate from Date work round the possibility
that the Date, seen as a QDateTime(,, LocalTime), needs to be handled
as UTC when extracting the date, and catch two more places that
conversion from QDate neglected to use UTC's start of day, for
consistency.
Revised tests to call UTC-specific methods instead of the local-time
ones, where appropriate. Drive-by: some tests were (entirely bogusly)
constructing a fresh Date using the UTC-fields of the Date they had,
in order to then test the non-UTC fields of this fresh Date; instead,
simply test that the UTC fields are as expected.
[ChangeLog][QML][Behavior change] Where a QDate is represented in
QML's JavaScript as a Date, it is now more consistently associated
with the start of the UTC day it describes. Previously cases where it
was represented as the start of local time's day could lead to a Date
turning into a QDate for the preceding day. Inconsistencies in the
specified behavior of Date preclude eliminating such problems
entirely, but they should now be limited to cases where (perversely
for a date property or parameter) the date is specified with a local
time late enough to make it coincide with the start of the next UTC
day (in which case that next day's QDate will be its C++
representation).
Fixes: QTBUG-92466
Change-Id: I2306dd9ecef0d5c2d59b562762392e51bb6d66ca
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2021-05-12 09:05:16 +00:00
|
|
|
// 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.
|
|
|
|
QDateTime dt = d->toQDateTime();
|
|
|
|
const QDateTime utc = dt.toUTC();
|
|
|
|
if (utc.date() != dt.date() && utc.addMSecs(-1).date() == dt.date())
|
|
|
|
dt = utc;
|
|
|
|
// 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.
|
|
|
|
*reinterpret_cast<QDate *>(data) = dt.date();
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
} break;
|
2020-12-02 11:56:51 +00:00
|
|
|
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;
|
2021-04-20 08:43:11 +00:00
|
|
|
} 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;
|
2019-02-13 14:54:14 +00:00
|
|
|
#if QT_CONFIG(regularexpression)
|
|
|
|
case QMetaType::QRegularExpression:
|
2020-03-04 15:46:42 +00:00
|
|
|
if (const QV4::RegExpObject *r = value.as<QV4::RegExpObject>()) {
|
2019-02-13 14:54:14 +00:00
|
|
|
*reinterpret_cast<QRegularExpression *>(data) = r->toQRegularExpression();
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
#endif
|
2015-01-02 13:37:26 +00:00
|
|
|
case QMetaType::QObjectStar: {
|
2020-03-04 15:46:42 +00:00
|
|
|
if (value.isNull()) {
|
|
|
|
*reinterpret_cast<QObject* *>(data) = nullptr;
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
2020-03-04 15:46:42 +00:00
|
|
|
}
|
|
|
|
if (value.as<QV4::QObjectWrapper>()) {
|
|
|
|
*reinterpret_cast<QObject* *>(data) = qtObjectFromJS(value);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
case QMetaType::QStringList: {
|
2020-03-04 15:46:42 +00:00
|
|
|
const QV4::ArrayObject *a = value.as<QV4::ArrayObject>();
|
2015-01-02 13:37:26 +00:00
|
|
|
if (a) {
|
|
|
|
*reinterpret_cast<QStringList *>(data) = a->toQStringList();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QMetaType::QVariantList: {
|
2020-03-04 15:46:42 +00:00
|
|
|
const QV4::ArrayObject *a = value.as<QV4::ArrayObject>();
|
2015-01-02 13:37:26 +00:00
|
|
|
if (a) {
|
2020-03-04 15:46:42 +00:00
|
|
|
*reinterpret_cast<QVariantList *>(data) = a->engine()->toVariant(
|
2021-03-24 14:36:37 +00:00
|
|
|
*a, /*typeHint*/QMetaType{}, /*createJSValueForObjects*/false).toList();
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QMetaType::QVariantMap: {
|
2020-03-04 15:46:42 +00:00
|
|
|
const QV4::Object *o = value.as<QV4::Object>();
|
2015-01-02 13:37:26 +00:00
|
|
|
if (o) {
|
2020-03-04 15:46:42 +00:00
|
|
|
*reinterpret_cast<QVariantMap *>(data) = o->engine()->variantMapFromJS(o);
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QMetaType::QVariant:
|
2020-03-04 15:46:42 +00:00
|
|
|
if (const QV4::Managed *m = value.as<QV4::Managed>())
|
2021-03-24 14:36:37 +00:00
|
|
|
*reinterpret_cast<QVariant*>(data) = m->engine()->toVariant(value, /*typeHint*/QMetaType{}, /*createJSValueForObjects*/false);
|
2020-03-04 15:46:42 +00:00
|
|
|
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());
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
case QMetaType::QJsonValue:
|
2020-03-04 15:46:42 +00:00
|
|
|
*reinterpret_cast<QJsonValue *>(data) = QV4::JsonObject::toJsonValue(value);
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
case QMetaType::QJsonObject: {
|
2020-03-04 15:46:42 +00:00
|
|
|
*reinterpret_cast<QJsonObject *>(data) = QV4::JsonObject::toJsonObject(value.as<Object>());
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case QMetaType::QJsonArray: {
|
2020-03-04 15:46:42 +00:00
|
|
|
const QV4::ArrayObject *a = value.as<ArrayObject>();
|
2015-01-02 13:37:26 +00:00
|
|
|
if (a) {
|
2015-04-26 20:58:49 +00:00
|
|
|
*reinterpret_cast<QJsonArray *>(data) = JsonObject::toJsonArray(a);
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
2022-05-17 13:50:06 +00:00
|
|
|
if (metaType.flags() & QMetaType::IsEnumeration) {
|
|
|
|
*reinterpret_cast<int *>(data) = value.toInt32();
|
|
|
|
return true;
|
|
|
|
}
|
2021-03-17 14:08:22 +00:00
|
|
|
|
2022-05-17 13:50:06 +00:00
|
|
|
if (metaType == QMetaType::fromType<QQmlListReference>()) {
|
|
|
|
if (const QV4::QmlListWrapper *wrapper = value.as<QV4::QmlListWrapper>()) {
|
|
|
|
*reinterpret_cast<QQmlListReference *>(data) = wrapper->toListReference();
|
|
|
|
return true;
|
2021-03-17 14:08:22 +00:00
|
|
|
}
|
2021-02-18 14:46:24 +00:00
|
|
|
}
|
|
|
|
|
2022-05-17 13:50:06 +00:00
|
|
|
if (const QQmlValueTypeWrapper *vtw = value.as<QQmlValueTypeWrapper>()) {
|
|
|
|
const QMetaType valueType = vtw->type();
|
|
|
|
if (valueType == metaType)
|
|
|
|
return vtw->toGadget(data);
|
|
|
|
if (QMetaType::canConvert(valueType, metaType))
|
|
|
|
return QMetaType::convert(valueType, vtw->d()->gadgetPtr(), metaType, data);
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
|
2015-02-13 09:02:28 +00:00
|
|
|
// Try to use magic; for compatibility with qjsvalue_cast.
|
2015-01-02 13:37:26 +00:00
|
|
|
|
2021-03-22 12:27:05 +00:00
|
|
|
if (convertToNativeQObject(value, metaType, reinterpret_cast<void **>(data)))
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
2021-03-22 12:27:05 +00:00
|
|
|
|
|
|
|
const bool isPointer = (metaType.flags() & QMetaType::IsPointer);
|
|
|
|
if (value.as<QV4::VariantObject>() && isPointer) {
|
|
|
|
const QByteArray pointedToTypeName = QByteArray(metaType.name()).chopped(1);
|
|
|
|
const QMetaType valueType = QMetaType::fromName(pointedToTypeName);
|
2020-03-04 15:46:42 +00:00
|
|
|
QVariant &var = value.as<QV4::VariantObject>()->d()->data();
|
2021-03-22 12:27:05 +00:00
|
|
|
if (valueType == var.metaType()) {
|
2015-01-02 13:37:26 +00:00
|
|
|
// We have T t, T* is requested, so return &t.
|
|
|
|
*reinterpret_cast<void* *>(data) = var.data();
|
|
|
|
return true;
|
2020-03-04 15:46:42 +00:00
|
|
|
} else if (Object *o = value.objectValue()) {
|
2015-01-02 13:37:26 +00:00
|
|
|
// Look in the prototype chain.
|
2020-03-04 15:46:42 +00:00
|
|
|
QV4::Scope scope(o->engine());
|
2018-06-19 11:28:26 +00:00
|
|
|
QV4::ScopedObject proto(scope, o->getPrototypeOf());
|
2015-01-02 13:37:26 +00:00
|
|
|
while (proto) {
|
|
|
|
bool canCast = false;
|
|
|
|
if (QV4::VariantObject *vo = proto->as<QV4::VariantObject>()) {
|
2016-10-05 10:18:20 +00:00
|
|
|
const QVariant &v = vo->d()->data();
|
2021-03-22 13:11:17 +00:00
|
|
|
canCast = (metaType == v.metaType());
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
else if (proto->as<QV4::QObjectWrapper>()) {
|
|
|
|
QV4::ScopedObject p(scope, proto.getPointer());
|
2021-03-22 12:27:05 +00:00
|
|
|
if (QObject *qobject = qtObjectFromJS(p)) {
|
|
|
|
if (const QMetaObject *metaObject = metaType.metaObject())
|
|
|
|
canCast = metaObject->cast(qobject) != nullptr;
|
|
|
|
else
|
|
|
|
canCast = qobject->qt_metacast(pointedToTypeName);
|
|
|
|
}
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
if (canCast) {
|
2021-03-22 12:27:05 +00:00
|
|
|
const QMetaType varType = var.metaType();
|
|
|
|
if (varType.flags() & QMetaType::IsPointer)
|
2015-01-02 13:37:26 +00:00
|
|
|
*reinterpret_cast<void* *>(data) = *reinterpret_cast<void* *>(var.data());
|
|
|
|
else
|
|
|
|
*reinterpret_cast<void* *>(data) = var.data();
|
|
|
|
return true;
|
|
|
|
}
|
2018-06-19 11:28:26 +00:00
|
|
|
proto = proto->getPrototypeOf();
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-22 12:27:05 +00:00
|
|
|
} else if (value.isNull() && isPointer) {
|
2018-02-21 09:41:54 +00:00
|
|
|
*reinterpret_cast<void* *>(data) = nullptr;
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
2021-03-22 13:11:17 +00:00
|
|
|
} else if (metaType == QMetaType::fromType<QJSValue>()) {
|
2020-03-04 15:46:42 +00:00
|
|
|
QJSValuePrivate::setValue(reinterpret_cast<QJSValue*>(data), value.asReturnedValue());
|
2015-01-02 13:37:26 +00:00
|
|
|
return true;
|
2021-09-03 12:35:26 +00:00
|
|
|
} else if (!isPointer) {
|
|
|
|
QVariant val;
|
|
|
|
if (QQml_valueTypeProvider()->createValueType(
|
|
|
|
metaType, QJSValuePrivate::fromReturnedValue(value.asReturnedValue()), val)) {
|
|
|
|
Q_ASSERT(val.metaType() == metaType);
|
|
|
|
metaType.destruct(data);
|
|
|
|
metaType.construct(data, val.constData());
|
|
|
|
return true;
|
|
|
|
}
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
|
2022-05-17 13:50:06 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-02 13:37:26 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-03-22 12:27:05 +00:00
|
|
|
static bool convertToNativeQObject(const QV4::Value &value, QMetaType targetType, void **result)
|
2015-01-02 13:37:26 +00:00
|
|
|
{
|
2021-03-22 12:27:05 +00:00
|
|
|
if (!(targetType.flags() & QMetaType::IsPointer))
|
2015-01-02 13:37:26 +00:00
|
|
|
return false;
|
2020-03-04 15:46:42 +00:00
|
|
|
if (QObject *qobject = qtObjectFromJS(value)) {
|
2021-03-22 12:27:05 +00:00
|
|
|
// 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);
|
2015-01-02 13:37:26 +00:00
|
|
|
if (void *instance = qobject->qt_metacast(className)) {
|
|
|
|
*result = instance;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-03-04 15:46:42 +00:00
|
|
|
static QObject *qtObjectFromJS(const QV4::Value &value)
|
2015-01-02 13:37:26 +00:00
|
|
|
{
|
2015-01-15 10:36:57 +00:00
|
|
|
if (!value.isObject())
|
2018-02-21 09:41:54 +00:00
|
|
|
return nullptr;
|
2015-01-02 13:37:26 +00:00
|
|
|
|
2020-03-04 15:46:42 +00:00
|
|
|
QV4::Scope scope(value.as<QV4::Managed>()->engine());
|
2015-01-02 13:37:26 +00:00
|
|
|
QV4::Scoped<QV4::VariantObject> v(scope, value);
|
|
|
|
|
|
|
|
if (v) {
|
2016-10-05 10:18:20 +00:00
|
|
|
QVariant variant = v->d()->data();
|
2015-01-02 13:37:26 +00:00
|
|
|
int type = variant.userType();
|
|
|
|
if (type == QMetaType::QObjectStar)
|
|
|
|
return *reinterpret_cast<QObject* const *>(variant.constData());
|
|
|
|
}
|
|
|
|
QV4::Scoped<QV4::QObjectWrapper> wrapper(scope, value);
|
2021-03-17 14:08:22 +00:00
|
|
|
if (wrapper)
|
|
|
|
return wrapper->object();
|
|
|
|
|
|
|
|
QV4::Scoped<QV4::QQmlTypeWrapper> typeWrapper(scope, value);
|
|
|
|
if (typeWrapper)
|
|
|
|
return typeWrapper->object();
|
|
|
|
|
|
|
|
return nullptr;
|
2015-01-02 13:37:26 +00:00
|
|
|
}
|
|
|
|
|
2019-04-05 07:59:10 +00:00
|
|
|
struct QV4EngineRegistrationData
|
|
|
|
{
|
|
|
|
QV4EngineRegistrationData() : extensionCount(0) {}
|
|
|
|
|
|
|
|
QMutex mutex;
|
|
|
|
int extensionCount;
|
|
|
|
};
|
|
|
|
Q_GLOBAL_STATIC(QV4EngineRegistrationData, registrationData);
|
|
|
|
|
|
|
|
QMutex *ExecutionEngine::registrationMutex()
|
|
|
|
{
|
|
|
|
return ®istrationData()->mutex;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ExecutionEngine::registerExtension()
|
|
|
|
{
|
|
|
|
return registrationData()->extensionCount++;
|
|
|
|
}
|
|
|
|
|
2020-02-24 08:41:22 +00:00
|
|
|
#if QT_CONFIG(qml_network)
|
2020-01-03 09:32:01 +00:00
|
|
|
QNetworkAccessManager *QV4::detail::getNetworkAccessManager(ExecutionEngine *engine)
|
|
|
|
{
|
|
|
|
return engine->qmlEngine()->networkAccessManager();
|
|
|
|
}
|
2020-02-24 08:41:22 +00:00
|
|
|
#endif // qml_network
|
2020-01-03 09:32:01 +00:00
|
|
|
|
2013-06-24 13:28:00 +00:00
|
|
|
QT_END_NAMESPACE
|