diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2018-08-24 01:00:36 +0200 |
---|---|---|
committer | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2018-08-24 01:00:36 +0200 |
commit | 444d645706d1f0cf287f8f8fc7aa90eb3482a8ea (patch) | |
tree | 850df67a76283eaf687f1934ef92c2e4b339ed58 | |
parent | b974b6249a2dce1af8b549fc0d0a5c6dd28d6292 (diff) | |
parent | 732c25029ec95feb27a607ef19bb3b7423a955a1 (diff) |
Merge remote-tracking branch 'origin/5.12' into dev
Change-Id: Ieb0dd0116d22f4a35c35b7321a0ed786846b328f
45 files changed, 1886 insertions, 760 deletions
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 624e3c42e7..88accc2f49 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1618,36 +1618,37 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen // No more new strings after this point, we're calculating offsets. output.jsGenerator.stringTable.freeze(); - const int importSize = sizeof(QV4::CompiledData::Import) * output.imports.count(); - const int objectOffsetTableSize = output.objects.count() * sizeof(quint32); + const uint importSize = sizeof(QV4::CompiledData::Import) * output.imports.count(); + const uint objectOffsetTableSize = output.objects.count() * sizeof(quint32); QHash<const Object*, quint32> objectOffsets; - int objectsSize = 0; + const unsigned int objectOffset = sizeof(QV4::CompiledData::QmlUnit) + importSize; + uint nextOffset = objectOffset + objectOffsetTableSize; for (Object *o : qAsConst(output.objects)) { - objectOffsets.insert(o, sizeof(QV4::CompiledData::QmlUnit) + importSize + objectOffsetTableSize + objectsSize); - objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count); + objectOffsets.insert(o, nextOffset); + nextOffset += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count); int signalTableSize = 0; for (const Signal *s = o->firstSignal(); s; s = s->next) signalTableSize += QV4::CompiledData::Signal::calculateSize(s->parameters->count); - objectsSize += signalTableSize; + nextOffset += signalTableSize; int enumTableSize = 0; for (const Enum *e = o->firstEnum(); e; e = e->next) enumTableSize += QV4::CompiledData::Enum::calculateSize(e->enumValues->count); - objectsSize += enumTableSize; + nextOffset += enumTableSize; } - const uint totalSize = sizeof(QV4::CompiledData::QmlUnit) + importSize + objectOffsetTableSize + objectsSize; + const uint totalSize = nextOffset; char *data = (char*)malloc(totalSize); memset(data, 0, totalSize); QV4::CompiledData::QmlUnit *qmlUnit = reinterpret_cast<QV4::CompiledData::QmlUnit *>(data); qmlUnit->offsetToImports = sizeof(*qmlUnit); qmlUnit->nImports = output.imports.count(); - qmlUnit->offsetToObjects = qmlUnit->offsetToImports + importSize; + qmlUnit->offsetToObjects = objectOffset; qmlUnit->nObjects = output.objects.count(); // write imports @@ -1660,9 +1661,9 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen // write objects quint32_le *objectTable = reinterpret_cast<quint32_le*>(data + qmlUnit->offsetToObjects); - char *objectPtr = data + qmlUnit->offsetToObjects + objectOffsetTableSize; for (int i = 0; i < output.objects.count(); ++i) { const Object *o = output.objects.at(i); + char * const objectPtr = data + objectOffsets.value(o); *objectTable++ = objectOffsets.value(o); QV4::CompiledData::Object *objectToWrite = reinterpret_cast<QV4::CompiledData::Object*>(objectPtr); @@ -1776,10 +1777,6 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen for (int i = 0; i < o->namedObjectsInComponent.count; ++i) { *namedObjectInComponentPtr++ = o->namedObjectsInComponent.at(i); } - - objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count); - objectPtr += signalTableSize; - objectPtr += enumTableSize; } qmlUnit = unitFinalizer(qmlUnit, totalSize); @@ -1790,7 +1787,7 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen qDebug() << " " << jsUnit->functionTableSize << "functions"; qDebug() << " " << jsUnit->unitSize << "for JS unit"; qDebug() << " " << importSize << "for imports"; - qDebug() << " " << objectsSize << "for" << qmlUnit->nObjects << "objects"; + qDebug() << " " << nextOffset - objectOffset - objectOffsetTableSize << "for" << qmlUnit->nObjects << "objects"; quint32 totalBindingCount = 0; for (quint32 i = 0; i < qmlUnit->nObjects; ++i) totalBindingCount += qmlUnit->objectAt(i)->nBindings; diff --git a/src/qml/jit/qv4jithelpers.cpp b/src/qml/jit/qv4jithelpers.cpp index 23e3095a85..9e057dd33d 100644 --- a/src/qml/jit/qv4jithelpers.cpp +++ b/src/qml/jit/qv4jithelpers.cpp @@ -42,6 +42,7 @@ #include "qv4function_p.h" #include "qv4value_p.h" #include "qv4object_p.h" +#include "qv4functionobject_p.h" #include "qv4lookup_p.h" #include <QtCore/private/qnumeric_p.h> @@ -71,7 +72,8 @@ ReturnedValue loadGlobalLookup(ExecutionEngine *engine, Function *f, int index) ReturnedValue loadSuperConstructor(ExecutionEngine *engine, const Value *t) { - if (!t->isObject()) { + const FunctionObject *f = t->as<FunctionObject>(); + if (!f || !f->isConstructor()) { engine->throwTypeError(); return Encode::undefined(); } diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp index c52e9bf24e..dc8bce12a0 100644 --- a/src/qml/jsruntime/qv4arraybuffer.cpp +++ b/src/qml/jsruntime/qv4arraybuffer.cpp @@ -74,9 +74,9 @@ ReturnedValue ArrayBufferCtor::virtualCallAsConstructor(const FunctionObject *f, } -ReturnedValue ArrayBufferCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) +ReturnedValue ArrayBufferCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int) { - return virtualCallAsConstructor(f, argv, argc, f); + return f->engine()->throwTypeError(); } ReturnedValue ArrayBufferCtor::method_isView(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -188,18 +188,21 @@ ReturnedValue ArrayBufferPrototype::method_slice(const FunctionObject *b, const double final = (end < 0) ? qMax(a->d()->data->size + end, 0.) : qMin(end, (double)a->d()->data->size); Scope scope(v4); - ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor())); + const FunctionObject *constructor = a->speciesConstructor(scope, scope.engine->arrayBufferCtor()); if (!constructor) return v4->throwTypeError(); double newLen = qMax(final - first, 0.); ScopedValue argument(scope, QV4::Encode(newLen)); QV4::Scoped<ArrayBuffer> newBuffer(scope, constructor->callAsConstructor(argument, 1)); - if (!newBuffer || newBuffer->d()->data->size < (int)newLen) + if (!newBuffer || newBuffer->d()->data->size < (int)newLen || + newBuffer->isDetachedBuffer() || newBuffer->isSharedArrayBuffer() || + newBuffer->sameValue(*a) || + a->isDetachedBuffer()) return v4->throwTypeError(); memcpy(newBuffer->d()->data->data(), a->d()->data->data() + (uint)first, newLen); - return Encode::undefined(); + return newBuffer->asReturnedValue(); } ReturnedValue ArrayBufferPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 450916fbf7..c162a38e9d 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -104,7 +104,7 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor) ScopedString name(scope); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(engine->id_toString(), method_toString, 0); - defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(engine->id_toLocaleString(), method_toLocaleString, 0); defineDefaultProperty(QStringLiteral("concat"), method_concat, 1); name = engine->newIdentifier(QStringLiteral("copyWithin")); unscopables->put(name, Primitive::fromBoolean(true)); @@ -165,23 +165,13 @@ ScopedObject createObjectFromCtorOrArray(Scope &scope, ScopedFunctionObject ctor { ScopedObject a(scope, Primitive::undefinedValue()); - if (ctor) { - // ### the spec says that we should only call constructors if - // IsConstructor(that), but we have no way of knowing if a builtin is a - // constructor. so for the time being, just try call it, and silence any - // exceptions-- this is not ideal, as the spec also says that we should - // return on exception. - // - // this also isn't completely kosher. for instance: + if (ctor && ctor->isConstructor()) { + // this isn't completely kosher. for instance: // Array.from.call(Object, []).constructor == Object // is expected by the tests, but naturally, we get Number. ScopedValue argument(scope, useLen ? QV4::Encode(len) : Primitive::undefinedValue()); a = ctor->callAsConstructor(argument, useLen ? 1 : 0); - if (scope.engine->hasException) - scope.engine->catchException(); // probably not a constructor, then. - } - - if (!a) { + } else { a = scope.engine->newArrayObject(len); } @@ -369,9 +359,36 @@ ReturnedValue ArrayPrototype::method_toString(const FunctionObject *builtin, con return ObjectPrototype::method_toString(builtin, that, argv, argc); } -ReturnedValue ArrayPrototype::method_toLocaleString(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) +ReturnedValue ArrayPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *, int) { - return method_toString(builtin, thisObject, argv, argc); + Scope scope(b); + ScopedObject instance(scope, thisObject); + if (!instance) + return scope.engine->throwTypeError(); + + uint len = instance->getLength(); + const QString separator = QStringLiteral(","); + + QString R; + + ScopedValue v(scope); + ScopedString s(scope); + + for (uint k = 0; k < len; ++k) { + if (k) + R += separator; + + v = instance->get(k); + if (v->isNullOrUndefined()) + continue; + v = Runtime::method_callElement(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0); + s = v->toString(scope.engine); + if (scope.hasException()) + return Encode::undefined(); + + R += s->toQString(); + } + return scope.engine->newString(R)->asReturnedValue(); } ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value *that, const Value *argv, int argc) @@ -432,7 +449,7 @@ ReturnedValue ArrayPrototype::method_copyWithin(const FunctionObject *b, const V double len = instance->getLength(); double target = argv[0].toInteger(); - double start = argv[1].toInteger(); + double start = argc > 1 ? argv[1].toInteger() : 0; double end = len; if (argc > 2 && !argv[2].isUndefined()) { diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp index d959b1667b..3f0e2fd1a7 100644 --- a/src/qml/jsruntime/qv4dataview.cpp +++ b/src/qml/jsruntime/qv4dataview.cpp @@ -55,38 +55,63 @@ void Heap::DataViewCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("DataView")); } -ReturnedValue DataViewCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +static uint toIndex(ExecutionEngine *e, const Value &v) +{ + if (v.isUndefined()) + return 0; + double index = v.toInteger(); + if (index < 0) { + e->throwRangeError(QStringLiteral("index out of range")); + return 0; + } + uint idx = static_cast<uint>(index); + if (idx != index) { + e->throwRangeError(QStringLiteral("index out of range")); + return 0; + } + return idx; +} + +ReturnedValue DataViewCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { Scope scope(f->engine()); Scoped<ArrayBuffer> buffer(scope, argc ? argv[0] : Primitive::undefinedValue()); - if (!buffer || buffer->isDetachedBuffer()) + if (!newTarget || !buffer) + return scope.engine->throwTypeError(); + + uint offset = ::toIndex(scope.engine, argc > 1 ? argv[1]: Primitive::undefinedValue()); + if (scope.hasException()) + return Encode::undefined(); + if (buffer->isDetachedBuffer()) return scope.engine->throwTypeError(); - double bo = argc > 1 ? argv[1].toNumber() : 0; - uint byteOffset = (uint)bo; uint bufferLength = buffer->d()->data->size; - double bl = argc < 3 || argv[2].isUndefined() ? (bufferLength - bo) : argv[2].toNumber(); - uint byteLength = (uint)bl; - if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength) + if (offset > bufferLength) + return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range")); + + uint byteLength = (argc < 3 || argv[2].isUndefined()) ? (bufferLength - offset) : ::toIndex(scope.engine, argv[2]); + if (scope.hasException()) + return Encode::undefined(); + if (offset + byteLength > bufferLength) return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range")); Scoped<DataView> a(scope, scope.engine->memoryManager->allocate<DataView>()); a->d()->buffer.set(scope.engine, buffer->d()); a->d()->byteLength = byteLength; - a->d()->byteOffset = byteOffset; + a->d()->byteOffset = offset; return a.asReturnedValue(); } -ReturnedValue DataViewCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) +ReturnedValue DataViewCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int) { - return virtualCallAsConstructor(f, argv, argc, f); + return f->engine()->throwTypeError(); } void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(3)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); defineDefaultProperty(engine->id_constructor(), (o = ctor)); defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr); @@ -138,6 +163,9 @@ ReturnedValue DataViewPrototype::method_get_byteLength(const FunctionObject *b, if (!v) return b->engine()->throwTypeError(); + if (v->d()->buffer->isDetachedBuffer()) + return b->engine()->throwTypeError(); + return Encode(v->d()->byteLength); } @@ -147,23 +175,28 @@ ReturnedValue DataViewPrototype::method_get_byteOffset(const FunctionObject *b, if (!v) return b->engine()->throwTypeError(); + if (v->d()->buffer->isDetachedBuffer()) + return b->engine()->throwTypeError(); + return Encode(v->d()->byteOffset); } template <typename T> ReturnedValue DataViewPrototype::method_getChar(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { + ExecutionEngine *e = b->engine(); const DataView *v = thisObject->as<DataView>(); - if (!v || argc < 1) - return b->engine()->throwTypeError(); - double l = argv[0].toNumber(); - uint idx = (uint)l; - if (l != idx || idx + sizeof(T) > v->d()->byteLength) - return b->engine()->throwTypeError(); + if (!v) + return e->throwTypeError(); + uint idx = ::toIndex(e, argc ? argv[0] : Primitive::undefinedValue()); + if (e->hasException) + return Encode::undefined(); + if (v->d()->buffer->isDetachedBuffer()) + return e->throwTypeError(); + if (idx + sizeof(T) > v->d()->byteLength) + return e->throwRangeError(QStringLiteral("index out of range")); idx += v->d()->byteOffset; - if (v->d()->buffer->isDetachedBuffer()) - return b->engine()->throwTypeError(); T t = T(v->d()->buffer->data->data()[idx]); return Encode((int)t); @@ -172,19 +205,21 @@ ReturnedValue DataViewPrototype::method_getChar(const FunctionObject *b, const V template <typename T> ReturnedValue DataViewPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { + ExecutionEngine *e = b->engine(); const DataView *v = thisObject->as<DataView>(); - if (!v || argc < 1) - return b->engine()->throwTypeError(); - double l = argv[0].toNumber(); - uint idx = (uint)l; - if (l != idx || idx + sizeof(T) > v->d()->byteLength) - return b->engine()->throwTypeError(); + if (!v) + return e->throwTypeError(); + uint idx = ::toIndex(e, argc ? argv[0] : Primitive::undefinedValue()); + if (e->hasException) + return Encode::undefined(); + if (v->d()->buffer->isDetachedBuffer()) + return e->throwTypeError(); + if (idx + sizeof(T) > v->d()->byteLength) + return e->throwRangeError(QStringLiteral("index out of range")); idx += v->d()->byteOffset; bool littleEndian = argc < 2 ? false : argv[1].toBoolean(); - if (v->d()->buffer->isDetachedBuffer()) - return b->engine()->throwTypeError(); T t = littleEndian ? qFromLittleEndian<T>((uchar *)v->d()->buffer->data->data() + idx) : qFromBigEndian<T>((uchar *)v->d()->buffer->data->data() + idx); @@ -195,20 +230,21 @@ ReturnedValue DataViewPrototype::method_get(const FunctionObject *b, const Value template <typename T> ReturnedValue DataViewPrototype::method_getFloat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { + ExecutionEngine *e = b->engine(); const DataView *v = thisObject->as<DataView>(); - if (!v || argc < 1) - return b->engine()->throwTypeError(); - double l = argv[0].toNumber(); - uint idx = (uint)l; - if (l != idx || idx + sizeof(T) > v->d()->byteLength) - return b->engine()->throwTypeError(); + if (!v) + return e->throwTypeError(); + uint idx = ::toIndex(e, argc ? argv[0] : Primitive::undefinedValue()); + if (e->hasException) + return Encode::undefined(); + if (v->d()->buffer->isDetachedBuffer()) + return e->throwTypeError(); + if (idx + sizeof(T) > v->d()->byteLength) + return e->throwRangeError(QStringLiteral("index out of range")); idx += v->d()->byteOffset; bool littleEndian = argc < 2 ? false : argv[1].toBoolean(); - if (v->d()->buffer->isDetachedBuffer()) - return b->engine()->throwTypeError(); - if (sizeof(T) == 4) { // float union { @@ -235,19 +271,22 @@ ReturnedValue DataViewPrototype::method_getFloat(const FunctionObject *b, const template <typename T> ReturnedValue DataViewPrototype::method_setChar(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { + ExecutionEngine *e = b->engine(); const DataView *v = thisObject->as<DataView>(); - if (!v || argc < 1) - return b->engine()->throwTypeError(); - double l = argv[0].toNumber(); - uint idx = (uint)l; - if (l != idx || idx + sizeof(T) > v->d()->byteLength) - return b->engine()->throwTypeError(); - idx += v->d()->byteOffset; + if (!v) + return e->throwTypeError(); + uint idx = ::toIndex(e, argc ? argv[0] : Primitive::undefinedValue()); + if (e->hasException) + return Encode::undefined(); int val = argc >= 2 ? argv[1].toInt32() : 0; if (v->d()->buffer->isDetachedBuffer()) - return b->engine()->throwTypeError(); + return e->throwTypeError(); + + if (idx + sizeof(T) > v->d()->byteLength) + return e->throwRangeError(QStringLiteral("index out of range")); + idx += v->d()->byteOffset; v->d()->buffer->data->data()[idx] = (char)val; @@ -257,21 +296,24 @@ ReturnedValue DataViewPrototype::method_setChar(const FunctionObject *b, const V template <typename T> ReturnedValue DataViewPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { + ExecutionEngine *e = b->engine(); const DataView *v = thisObject->as<DataView>(); - if (!v || argc < 1) - return b->engine()->throwTypeError(); - double l = argv[0].toNumber(); - uint idx = (uint)l; - if (l != idx || idx + sizeof(T) > v->d()->byteLength) - return b->engine()->throwTypeError(); - idx += v->d()->byteOffset; + if (!v) + return e->throwTypeError(); + uint idx = ::toIndex(e, argc ? argv[0] : Primitive::undefinedValue()); + if (e->hasException) + return Encode::undefined(); int val = argc >= 2 ? argv[1].toInt32() : 0; - bool littleEndian = argc < 3 ? false : argv[2].toBoolean(); if (v->d()->buffer->isDetachedBuffer()) - return b->engine()->throwTypeError(); + return e->throwTypeError(); + + if (idx + sizeof(T) > v->d()->byteLength) + return e->throwRangeError(QStringLiteral("index out of range")); + idx += v->d()->byteOffset; + if (littleEndian) qToLittleEndian<T>(val, (uchar *)v->d()->buffer->data->data() + idx); @@ -284,19 +326,23 @@ ReturnedValue DataViewPrototype::method_set(const FunctionObject *b, const Value template <typename T> ReturnedValue DataViewPrototype::method_setFloat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { + ExecutionEngine *e = b->engine(); const DataView *v = thisObject->as<DataView>(); - if (!v || argc < 1) - return b->engine()->throwTypeError(); - double l = argv[0].toNumber(); - uint idx = (uint)l; - if (l != idx || idx + sizeof(T) > v->d()->byteLength) - return b->engine()->throwTypeError(); - idx += v->d()->byteOffset; + if (!v) + return e->throwTypeError(); + uint idx = ::toIndex(e, argc ? argv[0] : Primitive::undefinedValue()); + if (e->hasException) + return Encode::undefined(); double val = argc >= 2 ? argv[1].toNumber() : qt_qnan(); bool littleEndian = argc < 3 ? false : argv[2].toBoolean(); + if (v->d()->buffer->isDetachedBuffer()) - return b->engine()->throwTypeError(); + return e->throwTypeError(); + + if (idx + sizeof(T) > v->d()->byteLength) + return e->throwRangeError(QStringLiteral("index out of range")); + idx += v->d()->byteOffset; if (sizeof(T) == 4) { // float diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 3efd626685..7335dd1ba0 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -806,7 +806,7 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(engine->id_toString(), method_toString, 0); defineDefaultProperty(QStringLiteral("toDateString"), method_toDateString, 0); defineDefaultProperty(QStringLiteral("toTimeString"), method_toTimeString, 0); - defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(engine->id_toLocaleString(), method_toLocaleString, 0); defineDefaultProperty(QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0); defineDefaultProperty(QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0); defineDefaultProperty(engine->id_valueOf(), method_valueOf, 0); diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index ea17f3423c..5ccf3d4f6e 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -275,6 +275,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) jsStrings[String_index] = newIdentifier(QStringLiteral("index")); jsStrings[String_input] = newIdentifier(QStringLiteral("input")); jsStrings[String_toString] = newIdentifier(QStringLiteral("toString")); + jsStrings[String_toLocaleString] = newIdentifier(QStringLiteral("toLocaleString")); jsStrings[String_destroy] = newIdentifier(QStringLiteral("destroy")); jsStrings[String_valueOf] = newIdentifier(QStringLiteral("valueOf")); jsStrings[String_byteLength] = newIdentifier(QStringLiteral("byteLength")); @@ -401,6 +402,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) Q_ASSERT(index == ErrorPrototype::Index_Name); classes[Class_ProxyObject] = classes[Class_Empty]->changeVTable(ProxyObject::staticVTable()); + classes[Class_ProxyFunctionObject] = classes[Class_Empty]->changeVTable(ProxyFunctionObject::staticVTable()); jsObjects[GetStack_Function] = FunctionObject::createBuiltinFunction(this, str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack, 0); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 7608c04800..e11e607bd1 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -315,6 +315,7 @@ public: String_index, String_input, String_toString, + String_toLocaleString, String_destroy, String_valueOf, String_byteLength, @@ -385,6 +386,7 @@ public: String *id_index() const { return reinterpret_cast<String *>(jsStrings + String_index); } String *id_input() const { return reinterpret_cast<String *>(jsStrings + String_input); } String *id_toString() const { return reinterpret_cast<String *>(jsStrings + String_toString); } + String *id_toLocaleString() const { return reinterpret_cast<String *>(jsStrings + String_toLocaleString); } String *id_destroy() const { return reinterpret_cast<String *>(jsStrings + String_destroy); } String *id_valueOf() const { return reinterpret_cast<String *>(jsStrings + String_valueOf); } String *id_byteLength() const { return reinterpret_cast<String *>(jsStrings + String_byteLength); } diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index 189208e731..789ec5d970 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -118,6 +118,7 @@ struct Q_QML_EXPORT EngineBase { Class_ErrorProto, Class_QmlContextWrapper, Class_ProxyObject, + Class_ProxyFunctionObject, Class_Symbol, NClasses }; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 35d8fe18e0..a5dc0ba567 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -73,7 +73,7 @@ DEFINE_OBJECT_VTABLE(FunctionObject); void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, VTable::Call call) { jsCall = call; - jsConstruct = QV4::FunctionObject::virtualCallAsConstructor; + jsConstruct = nullptr; Object::init(); this->scope.set(scope->engine(), scope->d()); @@ -165,11 +165,6 @@ ReturnedValue FunctionObject::name() const return get(scope()->internalClass->engine->id_name()); } -ReturnedValue FunctionObject::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) -{ - return f->engine()->throwTypeError(); -} - ReturnedValue FunctionObject::virtualCall(const FunctionObject *, const Value *, const Value *, int) { return Encode::undefined(); @@ -587,11 +582,6 @@ ReturnedValue ConstructorFunction::virtualCall(const FunctionObject *f, const Va DEFINE_OBJECT_VTABLE(MemberFunction); -ReturnedValue MemberFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) -{ - return f->engine()->throwTypeError(QStringLiteral("Function is not a constructor.")); -} - DEFINE_OBJECT_VTABLE(DefaultClassConstructorFunction); ReturnedValue DefaultClassConstructorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) @@ -652,6 +642,9 @@ void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject this->boundArgs.set(s.engine, boundArgs ? boundArgs->d() : nullptr); this->boundThis.set(scope->engine(), boundThis); + if (!target->isConstructor()) + jsConstruct = nullptr; + ScopedObject f(s, this); ScopedValue l(s, target->get(s.engine->id_length())); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 8482189bb3..54964b9bbd 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -80,6 +80,10 @@ DECLARE_HEAP_OBJECT(FunctionObject, Object) { Index_ProtoConstructor = 0 }; + bool isConstructor() const { + return jsConstruct != nullptr; + } + Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name, VTable::Call call); void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr, bool createProto = false); void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr); @@ -170,13 +174,16 @@ struct Q_QML_EXPORT FunctionObject: Object { inline ReturnedValue callAsConstructor(const JSCallData &data) const; ReturnedValue callAsConstructor(const Value *argv, int argc, const Value *newTarget = nullptr) const { + if (!d()->jsConstruct) + return engine()->throwTypeError(QStringLiteral("Function is not a constructor.")); return d()->jsConstruct(this, argv, argc, newTarget ? newTarget : this); } inline ReturnedValue call(const JSCallData &data) const; ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const { + if (!d()->jsCall) + return engine()->throwTypeError(QStringLiteral("Function can only be called with |new|.")); return d()->jsCall(this, thisObject, argv, argc); } - static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function); @@ -187,6 +194,9 @@ struct Q_QML_EXPORT FunctionObject: Object { bool strictMode() const { return d()->function ? d()->function->isStrict() : false; } bool isBinding() const; bool isBoundFunction() const; + bool isConstructor() const { + return d()->isConstructor(); + } ReturnedValue protoProperty() const { return get(engine()->id_prototype()); } @@ -261,7 +271,6 @@ struct ConstructorFunction : ScriptFunction { struct MemberFunction : ScriptFunction { V4_OBJECT2(MemberFunction, ScriptFunction) V4_INTERNALCLASS(MemberFunction) - static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); }; diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp index 4a784bff35..bab0cfbfb1 100644 --- a/src/qml/jsruntime/qv4generatorobject.cpp +++ b/src/qml/jsruntime/qv4generatorobject.cpp @@ -84,11 +84,6 @@ Heap::FunctionObject *GeneratorFunction::create(ExecutionContext *context, Funct return g->d(); } -ReturnedValue GeneratorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) -{ - return f->engine()->throwTypeError(); -} - ReturnedValue GeneratorFunction::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) { const GeneratorFunction *gf = static_cast<const GeneratorFunction *>(f); @@ -245,8 +240,3 @@ Heap::FunctionObject *MemberGeneratorFunction::create(ExecutionContext *context, g->setPrototypeOf(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype()))); return g->d(); } - -ReturnedValue MemberGeneratorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) -{ - return f->engine()->throwTypeError(QStringLiteral("Function is not a constructor.")); -} diff --git a/src/qml/jsruntime/qv4generatorobject_p.h b/src/qml/jsruntime/qv4generatorobject_p.h index 017580e1a1..f00f730344 100644 --- a/src/qml/jsruntime/qv4generatorobject_p.h +++ b/src/qml/jsruntime/qv4generatorobject_p.h @@ -109,7 +109,7 @@ struct GeneratorFunction : ScriptFunction V4_INTERNALCLASS(GeneratorFunction) static Heap::FunctionObject *create(ExecutionContext *scope, Function *function); - static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static constexpr VTable::CallAsConstructor virtualCallAsConstructor = nullptr; static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; @@ -119,7 +119,6 @@ struct MemberGeneratorFunction : GeneratorFunction V4_INTERNALCLASS(MemberGeneratorFunction) static Heap::FunctionObject *create(ExecutionContext *scope, Function *function, String *name); - static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); }; struct GeneratorPrototype : Object diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h index 307eec9111..55cedf50aa 100644 --- a/src/qml/jsruntime/qv4jscall_p.h +++ b/src/qml/jsruntime/qv4jscall_p.h @@ -102,15 +102,13 @@ struct JSCallData { inline ReturnedValue FunctionObject::callAsConstructor(const JSCallData &data) const { - if (!d()->jsConstruct) - return engine()->throwTypeError(QStringLiteral("Object is not a constructor.")); - return d()->jsConstruct(this, data.args, data.argc, this); + return callAsConstructor(data.args, data.argc, this); } inline ReturnedValue FunctionObject::call(const JSCallData &data) const { - return d()->jsCall(this, data.thisObject, data.args, data.argc); + return call(data.thisObject, data.args, data.argc); } diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index d103f65d47..d552948e95 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -117,7 +117,7 @@ QT_WARNING_POP defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(engine->id_toString(), method_toString, 1); - defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString); + defineDefaultProperty(engine->id_toLocaleString(), method_toLocaleString); defineDefaultProperty(engine->id_valueOf(), method_valueOf); defineDefaultProperty(QStringLiteral("toFixed"), method_toFixed, 1); defineDefaultProperty(QStringLiteral("toExponential"), method_toExponential, 1); diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 6a6687661c..eb4e392c0f 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -308,16 +308,6 @@ PropertyIndex Object::getValueOrSetter(PropertyKey id, PropertyAttributes *attrs return { nullptr, nullptr }; } -ReturnedValue Object::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) -{ - return f->engine()->throwTypeError(); -} - -ReturnedValue Object::virtualCall(const FunctionObject *f, const Value *, const Value *, int) -{ - return f->engine()->throwTypeError(); -} - ReturnedValue Object::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { if (id.isArrayIndex()) @@ -960,6 +950,28 @@ bool Object::isArray() const return false; } +const FunctionObject *Object::speciesConstructor(Scope &scope, const FunctionObject *defaultConstructor) const +{ + ScopedValue C(scope, get(scope.engine->id_constructor())); + if (C->isUndefined()) + return defaultConstructor; + const Object *c = C->objectValue(); + if (!c) { + scope.engine->throwTypeError(); + return nullptr; + } + ScopedValue S(scope, c->get(scope.engine->symbol_species())); + if (S->isNullOrUndefined()) + return defaultConstructor; + const FunctionObject *f = S->as<FunctionObject>(); + if (!f || !f->isConstructor()) { + scope.engine->throwTypeError(); + return nullptr; + } + Q_ASSERT(f->isFunctionObject()); + return static_cast<const FunctionObject *>(f); +} + DEFINE_OBJECT_VTABLE(ArrayObject); diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 431c378334..70fee128b6 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -361,10 +361,9 @@ public: bool isConcatSpreadable() const; bool isArray() const; + const FunctionObject *speciesConstructor(Scope &scope, const FunctionObject *defaultConstructor) const; protected: - static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); - static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver,bool *hasProperty); static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); static bool virtualDeleteProperty(Managed *m, PropertyKey id); diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index ed810cb456..c968bff0fe 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -119,7 +119,7 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(v4->id_toString(), method_toString, 0); - defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString, 0); + defineDefaultProperty(v4->id_toLocaleString(), method_toLocaleString, 0); defineDefaultProperty(v4->id_valueOf(), method_valueOf, 0); defineDefaultProperty(QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 1); defineDefaultProperty(QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 1); diff --git a/src/qml/jsruntime/qv4proxy.cpp b/src/qml/jsruntime/qv4proxy.cpp index e26c51b473..d4f342c50e 100644 --- a/src/qml/jsruntime/qv4proxy.cpp +++ b/src/qml/jsruntime/qv4proxy.cpp @@ -48,6 +48,7 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(ProxyObject); +DEFINE_OBJECT_VTABLE(ProxyFunctionObject); void Heap::ProxyObject::init(const QV4::Object *target, const QV4::Object *handler) { @@ -57,6 +58,18 @@ void Heap::ProxyObject::init(const QV4::Object *target, const QV4::Object *handl this->handler.set(e, handler->d()); } +void Heap::ProxyFunctionObject::init(const QV4::FunctionObject *target, const QV4::Object *handler) +{ + ExecutionEngine *e = internalClass->engine; + FunctionObject::init(e->rootContext()); + this->target.set(e, target->d()); + this->handler.set(e, handler->d()); + + if (!target->isConstructor()) + jsConstruct = nullptr; +} + + ReturnedValue ProxyObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { Scope scope(m); @@ -631,15 +644,68 @@ OwnPropertyKeyIterator *ProxyObject::virtualOwnPropertyKeys(const Object *m) } -//ReturnedValue ProxyObject::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) -//{ +ReturnedValue ProxyFunctionObject::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) +{ + Scope scope(f); + const ProxyObject *o = static_cast<const ProxyObject *>(f); + if (!o->d()->handler) + return scope.engine->throwTypeError(); -//} + ScopedFunctionObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString name(scope, scope.engine->newString(QStringLiteral("construct"))); + ScopedValue trap(scope, handler->get(name)); -//ReturnedValue ProxyObject::call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) -//{ + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) { + Q_ASSERT(target->isConstructor()); + return target->callAsConstructor(argv, argc, newTarget); + } + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); -//} + ScopedFunctionObject trapFunction(scope, trap); + Value *arguments = scope.alloc(3); + arguments[0] = target; + arguments[1] = scope.engine->newArrayObject(argv, argc); + arguments[2] = newTarget ? *newTarget : Primitive::undefinedValue(); + ScopedObject result(scope, trapFunction->call(handler, arguments, 3)); + + if (!result) + return scope.engine->throwTypeError(); + return result->asReturnedValue(); +} + +ReturnedValue ProxyFunctionObject::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(f); + + const ProxyObject *o = static_cast<const ProxyObject *>(f); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedFunctionObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString name(scope, scope.engine->newString(QStringLiteral("apply"))); + ScopedValue trap(scope, handler->get(name)); + + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->call(thisObject, argv, argc); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + ScopedFunctionObject trapFunction(scope, trap); + Value *arguments = scope.alloc(3); + arguments[0] = target; + arguments[1] = thisObject ? *thisObject : Primitive::undefinedValue(); + arguments[2] = scope.engine->newArrayObject(argv, argc); + return trapFunction->call(handler, arguments, 3); +} DEFINE_OBJECT_VTABLE(Proxy); @@ -668,8 +734,10 @@ ReturnedValue Proxy::virtualCallAsConstructor(const FunctionObject *f, const Val if (!phandler->d()->handler) return scope.engine->throwTypeError(); - ScopedObject o(scope, scope.engine->memoryManager->allocate<ProxyObject>(target, handler)); - return o->asReturnedValue(); + const FunctionObject *targetFunction = target->as<FunctionObject>(); + if (targetFunction) + return scope.engine->memoryManager->allocate<ProxyFunctionObject>(targetFunction, handler)->asReturnedValue(); + return scope.engine->memoryManager->allocate<ProxyObject>(target, handler)->asReturnedValue(); } ReturnedValue Proxy::virtualCall(const FunctionObject *f, const Value *, const Value *, int) @@ -683,6 +751,7 @@ ReturnedValue Proxy::method_revocable(const FunctionObject *f, const Value *, co ScopedObject proxy(scope, Proxy::virtualCallAsConstructor(f, argv, argc, f)); if (scope.hasException()) return Encode::undefined(); + Q_ASSERT(proxy); ScopedString revoke(scope, scope.engine->newString(QStringLiteral("revoke"))); ScopedFunctionObject revoker(scope, createBuiltinFunction(scope.engine, revoke, method_revoke, 0)); @@ -698,8 +767,9 @@ ReturnedValue Proxy::method_revocable(const FunctionObject *f, const Value *, co ReturnedValue Proxy::method_revoke(const FunctionObject *f, const Value *, const Value *, int) { Scope scope(f); - Scoped<ProxyObject> proxy(scope, f->get(scope.engine->symbol_revokableProxy())); - Q_ASSERT(proxy); + ScopedObject o(scope, f->get(scope.engine->symbol_revokableProxy())); + Q_ASSERT(o); + ProxyObject *proxy = o->cast<ProxyObject>(); proxy->d()->target.set(scope.engine, nullptr); proxy->d()->handler.set(scope.engine, nullptr); diff --git a/src/qml/jsruntime/qv4proxy_p.h b/src/qml/jsruntime/qv4proxy_p.h index bad4d040c7..7c10b91b13 100644 --- a/src/qml/jsruntime/qv4proxy_p.h +++ b/src/qml/jsruntime/qv4proxy_p.h @@ -63,12 +63,16 @@ namespace Heap { Member(class, Pointer, Object *, target) \ Member(class, Pointer, Object *, handler) -DECLARE_HEAP_OBJECT(ProxyObject, Object) { +DECLARE_HEAP_OBJECT(ProxyObject, FunctionObject) { DECLARE_MARKOBJECTS(ProxyObject) void init(const QV4::Object *target, const QV4::Object *handler); }; +struct ProxyFunctionObject : ProxyObject { + void init(const QV4::FunctionObject *target, const QV4::Object *handler); +}; + #define ProxyMembers(class, Member) \ Member(class, Pointer, Symbol *, revokableProxySymbol) \ @@ -80,10 +84,21 @@ DECLARE_HEAP_OBJECT(Proxy, FunctionObject) { } -struct ProxyObject: Object { +/* + * The inheritance from FunctionObject is a hack. Regular proxy objects are no function objects. + * But this helps implement the proxy for function objects, where we need this and thus gives us + * all the virtual methods from ProxyObject without having to duplicate them. + * + * But it does require a few hacks to make sure we don't recognize regular proxy objects as function + * objects in the runtime. + */ +struct ProxyObject : FunctionObject { V4_OBJECT2(ProxyObject, Object) Q_MANAGED_TYPE(ProxyObject) V4_INTERNALCLASS(ProxyObject) + enum { + IsFunctionObject = false + }; static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); @@ -96,10 +111,18 @@ struct ProxyObject: Object { static Heap::Object *virtualGetPrototypeOf(const Managed *); static bool virtualSetPrototypeOf(Managed *, const Object *); static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m); +}; - // those might require a second proxy object that derives from FunctionObject... -// static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); -// static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +struct ProxyFunctionObject : ProxyObject { + V4_OBJECT2(ProxyFunctionObject, FunctionObject) + Q_MANAGED_TYPE(ProxyObject) + V4_INTERNALCLASS(ProxyFunctionObject) + enum { + IsFunctionObject = true + }; + + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct Proxy : FunctionObject diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp index 90c39f7c37..3dc0956e0c 100644 --- a/src/qml/jsruntime/qv4reflect.cpp +++ b/src/qml/jsruntime/qv4reflect.cpp @@ -103,7 +103,11 @@ ReturnedValue Reflect::method_apply(const FunctionObject *f, const Value *, cons ReturnedValue Reflect::method_construct(const FunctionObject *f, const Value *, const Value *argv, int argc) { Scope scope(f); - if (argc < 2 || !argv[0].isFunctionObject() || !argv[1].isObject()) + if (argc < 2 || !argv[1].isObject()) + return scope.engine->throwTypeError(); + const FunctionObject *target = argv[0].as<FunctionObject>(); + const FunctionObject *newTarget = argc == 3 ? argv[2].as<FunctionObject>() : target; + if (!target || !target->isConstructor() || !newTarget || !newTarget->isConstructor()) return scope.engine->throwTypeError(); const Object *o = static_cast<const Object *>(argv + 1); @@ -111,7 +115,7 @@ ReturnedValue Reflect::method_construct(const FunctionObject *f, const Value *, if (scope.hasException()) return Encode::undefined(); - return static_cast<const FunctionObject &>(argv[0]).callAsConstructor(arguments.argv, arguments.argc); + return target->callAsConstructor(arguments.argv, arguments.argc, newTarget); } ReturnedValue Reflect::method_defineProperty(const FunctionObject *f, const Value *, const Value *argv, int argc) diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index dd33a9fc41..dd16110c28 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -765,26 +765,6 @@ ReturnedValue RegExpPrototype::method_get_source(const FunctionObject *f, const return scope.engine->newString(re->toString())->asReturnedValue(); } -static const FunctionObject *speciesConstructor(Scope &scope, const Object *o, const FunctionObject *defaultConstructor) -{ - ScopedValue C(scope, o->get(scope.engine->id_constructor())); - if (C->isUndefined()) - return defaultConstructor; - const Object *c = C->objectValue(); - if (!c) { - scope.engine->throwTypeError(); - return nullptr; - } - ScopedValue S(scope, c->get(scope.engine->symbol_species())); - if (S->isNullOrUndefined()) - return defaultConstructor; - if (!S->isFunctionObject()) { - scope.engine->throwTypeError(); - return nullptr; - } - return static_cast<const FunctionObject *>(S.ptr); -} - ReturnedValue RegExpPrototype::method_split(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) { Scope scope(f); @@ -805,7 +785,7 @@ ReturnedValue RegExpPrototype::method_split(const FunctionObject *f, const Value flags = scope.engine->newString(flagsString + QLatin1Char('y')); bool unicodeMatching = flagsString.contains(QLatin1Char('u')); - const FunctionObject *C = speciesConstructor(scope, rx, scope.engine->regExpCtor()); + const FunctionObject *C = rx->speciesConstructor(scope, scope.engine->regExpCtor()); if (!C) return Encode::undefined(); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 82e1284289..9180108a0c 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1550,8 +1550,9 @@ ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classInde if (superClass.isNull()) { protoParent = Encode::null(); } else { + const FunctionObject *superFunction = superClass.as<FunctionObject>(); // ### check that the heritage object is a constructor - if (!superClass.isFunctionObject()) + if (!superFunction || !superFunction->isConstructor()) return engine->throwTypeError(QStringLiteral("The superclass is not a function object.")); const FunctionObject *s = static_cast<const FunctionObject *>(&superClass); ScopedValue result(scope, s->get(scope.engine->id_prototype())); diff --git a/src/qml/jsruntime/qv4symbol.cpp b/src/qml/jsruntime/qv4symbol.cpp index bdefe1eb9e..d5ae094e1f 100644 --- a/src/qml/jsruntime/qv4symbol.cpp +++ b/src/qml/jsruntime/qv4symbol.cpp @@ -80,6 +80,11 @@ ReturnedValue QV4::SymbolCtor::virtualCall(const QV4::FunctionObject *f, const Q return Symbol::create(scope.engine, desc)->asReturnedValue(); } +ReturnedValue SymbolCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) +{ + return f->engine()->throwTypeError(QStringLiteral("Symbol can't be used together with |new|.")); +} + ReturnedValue SymbolCtor::method_for(const FunctionObject *f, const Value *, const Value *argv, int argc) { Scope scope(f); diff --git a/src/qml/jsruntime/qv4symbol_p.h b/src/qml/jsruntime/qv4symbol_p.h index cb60f748de..c7e12b512b 100644 --- a/src/qml/jsruntime/qv4symbol_p.h +++ b/src/qml/jsruntime/qv4symbol_p.h @@ -83,6 +83,7 @@ struct SymbolCtor : FunctionObject V4_OBJECT2(SymbolCtor, FunctionObject) static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *newTarget); static ReturnedValue method_for(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_keyFor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index bdede94d46..456a577b52 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -43,6 +43,7 @@ #include "qv4string_p.h" #include "qv4jscall_p.h" #include "qv4symbol_p.h" +#include "qv4runtime_p.h" #include <cmath> @@ -56,16 +57,31 @@ DEFINE_OBJECT_VTABLE(TypedArray); Q_STATIC_ASSERT((int)ExecutionEngine::NTypedArrayTypes == (int)Heap::TypedArray::NTypes); +static inline int toInt32(Value v) +{ + Q_ASSERT(v.isNumber()); + if (v.isInteger()) + return v.integerValue(); + return Double::toInt32(v.doubleValue()); +} + +static inline double toDouble(Value v) +{ + Q_ASSERT(v.isNumber()); + if (v.isInteger()) + return v.integerValue(); + return v.doubleValue(); +} + ReturnedValue Int8ArrayRead(const char *data, int index) { return Encode((int)(signed char)data[index]); } -void Int8ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value) +void Int8ArrayWrite(char *data, int index, const Value &value) { - signed char v = (signed char)value.toUInt32(); - if (e->hasException) - return; + int n = toInt32(value); + signed char v = static_cast<signed char>(n); data[index] = v; } @@ -74,23 +90,22 @@ ReturnedValue UInt8ArrayRead(const char *data, int index) return Encode((int)(unsigned char)data[index]); } -void UInt8ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value) +void UInt8ArrayWrite(char *data, int index, const Value &value) { - unsigned char v = (unsigned char)value.toUInt32(); - if (e->hasException) - return; + int n = toInt32(value); + unsigned char v = static_cast<unsigned char>(uint(n)); data[index] = v; } -void UInt8ClampedArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value) +void UInt8ClampedArrayWrite(char *data, int index, const Value &value) { + Q_ASSERT(value.isNumber()); if (value.isInteger()) { data[index] = (char)(unsigned char)qBound(0, value.integerValue(), 255); return; } - double d = value.toNumber(); - if (e->hasException) - return; + Q_ASSERT(value.isDouble()); + double d = value.doubleValue(); // ### is there a way to optimise this? if (d <= 0 || std::isnan(d)) { data[index] = 0; @@ -122,11 +137,10 @@ ReturnedValue Int16ArrayRead(const char *data, int index) return Encode((int)*(const short *)(data + index)); } -void Int16ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value) +void Int16ArrayWrite(char *data, int index, const Value &value) { - short v = (short)value.toInt32(); - if (e->hasException) - return; + int n = toInt32(value); + short v = static_cast<short>(n); *(short *)(data + index) = v; } @@ -135,11 +149,10 @@ ReturnedValue UInt16ArrayRead(const char *data, int index) return Encode((int)*(const unsigned short *)(data + index)); } -void UInt16ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value) +void UInt16ArrayWrite(char *data, int index, const Value &value) { - unsigned short v = (unsigned short)value.toInt32(); - if (e->hasException) - return; + int n = toInt32(value); + unsigned short v = static_cast<unsigned short>(n); *(unsigned short *)(data + index) = v; } @@ -148,11 +161,9 @@ ReturnedValue Int32ArrayRead(const char *data, int index) return Encode(*(const int *)(data + index)); } -void Int32ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value) +void Int32ArrayWrite(char *data, int index, const Value &value) { - int v = (int)value.toInt32(); - if (e->hasException) - return; + int v = toInt32(value); *(int *)(data + index) = v; } @@ -161,11 +172,10 @@ ReturnedValue UInt32ArrayRead(const char *data, int index) return Encode(*(const unsigned int *)(data + index)); } -void UInt32ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value) +void UInt32ArrayWrite(char *data, int index, const Value &value) { - unsigned int v = (unsigned int)value.toUInt32(); - if (e->hasException) - return; + int n = toInt32(value); + unsigned v = static_cast<unsigned>(n); *(unsigned int *)(data + index) = v; } @@ -174,11 +184,9 @@ ReturnedValue Float32ArrayRead(const char *data, int index) return Encode(*(const float *)(data + index)); } -void Float32ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value) +void Float32ArrayWrite(char *data, int index, const Value &value) { - float v = value.toNumber(); - if (e->hasException) - return; + float v = toDouble(value); *(float *)(data + index) = v; } @@ -187,11 +195,9 @@ ReturnedValue Float64ArrayRead(const char *data, int index) return Encode(*(const double *)(data + index)); } -void Float64ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value) +void Float64ArrayWrite(char *data, int index, const Value &value) { - double v = value.toNumber(); - if (e->hasException) - return; + double v = toDouble(value); *(double *)(data + index) = v; } @@ -276,7 +282,7 @@ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, for (uint i = 0; i < l; ++i) { Primitive val; val.setRawValue(read(src, i*srcElementSize)); - write(scope.engine, dest, i*destElementSize, val); + write(dest, i*destElementSize, val); } } @@ -345,7 +351,10 @@ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, ScopedValue val(scope); while (idx < l) { val = o->get(idx); - array->d()->type->write(scope.engine, b, 0, val); + val = val->convertedToNumber(); + if (scope.engine->hasException) + return Encode::undefined(); + array->d()->type->write(b, 0, val); if (scope.engine->hasException) return Encode::undefined(); ++idx; @@ -383,6 +392,8 @@ ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Val uint index = id.asArrayIndex(); Scope scope(static_cast<const Object *>(m)->engine()); Scoped<TypedArray> a(scope, static_cast<const TypedArray *>(m)); + if (a->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); uint bytesPerElement = a->d()->type->bytesPerElement; uint byteOffset = a->d()->byteOffset + index * bytesPerElement; @@ -408,13 +419,18 @@ bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Valu Scope scope(v4); Scoped<TypedArray> a(scope, static_cast<TypedArray *>(m)); + if (a->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); uint bytesPerElement = a->d()->type->bytesPerElement; uint byteOffset = a->d()->byteOffset + index * bytesPerElement; if (byteOffset + bytesPerElement > (uint)a->d()->buffer->byteLength()) return false; - a->d()->type->write(scope.engine, a->d()->buffer->data->data(), byteOffset, value); + Value v = Primitive::fromReturnedValue(value.convertedToNumber()); + if (scope.hasException() || a->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + a->d()->type->write(a->d()->buffer->data->data(), byteOffset, v); return true; } @@ -450,6 +466,9 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteLength(const Function if (!v) return v4->throwTypeError(); + if (v->d()->buffer->isDetachedBuffer()) + return Encode(0); + return Encode(v->d()->byteLength); } @@ -460,6 +479,9 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteOffset(const Function if (!v) return v4->throwTypeError(); + if (v->d()->buffer->isDetachedBuffer()) + return Encode(0); + return Encode(v->d()->byteOffset); } @@ -470,41 +492,723 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_get_length(const FunctionObje if (!v) return v4->throwTypeError(); + if (v->d()->buffer->isDetachedBuffer()) + return Encode(0); + return Encode(v->d()->byteLength/v->d()->type->bytesPerElement); } +ReturnedValue IntrinsicTypedArrayPrototype::method_copyWithin(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(f); + Scoped<TypedArray> O(scope, thisObject); + if (!O || O->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + if (!argc) + return O->asReturnedValue(); + + qint64 len = static_cast<uint>(O->length()); + + qint64 to = static_cast<qint64>(argv[0].toInteger()); + if (to < 0) + to = qMax(len + to, 0ll); + else + to = qMin(to, len); + + qint64 from = (argc > 1) ? static_cast<qint64>(argv[1].toInteger()) : 0ll; + if (from < 0) + from = qMax(len + from, 0ll); + else + from = qMin(from, len); + + double fend = argv[2].toInteger(); + if (fend > len) + fend = len; + qint64 end = (argc > 2 && !argv[2].isUndefined()) ? static_cast<qint64>(fend) : len; + if (end < 0) + end = qMax(len + end, 0ll); + else + end = qMin(end, len); + + qint64 count = qMin(end - from, len - to); + + if (count <= 0) + return O->asReturnedValue(); + + if (O->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + if (from != to) { + int elementSize = O->d()->type->bytesPerElement; + char *data = O->d()->buffer->data->data() + O->d()->byteOffset; + memmove(data + to*elementSize, data + from*elementSize, count*elementSize); + } + + return O->asReturnedValue(); +} + ReturnedValue IntrinsicTypedArrayPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int) { Scope scope(b); - Scoped<TypedArray> O(scope, thisObject); - if (!O) - THROW_TYPE_ERROR(); + Scoped<TypedArray> v(scope, thisObject); + if (!v || v->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); - Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O)); + Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v)); ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind; return ao->asReturnedValue(); } -ReturnedValue IntrinsicTypedArrayPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int) +ReturnedValue IntrinsicTypedArrayPrototype::method_every(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); - Scoped<TypedArray> O(scope, thisObject); - if (!O) + Scoped<TypedArray> v(scope, thisObject); + if (!v || v->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + uint len = v->length(); + + if (!argc || !argv->isFunctionObject()) + THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); + + ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); + ScopedValue r(scope); + Value *arguments = scope.alloc(3); + + const char *data = v->d()->buffer->data->data(); + uint bytesPerElement = v->d()->type->bytesPerElement; + uint byteOffset = v->d()->byteOffset; + + bool ok = true; + for (uint k = 0; ok && k < len; ++k) { + if (v->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + arguments[0] = v->d()->type->read(data, byteOffset + k * bytesPerElement); + + arguments[1] = Primitive::fromDouble(k); + arguments[2] = v; + r = callback->call(that, arguments, 3); + ok = r->toBoolean(); + } + return Encode(ok); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_fill(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<TypedArray> v(scope, thisObject); + if (!v || v->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + uint len = v->length(); + double dlen = len; + double relativeStart = argc > 1 ? argv[1].toInteger() : 0.; + double relativeEnd = len; + if (argc > 2 && !argv[2].isUndefined()) + relativeEnd = argv[2].toInteger(); + + uint k = 0; + uint fin = 0; + + if (relativeStart < 0) { + k = static_cast<uint>(std::max(len+relativeStart, 0.)); + } else { + k = static_cast<uint>(std::min(relativeStart, dlen)); + } + + if (relativeEnd < 0) { + fin = static_cast<uint>(std::max(len + relativeEnd, 0.)); + } else { + fin = static_cast<uint>(std::min(relativeEnd, dlen)); + } + + double val = argc ? argv[0].toNumber() : std::numeric_limits<double>::quiet_NaN(); + Value value = Primitive::fromDouble(val); + if (scope.hasException() || v->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + char *data = v->d()->buffer->data->data(); + uint bytesPerElement = v->d()->type->bytesPerElement; + uint byteOffset = v->d()->byteOffset; + + while (k < fin) { + v->d()->type->write(data, byteOffset + k * bytesPerElement, value); + k++; + } + + return v.asReturnedValue(); +} + +static TypedArray *typedArraySpeciesCreate(Scope &scope, const TypedArray *instance, uint len) +{ + const FunctionObject *constructor = instance->speciesConstructor(scope, scope.engine->typedArrayCtors + instance->d()->arrayType); + if (!constructor) { + scope.engine->throwTypeError(); + return nullptr; + } + + Value *arguments = scope.alloc(1); + arguments[0] = Encode(len); + Scoped<TypedArray> a(scope, constructor->callAsConstructor(arguments, 1)); + if (!a || a->d()->buffer->isDetachedBuffer() || a->length() < len) { + scope.engine->throwTypeError(); + return nullptr; + } + return a; +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_filter(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<TypedArray> instance(scope, thisObject); + if (!instance || instance->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + uint len = instance->length(); + + if (!argc || !argv->isFunctionObject()) + THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); + + ScopedValue selected(scope); + ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); + Value *arguments = scope.alloc(3); + Value *list = arguments; + + uint to = 0; + for (uint k = 0; k < len; ++k) { + if (instance->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + bool exists; + arguments[0] = instance->get(k, &exists); + if (!exists) + continue; + + arguments[1] = Primitive::fromDouble(k); + arguments[2] = instance; + selected = callback->call(that, arguments, 3); + if (selected->toBoolean()) { + ++arguments; + scope.alloc(1); + ++to; + } + } + + TypedArray *a = typedArraySpeciesCreate(scope, instance, to); + if (!a) + return Encode::undefined(); + + for (uint i = 0; i < to; ++i) + a->put(i, list[i]); + + return a->asReturnedValue(); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_find(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<TypedArray> v(scope, thisObject); + if (!v || v->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + uint len = v->length(); + + if (!argc || !argv[0].isFunctionObject()) THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); + + ScopedValue result(scope); + Value *arguments = scope.alloc(3); + + ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); - Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O)); + for (uint k = 0; k < len; ++k) { + if (v->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + arguments[0] = v->get(k); + CHECK_EXCEPTION(); + + arguments[1] = Primitive::fromDouble(k); + arguments[2] = v; + result = callback->call(that, arguments, 3); + + CHECK_EXCEPTION(); + if (result->toBoolean()) + return arguments[0].asReturnedValue(); + } + + RETURN_UNDEFINED(); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_findIndex(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<TypedArray> v(scope, thisObject); + if (!v || v->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + uint len = v->length(); + + if (!argc || !argv[0].isFunctionObject()) + THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); + + ScopedValue result(scope); + Value *arguments = scope.alloc(3); + + ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); + + for (uint k = 0; k < len; ++k) { + if (v->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + arguments[0] = v->get(k); + CHECK_EXCEPTION(); + + arguments[1] = Primitive::fromDouble(k); + arguments[2] = v; + result = callback->call(that, arguments, 3); + + CHECK_EXCEPTION(); + if (result->toBoolean()) + return Encode(k); + } + + return Encode(-1); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<TypedArray> v(scope, thisObject); + if (!v || v->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + uint len = v->length(); + + if (!argc || !argv->isFunctionObject()) + THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); + + ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); + Value *arguments = scope.alloc(3); + + for (uint k = 0; k < len; ++k) { + if (v->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + bool exists; + arguments[0] = v->get(k, &exists); + if (!exists) + continue; + + arguments[1] = Primitive::fromDouble(k); + arguments[2] = v; + callback->call(that, arguments, 3); + } + RETURN_UNDEFINED(); +} + + +ReturnedValue IntrinsicTypedArrayPrototype::method_includes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<TypedArray> v(scope, thisObject); + if (!v || v->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + uint len = v->length(); + if (len == 0) { + return Encode(false); + } + + double n = 0; + if (argc > 1 && !argv[1].isUndefined()) { + n = argv[1].toInteger(); + } + + double k = 0; + if (n >= 0) { + k = n; + } else { + k = len + n; + if (k < 0) { + k = 0; + } + } + + while (k < len) { + ScopedValue val(scope, v->get(k)); + if (val->sameValueZero(argv[0])) { + return Encode(true); + } + k++; + } + + return Encode(false); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<TypedArray> v(scope, thisObject); + if (!v || v->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + uint len = v->length(); + if (!len) + return Encode(-1); + + ScopedValue searchValue(scope, argc ? argv[0] : Primitive::undefinedValue()); + uint fromIndex = 0; + + if (argc >= 2) { + double f = argv[1].toInteger(); + CHECK_EXCEPTION(); + if (f >= len) + return Encode(-1); + if (f < 0) + f = qMax(len + f, 0.); + fromIndex = (uint) f; + } + + if (v->isStringObject()) { + ScopedValue value(scope); + for (uint k = fromIndex; k < len; ++k) { + bool exists; + value = v->get(k, &exists); + if (exists && RuntimeHelpers::strictEqual(value, searchValue)) + return Encode(k); + } + return Encode(-1); + } + + ScopedValue value(scope); + + for (uint i = fromIndex; i < len; ++i) { + bool exists; + value = v->get(i, &exists); + CHECK_EXCEPTION(); + if (exists && RuntimeHelpers::strictEqual(value, searchValue)) + return Encode(i); + } + return Encode(-1); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_join(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<TypedArray> v(scope, thisObject); + if (!v || v->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + uint len = v->length(); + + ScopedValue arg(scope, argc ? argv[0] : Primitive::undefinedValue()); + + QString r4; + if (arg->isUndefined()) + r4 = QStringLiteral(","); + else + r4 = arg->toQString(); + + const quint32 r2 = len; + + if (!r2) + return Encode(scope.engine->newString()); + + QString R; + + // + // crazy! + // + ScopedString name(scope, scope.engine->newString(QStringLiteral("0"))); + ScopedValue r6(scope, v->get(name)); + if (!r6->isNullOrUndefined()) + R = r6->toQString(); + + ScopedValue r12(scope); + for (quint32 k = 1; k < r2; ++k) { + R += r4; + + name = Primitive::fromDouble(k).toString(scope.engine); + r12 = v->get(name); + CHECK_EXCEPTION(); + + if (!r12->isNullOrUndefined()) + R += r12->toQString(); + } + + return Encode(scope.engine->newString(R)); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<TypedArray> v(scope, thisObject); + if (!v || v->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v)); ao->d()->iterationKind = IteratorKind::KeyIteratorKind; return ao->asReturnedValue(); } -ReturnedValue IntrinsicTypedArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int) + +ReturnedValue IntrinsicTypedArrayPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); - Scoped<TypedArray> O(scope, thisObject); - if (!O) + Scoped<TypedArray> instance(scope, thisObject); + if (!instance || instance->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + uint len = instance->length(); + if (!len) + return Encode(-1); + + ScopedValue searchValue(scope); + uint fromIndex = len; + + if (argc >= 1) + searchValue = argv[0]; + else + searchValue = Primitive::undefinedValue(); + + if (argc >= 2) { + double f = argv[1].toInteger(); + CHECK_EXCEPTION(); + if (f > 0) + f = qMin(f, (double)(len - 1)); + else if (f < 0) { + f = len + f; + if (f < 0) + return Encode(-1); + } + fromIndex = (uint) f + 1; + } + + ScopedValue value(scope); + for (uint k = fromIndex; k > 0;) { + --k; + bool exists; + value = instance->get(k, &exists); + if (exists && RuntimeHelpers::strictEqual(value, searchValue)) + return Encode(k); + } + return Encode(-1); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_map(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<TypedArray> instance(scope, thisObject); + if (!instance || instance->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + uint len = instance->length(); + + if (!argc || !argv->isFunctionObject()) THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); + + TypedArray *a = typedArraySpeciesCreate(scope, instance, len); + if (!a) + return Encode::undefined(); - Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O)); + ScopedValue v(scope); + ScopedValue mapped(scope); + ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); + Value *arguments = scope.alloc(3); + + for (uint k = 0; k < len; ++k) { + if (instance->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + arguments[0] = instance->get(k); + + arguments[1] = Primitive::fromDouble(k); + arguments[2] = instance; + mapped = callback->call(that, arguments, 3); + a->put(k, mapped); + } + return a->asReturnedValue(); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_reduce(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<TypedArray> instance(scope, thisObject); + if (!instance || instance->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + uint len = instance->length(); + + if (!argc || !argv->isFunctionObject()) + THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); + + uint k = 0; + ScopedValue acc(scope); + ScopedValue v(scope); + + if (argc > 1) { + acc = argv[1]; + } else { + bool kPresent = false; + while (k < len && !kPresent) { + v = instance->get(k, &kPresent); + if (kPresent) + acc = v; + ++k; + } + if (!kPresent) + THROW_TYPE_ERROR(); + } + + Value *arguments = scope.alloc(4); + + while (k < len) { + if (instance->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + bool kPresent; + v = instance->get(k, &kPresent); + if (kPresent) { + arguments[0] = acc; + arguments[1] = v; + arguments[2] = Primitive::fromDouble(k); + arguments[3] = instance; + acc = callback->call(nullptr, arguments, 4); + } + ++k; + } + return acc->asReturnedValue(); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_reduceRight(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<TypedArray> instance(scope, thisObject); + if (!instance || instance->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + uint len = instance->length(); + + if (!argc || !argv->isFunctionObject()) + THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); + + if (len == 0) { + if (argc == 1) + THROW_TYPE_ERROR(); + return argv[1].asReturnedValue(); + } + + uint k = len; + ScopedValue acc(scope); + ScopedValue v(scope); + if (argc > 1) { + acc = argv[1]; + } else { + bool kPresent = false; + while (k > 0 && !kPresent) { + v = instance->get(k - 1, &kPresent); + if (kPresent) + acc = v; + --k; + } + if (!kPresent) + THROW_TYPE_ERROR(); + } + + Value *arguments = scope.alloc(4); + + while (k > 0) { + if (instance->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + bool kPresent; + v = instance->get(k - 1, &kPresent); + if (kPresent) { + arguments[0] = acc; + arguments[1] = v; + arguments[2] = Primitive::fromDouble(k - 1); + arguments[3] = instance; + acc = callback->call(nullptr, arguments, 4); + } + --k; + } + return acc->asReturnedValue(); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_reverse(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<TypedArray> instance(scope, thisObject); + if (!instance || instance->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + uint length = instance->length(); + + int lo = 0, hi = length - 1; + + ScopedValue lval(scope); + ScopedValue hval(scope); + for (; lo < hi; ++lo, --hi) { + bool loExists, hiExists; + lval = instance->get(lo, &loExists); + hval = instance->get(hi, &hiExists); + Q_ASSERT(hiExists && loExists); + bool ok; + ok = instance->put(lo, hval); + Q_ASSERT(ok); + ok = instance->put(hi, lval); + Q_ASSERT(ok); + } + return instance->asReturnedValue(); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_some(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<TypedArray> instance(scope, thisObject); + if (!instance || instance->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + uint len = instance->length(); + + if (!argc || !argv->isFunctionObject()) + THROW_TYPE_ERROR(); + const FunctionObject *callback = static_cast<const FunctionObject *>(argv); + + ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); + ScopedValue result(scope); + Value *arguments = scope.alloc(3); + + for (uint k = 0; k < len; ++k) { + if (instance->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + bool exists; + arguments[0] = instance->get(k, &exists); + if (!exists) + continue; + + arguments[1] = Primitive::fromDouble(k); + arguments[2] = instance; + result = callback->call(that, arguments, 3); + if (result->toBoolean()) + return Encode(true); + } + return Encode(false); +} + + +ReturnedValue IntrinsicTypedArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<TypedArray> v(scope, thisObject); + if (!v || v->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v)); ao->d()->iterationKind = IteratorKind::ValueIteratorKind; return ao->asReturnedValue(); } @@ -550,7 +1254,12 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b, ScopedValue val(scope); while (idx < l) { val = o->get(idx); - a->d()->type->write(scope.engine, b, 0, val); + if (scope.hasException()) + return Encode::undefined(); + val = val->convertedToNumber(); + if (scope.hasException() || buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + a->d()->type->write(b, 0, val); if (scope.engine->hasException) RETURN_UNDEFINED(); ++idx; @@ -591,7 +1300,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b, for (uint i = 0; i < l; ++i) { Primitive val; val.setRawValue(read(src, i*srcElementSize)); - write(scope.engine, dest, i*elementSize, val); + write(dest, i*elementSize, val); } if (srcCopy) @@ -600,6 +1309,53 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b, RETURN_UNDEFINED(); } +ReturnedValue IntrinsicTypedArrayPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<TypedArray> instance(scope, thisObject); + if (!instance || instance->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + uint len = instance->length(); + + double s = (argc ? argv[0] : Primitive::undefinedValue()).toInteger(); + uint start; + if (s < 0) + start = (uint)qMax(len + s, 0.); + else if (s > len) + start = len; + else + start = (uint) s; + uint end = len; + if (argc > 1 && !argv[1].isUndefined()) { + double e = argv[1].toInteger(); + if (e < 0) + end = (uint)qMax(len + e, 0.); + else if (e > len) + end = len; + else + end = (uint) e; + } + uint count = start > end ? 0 : end - start; + + TypedArray *a = typedArraySpeciesCreate(scope, instance, count); + if (!a) + return Encode::undefined(); + + ScopedValue v(scope); + uint n = 0; + for (uint i = start; i < end; ++i) { + if (instance->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + v = instance->get(i); + if (a->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + a->put(n, v); + ++n; + } + return a->asReturnedValue(); +} + ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) { Scope scope(builtin); @@ -609,8 +1365,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject return scope.engine->throwTypeError(); Scoped<ArrayBuffer> buffer(scope, a->d()->buffer); - if (!buffer || buffer->isDetachedBuffer()) - return scope.engine->throwTypeError(); + Q_ASSERT(buffer); int len = a->length(); double b = argc > 0 ? argv[0].toInteger() : 0; @@ -630,7 +1385,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject int newLen = end - begin; - ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor())); + ScopedFunctionObject constructor(scope, a->speciesConstructor(scope, scope.engine->typedArrayCtors + a->d()->arrayType)); if (!constructor) return scope.engine->throwTypeError(); @@ -638,7 +1393,42 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject arguments[0] = buffer; arguments[1] = Encode(a->d()->byteOffset + begin*a->d()->type->bytesPerElement); arguments[2] = Encode(newLen); - return constructor->callAsConstructor(arguments, 3); + a = constructor->callAsConstructor(arguments, 3); + if (!a || a->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + return a->asReturnedValue(); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<TypedArray> instance(scope, thisObject); + if (!instance || instance->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + + uint len = instance->length(); + const QString separator = QStringLiteral(","); + + QString R; + + ScopedValue v(scope); + ScopedString s(scope); + + for (uint k = 0; k < len; ++k) { + if (instance->d()->buffer->isDetachedBuffer()) + return scope.engine->throwTypeError(); + if (k) + R += separator; + + v = instance->get(k); + v = Runtime::method_callElement(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0); + s = v->toString(scope.engine); + if (scope.hasException()) + return Encode::undefined(); + + R += s->toQString(); + } + return scope.engine->newString(R)->asReturnedValue(); } ReturnedValue IntrinsicTypedArrayPrototype::method_get_toStringTag(const FunctionObject *, const Value *thisObject, const Value *, int) @@ -650,20 +1440,51 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_get_toStringTag(const Functio return a->engine()->newString(QString::fromLatin1(a->d()->type->name))->asReturnedValue(); } -ReturnedValue IntrinsicTypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) +static bool validateTypedArray(const Object *o) { - return f->engine()->throwTypeError(); + const TypedArray *a = o->as<TypedArray>(); + if (!a) + return false; + if (a->d()->buffer->isDetachedBuffer()) + return false; + return true; } -ReturnedValue IntrinsicTypedArrayCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int) +ReturnedValue IntrinsicTypedArrayCtor::method_of(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) { - return f->engine()->throwTypeError(); + Scope scope(f); + int len = argc; + const Value *items = argv; + const FunctionObject *C = thisObject->as<FunctionObject>(); + if (!C || !C->isConstructor()) + return scope.engine->throwTypeError(); + + Value lenValue = Primitive::fromInt32(len); + ScopedObject newObj(scope, C->callAsConstructor(&lenValue, 1)); + if (scope.hasException()) + return Encode::undefined(); + if (!::validateTypedArray(newObj)) + return scope.engine->throwTypeError(); + TypedArray *a = newObj->as<TypedArray>(); + Q_ASSERT(a); + if (a->length() < static_cast<uint>(len)) + return scope.engine->throwTypeError(); + + for (int k = 0; k < len; ++k) { + newObj->put(PropertyKey::fromArrayIndex(k), items[k]); + } + return newObj->asReturnedValue(); } void IntrinsicTypedArrayPrototype::init(ExecutionEngine *engine, IntrinsicTypedArrayCtor *ctor) { + Scope scope(engine); ctor->defineReadonlyProperty(engine->id_prototype(), *this); ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); + ScopedString s(scope, engine->newString(QStringLiteral("TypedArray"))); + ctor->defineReadonlyConfigurableProperty(engine->id_name(), s); + s = scope.engine->newString(QStringLiteral("of")); + ctor->defineDefaultProperty(s, IntrinsicTypedArrayCtor::method_of); ctor->addSymbolSpecies(); defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr); @@ -671,12 +1492,31 @@ void IntrinsicTypedArrayPrototype::init(ExecutionEngine *engine, IntrinsicTypedA defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr); defineAccessorProperty(QStringLiteral("length"), method_get_length, nullptr); + defineDefaultProperty(QStringLiteral("copyWithin"), method_copyWithin, 2); defineDefaultProperty(QStringLiteral("entries"), method_entries, 0); + defineDefaultProperty(QStringLiteral("every"), method_every, 1); + defineDefaultProperty(QStringLiteral("fill"), method_fill, 1); + defineDefaultProperty(QStringLiteral("filter"), method_filter, 1); + defineDefaultProperty(QStringLiteral("find"), method_find, 1); + defineDefaultProperty(QStringLiteral("findIndex"), method_findIndex, 1); + defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1); + defineDefaultProperty(QStringLiteral("includes"), method_includes, 1); + defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1); + defineDefaultProperty(QStringLiteral("join"), method_join, 1); defineDefaultProperty(QStringLiteral("keys"), method_keys, 0); + defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); + defineDefaultProperty(QStringLiteral("map"), method_map, 1); + defineDefaultProperty(QStringLiteral("reduce"), method_reduce, 1); + defineDefaultProperty(QStringLiteral("reduceRight"), method_reduceRight, 1); + defineDefaultProperty(QStringLiteral("reverse"), method_reverse, 0); + defineDefaultProperty(QStringLiteral("some"), method_some, 1); defineDefaultProperty(QStringLiteral("set"), method_set, 1); - defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 0); + defineDefaultProperty(QStringLiteral("slice"), method_slice, 2); + defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 2); + defineDefaultProperty(engine->id_toLocaleString(), method_toLocaleString, 0); + ScopedObject f(scope, engine->arrayPrototype()->get(engine->id_toString())); + defineDefaultProperty(engine->id_toString(), f); - Scope scope(engine); ScopedString valuesString(scope, engine->newIdentifier(QStringLiteral("values"))); ScopedObject values(scope, FunctionObject::createBuiltinFunction(engine, valuesString, method_values, 0)); defineDefaultProperty(QStringLiteral("values"), values); diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index 967d3235b2..11b3a0dabf 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -61,7 +61,7 @@ namespace QV4 { struct ArrayBuffer; typedef ReturnedValue (*TypedArrayRead)(const char *data, int index); -typedef void (*TypedArrayWrite)(ExecutionEngine *engine, char *data, int index, const Value &value); +typedef void (*TypedArrayWrite)(char *data, int index, const Value &value); struct TypedArrayOperations { int bytesPerElement; @@ -148,8 +148,9 @@ struct IntrinsicTypedArrayCtor: FunctionObject { V4_OBJECT2(IntrinsicTypedArrayCtor, FunctionObject) - static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); - static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static constexpr VTable::Call virtualCall = nullptr; + + static ReturnedValue method_of(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; struct TypedArrayCtor: FunctionObject @@ -172,11 +173,29 @@ struct IntrinsicTypedArrayPrototype : Object static ReturnedValue method_get_byteOffset(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_get_length(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_copyWithin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_every(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_fill(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_filter(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_find(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_findIndex(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_includes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_indexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_join(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_lastIndexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_map(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_reduce(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_reduceRight(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_reverse(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_some(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_slice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_subarray(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_get_toStringTag(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 6a6df3eb6d..e4be6b99dd 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -408,6 +408,7 @@ public: } static bool toBooleanImpl(Value val); double toInteger() const; + inline ReturnedValue convertedToNumber() const; inline double toNumber() const; static double toNumberImpl(Value v); double toNumberImpl() const { return toNumberImpl(*this); } @@ -554,6 +555,15 @@ inline double Value::toNumber() const return toNumberImpl(); } +inline ReturnedValue Value::convertedToNumber() const +{ + if (isInteger() || isDouble()) + return asReturnedValue(); + Value v; + v.setDouble(toNumberImpl()); + return v.asReturnedValue(); +} + inline ReturnedValue Heap::Base::asReturnedValue() const { diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 575cad70e4..aed3fce6b1 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -949,8 +949,8 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(ConvertThisToObject) MOTH_BEGIN_INSTR(LoadSuperConstructor) - const Value *f = &stack[CallData::Function]; - if (!f->isFunctionObject()) { + const FunctionObject *f = stack[CallData::Function].as<FunctionObject>(); + if (!f || !f->isConstructor()) { engine->throwTypeError(); } else { acc = static_cast<const Object *>(f)->getPrototypeOf()->asReturnedValue(); diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp index 42c72e0447..f9f13e7b7c 100644 --- a/src/qml/qml/qqmllocale.cpp +++ b/src/qml/qml/qqmllocale.cpp @@ -77,7 +77,7 @@ static bool isLocaleObject(const QV4::Value &val) void QQmlDateExtension::registerExtension(QV4::ExecutionEngine *engine) { - engine->datePrototype()->defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString); + engine->datePrototype()->defineDefaultProperty(engine->id_toLocaleString(), method_toLocaleString); engine->datePrototype()->defineDefaultProperty(QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString); engine->datePrototype()->defineDefaultProperty(QStringLiteral("toLocaleDateString"), method_toLocaleDateString); engine->dateCtor()->defineDefaultProperty(QStringLiteral("fromLocaleString"), method_fromLocaleString); @@ -360,7 +360,7 @@ ReturnedValue QQmlDateExtension::method_timeZoneUpdated(const QV4::FunctionObjec void QQmlNumberExtension::registerExtension(QV4::ExecutionEngine *engine) { - engine->numberPrototype()->defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString); + engine->numberPrototype()->defineDefaultProperty(engine->id_toLocaleString(), method_toLocaleString); engine->numberPrototype()->defineDefaultProperty(QStringLiteral("toLocaleCurrencyString"), method_toLocaleCurrencyString); engine->numberCtor()->defineDefaultProperty(QStringLiteral("fromLocaleString"), method_fromLocaleString); } diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 75241ab99c..2c22d255a4 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -503,9 +503,11 @@ QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQm d->extraData.cd->extMetaObject = type.extensionMetaObject; // Check if the user wants only scoped enum classes - auto indexOfClassInfo = metaObject()->indexOfClassInfo("RegisterEnumClassesUnscoped"); - if (indexOfClassInfo != -1 && QString::fromUtf8(metaObject()->classInfo(indexOfClassInfo).value()) == QLatin1String("false")) - d->extraData.cd->registerEnumClassesUnscoped = false; + if (d->baseMetaObject) { + auto indexOfClassInfo = d->baseMetaObject->indexOfClassInfo("RegisterEnumClassesUnscoped"); + if (indexOfClassInfo != -1 && QString::fromUtf8(d->baseMetaObject->classInfo(indexOfClassInfo).value()) == QLatin1String("false")) + d->extraData.cd->registerEnumClassesUnscoped = false; + } } QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterCompositeType &type) diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 8ae5ea64a7..85045be411 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1847,8 +1847,8 @@ void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity) d->vData.velocity = yVelocity; d->hData.vTime = d->vData.vTime = d->timeline.time(); - bool flickedX = d->flickX(xVelocity); - bool flickedY = d->flickY(yVelocity); + const bool flickedX = xflick() && !qFuzzyIsNull(xVelocity) && d->flickX(xVelocity); + const bool flickedY = yflick() && !qFuzzyIsNull(yVelocity) && d->flickY(yVelocity); if (flickedX) d->hMoved = true; diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index b518ddd930..9208ea1f23 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -805,7 +805,7 @@ void QQuickTableViewPrivate::cancelLoadRequest() loadRequest.markAsDone(); model->cancel(modelIndexAtCell(loadRequest.currentCell())); - if (rebuildState == RebuildState::NotStarted) { + if (rebuildScheduled) { // No reason to rollback already loaded edge items // since we anyway are about to reload all items. return; @@ -929,7 +929,13 @@ void QQuickTableViewPrivate::beginRebuildTable() { Q_Q(QQuickTableView); + rebuildScheduled = false; + + if (loadRequest.isActive()) + cancelLoadRequest(); + releaseLoadedItems(); + loadedTable = QRect(); loadedTableOuterRect = QRect(); loadedTableInnerRect = QRect(); @@ -1079,9 +1085,7 @@ void QQuickTableViewPrivate::drainReusePoolAfterLoadRequest() } void QQuickTableViewPrivate::invalidateTable() { - rebuildState = RebuildState::NotStarted; - if (loadRequest.isActive()) - cancelLoadRequest(); + rebuildScheduled = true; q_func()->polish(); } @@ -1108,6 +1112,11 @@ void QQuickTableViewPrivate::updatePolish() return; } + if (rebuildState != RebuildState::Done) { + processRebuildTable(); + return; + } + // viewportRect describes the part of the content view that is actually visible. Since a // negative width/height can happen (e.g during start-up), we check for this to avoid rebuilding // the table (and e.g calculate initial row/column sizes) based on a premature viewport rect. @@ -1115,7 +1124,8 @@ void QQuickTableViewPrivate::updatePolish() if (!viewportRect.isValid()) return; - if (rebuildState != RebuildState::Done) { + if (rebuildScheduled) { + rebuildState = RebuildState::Begin; processRebuildTable(); return; } @@ -1560,8 +1570,7 @@ qreal QQuickTableView::explicitContentWidth() const { Q_D(const QQuickTableView); - if (d->rebuildState == QQuickTableViewPrivate::RebuildState::NotStarted - && d->explicitContentWidth.isNull) { + if (d->rebuildScheduled && d->explicitContentWidth.isNull) { // The table is pending to be rebuilt. Since we don't // know the contentWidth before this is done, we do the // rebuild now, instead of waiting for the polish event. @@ -1585,8 +1594,7 @@ qreal QQuickTableView::explicitContentHeight() const { Q_D(const QQuickTableView); - if (d->rebuildState == QQuickTableViewPrivate::RebuildState::NotStarted - && d->explicitContentHeight.isNull) { + if (d->rebuildScheduled && d->explicitContentHeight.isNull) { // The table is pending to be rebuilt. Since we don't // know the contentHeight before this is done, we do the // rebuild now, instead of waiting for the polish event. diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h index 53fd936195..1ba18480ba 100644 --- a/src/quick/items/qquicktableview_p_p.h +++ b/src/quick/items/qquicktableview_p_p.h @@ -164,7 +164,7 @@ public: }; enum class RebuildState { - NotStarted = 0, + Begin = 0, LoadInitalTable, VerifyTable, LayoutTable, @@ -208,7 +208,7 @@ public: QSize tableSize; - RebuildState rebuildState = RebuildState::NotStarted; + RebuildState rebuildState = RebuildState::Done; TableEdgeLoadRequest loadRequest; QPoint contentSizeBenchMarkPoint = QPoint(-1, -1); @@ -221,6 +221,7 @@ public: bool columnRowPositionsInvalid = false; bool layoutWarningIssued = false; bool polishing = false; + bool rebuildScheduled = true; QJSValue rowHeightProvider; QJSValue columnWidthProvider; diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations index 407c2d2ab4..106b16f64a 100644 --- a/tests/auto/qml/ecmascripttests/TestExpectations +++ b/tests/auto/qml/ecmascripttests/TestExpectations @@ -26,7 +26,6 @@ language/module-code/namespace/internals/define-own-property.js strictFails language/module-code/namespace/internals/set.js strictFails # ----- test failures that should be fixed -built-ins/Array/from/iter-cstm-ctor-err.js fails built-ins/Array/from/proto-from-ctor-realm.js fails built-ins/Array/isArray/proxy-revoked.js fails built-ins/Array/isArray/proxy.js fails @@ -108,8 +107,6 @@ built-ins/Array/prototype/splice/create-species.js fails built-ins/Array/prototype/splice/length-and-deleteCount-exceeding-integer-limit.js fails built-ins/Array/prototype/splice/length-exceeding-integer-limit-shrink-array.js fails built-ins/Array/prototype/splice/length-near-integer-limit-grow-array.js fails -built-ins/Array/prototype/toLocaleString/S15.4.4.3_A1_T1.js fails -built-ins/Array/prototype/toLocaleString/S15.4.4.3_A3_T1.js fails built-ins/Array/prototype/toLocaleString/primitive_this_value.js strictFails built-ins/Array/prototype/toLocaleString/primitive_this_value_getter.js strictFails built-ins/Array/prototype/unshift/clamps-to-integer-limit.js fails @@ -118,24 +115,7 @@ built-ins/Array/prototype/unshift/throws-if-integer-limit-exceeded.js fails built-ins/ArrayBuffer/data-allocation-after-object-creation.js fails built-ins/ArrayBuffer/proto-from-ctor-realm.js fails built-ins/ArrayBuffer/prototype-from-newtarget.js fails -built-ins/ArrayBuffer/prototype/slice/end-default-if-absent.js fails -built-ins/ArrayBuffer/prototype/slice/end-default-if-undefined.js fails -built-ins/ArrayBuffer/prototype/slice/end-exceeds-length.js fails -built-ins/ArrayBuffer/prototype/slice/negative-end.js fails -built-ins/ArrayBuffer/prototype/slice/negative-start.js fails built-ins/ArrayBuffer/prototype/slice/nonconstructor.js fails -built-ins/ArrayBuffer/prototype/slice/species-constructor-is-undefined.js fails -built-ins/ArrayBuffer/prototype/slice/species-is-null.js fails -built-ins/ArrayBuffer/prototype/slice/species-is-undefined.js fails -built-ins/ArrayBuffer/prototype/slice/species-returns-larger-arraybuffer.js fails -built-ins/ArrayBuffer/prototype/slice/species.js fails -built-ins/ArrayBuffer/prototype/slice/start-default-if-absent.js fails -built-ins/ArrayBuffer/prototype/slice/start-default-if-undefined.js fails -built-ins/ArrayBuffer/prototype/slice/start-exceeds-end.js fails -built-ins/ArrayBuffer/prototype/slice/start-exceeds-length.js fails -built-ins/ArrayBuffer/prototype/slice/tointeger-conversion-end.js fails -built-ins/ArrayBuffer/prototype/slice/tointeger-conversion-start.js fails -built-ins/ArrayBuffer/undefined-newtarget-throws.js fails built-ins/ArrayIteratorPrototype/next/detach-typedarray-in-progress.js fails built-ins/AsyncFunction/AsyncFunction-construct.js fails built-ins/AsyncFunction/AsyncFunction-is-extensible.js fails @@ -235,94 +215,7 @@ built-ins/Atomics/xor/shared-nonint-views.js fails built-ins/Boolean/proto-from-ctor-realm.js fails built-ins/DataView/custom-proto-access-throws.js fails built-ins/DataView/custom-proto-if-object-is-used.js fails -built-ins/DataView/detached-buffer.js fails -built-ins/DataView/length.js fails -built-ins/DataView/newtarget-undefined-throws.js fails built-ins/DataView/proto-from-ctor-realm.js fails -built-ins/DataView/prototype/byteLength/detached-buffer.js fails -built-ins/DataView/prototype/byteOffset/detached-buffer.js fails -built-ins/DataView/prototype/getFloat32/detached-buffer-after-toindex-byteoffset.js fails -built-ins/DataView/prototype/getFloat32/index-is-out-of-range.js fails -built-ins/DataView/prototype/getFloat32/negative-byteoffset-throws.js fails -built-ins/DataView/prototype/getFloat32/toindex-byteoffset.js fails -built-ins/DataView/prototype/getFloat64/detached-buffer-after-toindex-byteoffset.js fails -built-ins/DataView/prototype/getFloat64/index-is-out-of-range.js fails -built-ins/DataView/prototype/getFloat64/negative-byteoffset-throws.js fails -built-ins/DataView/prototype/getFloat64/toindex-byteoffset.js fails -built-ins/DataView/prototype/getInt16/detached-buffer-after-toindex-byteoffset.js fails -built-ins/DataView/prototype/getInt16/index-is-out-of-range.js fails -built-ins/DataView/prototype/getInt16/negative-byteoffset-throws.js fails -built-ins/DataView/prototype/getInt16/toindex-byteoffset.js fails -built-ins/DataView/prototype/getInt32/detached-buffer-after-toindex-byteoffset.js fails -built-ins/DataView/prototype/getInt32/index-is-out-of-range.js fails -built-ins/DataView/prototype/getInt32/negative-byteoffset-throws.js fails -built-ins/DataView/prototype/getInt32/toindex-byteoffset.js fails -built-ins/DataView/prototype/getInt8/detached-buffer-after-toindex-byteoffset.js fails -built-ins/DataView/prototype/getInt8/index-is-out-of-range.js fails -built-ins/DataView/prototype/getInt8/negative-byteoffset-throws.js fails -built-ins/DataView/prototype/getInt8/toindex-byteoffset.js fails -built-ins/DataView/prototype/getUint16/detached-buffer-after-toindex-byteoffset.js fails -built-ins/DataView/prototype/getUint16/index-is-out-of-range.js fails -built-ins/DataView/prototype/getUint16/negative-byteoffset-throws.js fails -built-ins/DataView/prototype/getUint16/toindex-byteoffset.js fails -built-ins/DataView/prototype/getUint32/detached-buffer-after-toindex-byteoffset.js fails -built-ins/DataView/prototype/getUint32/index-is-out-of-range.js fails -built-ins/DataView/prototype/getUint32/negative-byteoffset-throws.js fails -built-ins/DataView/prototype/getUint32/toindex-byteoffset.js fails -built-ins/DataView/prototype/getUint8/detached-buffer-after-toindex-byteoffset.js fails -built-ins/DataView/prototype/getUint8/index-is-out-of-range.js fails -built-ins/DataView/prototype/getUint8/negative-byteoffset-throws.js fails -built-ins/DataView/prototype/getUint8/toindex-byteoffset.js fails -built-ins/DataView/prototype/setFloat32/detached-buffer-after-toindex-byteoffset.js fails -built-ins/DataView/prototype/setFloat32/index-check-before-value-conversion.js fails -built-ins/DataView/prototype/setFloat32/index-is-out-of-range.js fails -built-ins/DataView/prototype/setFloat32/negative-byteoffset-throws.js fails -built-ins/DataView/prototype/setFloat32/range-check-after-value-conversion.js fails -built-ins/DataView/prototype/setFloat32/toindex-byteoffset.js fails -built-ins/DataView/prototype/setFloat64/detached-buffer-after-toindex-byteoffset.js fails -built-ins/DataView/prototype/setFloat64/index-check-before-value-conversion.js fails -built-ins/DataView/prototype/setFloat64/index-is-out-of-range.js fails -built-ins/DataView/prototype/setFloat64/negative-byteoffset-throws.js fails -built-ins/DataView/prototype/setFloat64/range-check-after-value-conversion.js fails -built-ins/DataView/prototype/setFloat64/toindex-byteoffset.js fails -built-ins/DataView/prototype/setInt16/detached-buffer-after-toindex-byteoffset.js fails -built-ins/DataView/prototype/setInt16/index-check-before-value-conversion.js fails -built-ins/DataView/prototype/setInt16/index-is-out-of-range.js fails -built-ins/DataView/prototype/setInt16/negative-byteoffset-throws.js fails -built-ins/DataView/prototype/setInt16/range-check-after-value-conversion.js fails -built-ins/DataView/prototype/setInt16/toindex-byteoffset.js fails -built-ins/DataView/prototype/setInt32/detached-buffer-after-toindex-byteoffset.js fails -built-ins/DataView/prototype/setInt32/index-check-before-value-conversion.js fails -built-ins/DataView/prototype/setInt32/index-is-out-of-range.js fails -built-ins/DataView/prototype/setInt32/negative-byteoffset-throws.js fails -built-ins/DataView/prototype/setInt32/range-check-after-value-conversion.js fails -built-ins/DataView/prototype/setInt32/toindex-byteoffset.js fails -built-ins/DataView/prototype/setInt8/detached-buffer-after-toindex-byteoffset.js fails -built-ins/DataView/prototype/setInt8/index-check-before-value-conversion.js fails -built-ins/DataView/prototype/setInt8/index-is-out-of-range.js fails -built-ins/DataView/prototype/setInt8/negative-byteoffset-throws.js fails -built-ins/DataView/prototype/setInt8/range-check-after-value-conversion.js fails -built-ins/DataView/prototype/setInt8/toindex-byteoffset.js fails -built-ins/DataView/prototype/setUint16/detached-buffer-after-toindex-byteoffset.js fails -built-ins/DataView/prototype/setUint16/index-check-before-value-conversion.js fails -built-ins/DataView/prototype/setUint16/index-is-out-of-range.js fails -built-ins/DataView/prototype/setUint16/negative-byteoffset-throws.js fails -built-ins/DataView/prototype/setUint16/range-check-after-value-conversion.js fails -built-ins/DataView/prototype/setUint16/toindex-byteoffset.js fails -built-ins/DataView/prototype/setUint32/detached-buffer-after-toindex-byteoffset.js fails -built-ins/DataView/prototype/setUint32/index-check-before-value-conversion.js fails -built-ins/DataView/prototype/setUint32/index-is-out-of-range.js fails -built-ins/DataView/prototype/setUint32/negative-byteoffset-throws.js fails -built-ins/DataView/prototype/setUint32/range-check-after-value-conversion.js fails -built-ins/DataView/prototype/setUint32/toindex-byteoffset.js fails -built-ins/DataView/prototype/setUint8/detached-buffer-after-toindex-byteoffset.js fails -built-ins/DataView/prototype/setUint8/index-check-before-value-conversion.js fails -built-ins/DataView/prototype/setUint8/index-is-out-of-range.js fails -built-ins/DataView/prototype/setUint8/negative-byteoffset-throws.js fails -built-ins/DataView/prototype/setUint8/range-check-after-value-conversion.js fails -built-ins/DataView/prototype/setUint8/toindex-byteoffset.js fails -built-ins/DataView/toindex-bytelength.js fails -built-ins/DataView/toindex-byteoffset.js fails built-ins/Date/UTC/infinity-make-day.js fails built-ins/Date/UTC/nans.js fails built-ins/Date/UTC/no-arg.js fails @@ -369,7 +262,6 @@ built-ins/Function/prototype/toString/async-method-class-statement.js fails built-ins/Function/prototype/toString/async-method-object.js fails built-ins/Function/prototype/toString/intrinsics.js fails built-ins/Function/prototype/toString/method-computed-property-name.js fails -built-ins/Function/prototype/toString/proxy.js fails built-ins/GeneratorFunction/proto-from-ctor-realm.js fails built-ins/JSON/parse/revived-proxy-revoked.js fails built-ins/JSON/parse/revived-proxy.js fails @@ -590,24 +482,10 @@ built-ins/Promise/resolve/prop-desc.js fails built-ins/Promise/resolve/resolve-from-promise-capability.js fails built-ins/Promise/resolve/resolve-prms-cstm-then.js fails built-ins/Proxy/apply/arguments-realm.js fails -built-ins/Proxy/apply/call-parameters.js fails -built-ins/Proxy/apply/call-result.js fails -built-ins/Proxy/apply/return-abrupt.js fails built-ins/Proxy/apply/trap-is-not-callable-realm.js fails -built-ins/Proxy/apply/trap-is-null.js fails -built-ins/Proxy/apply/trap-is-undefined-no-property.js fails -built-ins/Proxy/apply/trap-is-undefined.js fails built-ins/Proxy/construct/arguments-realm.js fails -built-ins/Proxy/construct/call-parameters-new-target.js fails -built-ins/Proxy/construct/call-parameters.js fails -built-ins/Proxy/construct/call-result.js fails -built-ins/Proxy/construct/return-is-abrupt.js fails built-ins/Proxy/construct/trap-is-not-callable-realm.js fails -built-ins/Proxy/construct/trap-is-null.js fails -built-ins/Proxy/construct/trap-is-undefined-no-property.js fails built-ins/Proxy/construct/trap-is-undefined-proto-from-ctor-realm.js fails -built-ins/Proxy/construct/trap-is-undefined.js fails -built-ins/Proxy/create-target-is-not-constructor.js fails built-ins/Proxy/defineProperty/desc-realm.js fails built-ins/Proxy/defineProperty/null-handler-realm.js fails built-ins/Proxy/defineProperty/targetdesc-configurable-desc-not-configurable-realm.js fails @@ -648,8 +526,6 @@ built-ins/Proxy/revocable/revocation-function-nonconstructor.js fails built-ins/Proxy/set/trap-is-not-callable-realm.js fails built-ins/Proxy/set/trap-is-undefined-receiver.js fails built-ins/Proxy/setPrototypeOf/trap-is-not-callable-realm.js fails -built-ins/Reflect/construct/newtarget-is-not-constructor-throws.js fails -built-ins/Reflect/construct/return-with-newtarget-argument.js fails built-ins/Reflect/set/creates-a-data-descriptor.js fails built-ins/Reflect/set/different-property-descriptors.js fails built-ins/Reflect/set/receiver-is-not-object.js fails @@ -787,334 +663,9 @@ built-ins/TypedArray/from/iter-next-value-error.js fails built-ins/TypedArray/from/length.js fails built-ins/TypedArray/from/name.js fails built-ins/TypedArray/from/prop-desc.js fails -built-ins/TypedArray/name.js fails -built-ins/TypedArray/of/length.js fails -built-ins/TypedArray/of/name.js fails -built-ins/TypedArray/of/prop-desc.js fails -built-ins/TypedArray/prototype/byteLength/detached-buffer.js fails -built-ins/TypedArray/prototype/byteOffset/detached-buffer.js fails built-ins/TypedArray/prototype/constructor.js fails -built-ins/TypedArray/prototype/copyWithin/bit-precision.js fails -built-ins/TypedArray/prototype/copyWithin/coerced-values-end.js fails -built-ins/TypedArray/prototype/copyWithin/coerced-values-start.js fails -built-ins/TypedArray/prototype/copyWithin/coerced-values-target.js fails -built-ins/TypedArray/prototype/copyWithin/get-length-ignores-length-prop.js fails -built-ins/TypedArray/prototype/copyWithin/invoked-as-func.js fails -built-ins/TypedArray/prototype/copyWithin/invoked-as-method.js fails -built-ins/TypedArray/prototype/copyWithin/length.js fails -built-ins/TypedArray/prototype/copyWithin/name.js fails -built-ins/TypedArray/prototype/copyWithin/negative-end.js fails -built-ins/TypedArray/prototype/copyWithin/negative-out-of-bounds-end.js fails -built-ins/TypedArray/prototype/copyWithin/negative-out-of-bounds-start.js fails -built-ins/TypedArray/prototype/copyWithin/negative-out-of-bounds-target.js fails -built-ins/TypedArray/prototype/copyWithin/negative-start.js fails -built-ins/TypedArray/prototype/copyWithin/negative-target.js fails -built-ins/TypedArray/prototype/copyWithin/non-negative-out-of-bounds-end.js fails -built-ins/TypedArray/prototype/copyWithin/non-negative-out-of-bounds-target-and-start.js fails -built-ins/TypedArray/prototype/copyWithin/non-negative-target-and-start.js fails -built-ins/TypedArray/prototype/copyWithin/non-negative-target-start-and-end.js fails -built-ins/TypedArray/prototype/copyWithin/prop-desc.js fails -built-ins/TypedArray/prototype/copyWithin/return-abrupt-from-end.js fails -built-ins/TypedArray/prototype/copyWithin/return-abrupt-from-start.js fails -built-ins/TypedArray/prototype/copyWithin/return-abrupt-from-target.js fails -built-ins/TypedArray/prototype/copyWithin/return-this.js fails -built-ins/TypedArray/prototype/copyWithin/undefined-end.js fails -built-ins/TypedArray/prototype/entries/detached-buffer.js fails -built-ins/TypedArray/prototype/every/callbackfn-arguments-with-thisarg.js fails -built-ins/TypedArray/prototype/every/callbackfn-arguments-without-thisarg.js fails -built-ins/TypedArray/prototype/every/callbackfn-detachbuffer.js fails -built-ins/TypedArray/prototype/every/callbackfn-no-interaction-over-non-integer.js fails -built-ins/TypedArray/prototype/every/callbackfn-not-called-on-empty.js fails -built-ins/TypedArray/prototype/every/callbackfn-return-does-not-change-instance.js fails -built-ins/TypedArray/prototype/every/callbackfn-returns-abrupt.js fails -built-ins/TypedArray/prototype/every/callbackfn-set-value-during-interaction.js fails -built-ins/TypedArray/prototype/every/callbackfn-this.js fails -built-ins/TypedArray/prototype/every/get-length-uses-internal-arraylength.js fails -built-ins/TypedArray/prototype/every/invoked-as-func.js fails -built-ins/TypedArray/prototype/every/invoked-as-method.js fails -built-ins/TypedArray/prototype/every/length.js fails -built-ins/TypedArray/prototype/every/name.js fails -built-ins/TypedArray/prototype/every/prop-desc.js fails -built-ins/TypedArray/prototype/every/returns-false-if-any-cb-returns-false.js fails -built-ins/TypedArray/prototype/every/returns-true-if-every-cb-returns-true.js fails -built-ins/TypedArray/prototype/every/values-are-not-cached.js fails -built-ins/TypedArray/prototype/fill/coerced-indexes.js fails -built-ins/TypedArray/prototype/fill/fill-values-conversion-once.js fails built-ins/TypedArray/prototype/fill/fill-values-conversion-operations-consistent-nan.js fails -built-ins/TypedArray/prototype/fill/fill-values-conversion-operations.js fails -built-ins/TypedArray/prototype/fill/fill-values-custom-start-and-end.js fails -built-ins/TypedArray/prototype/fill/fill-values-non-numeric.js fails -built-ins/TypedArray/prototype/fill/fill-values-relative-end.js fails -built-ins/TypedArray/prototype/fill/fill-values-relative-start.js fails -built-ins/TypedArray/prototype/fill/fill-values.js fails -built-ins/TypedArray/prototype/fill/get-length-ignores-length-prop.js fails -built-ins/TypedArray/prototype/fill/invoked-as-func.js fails -built-ins/TypedArray/prototype/fill/invoked-as-method.js fails -built-ins/TypedArray/prototype/fill/length.js fails -built-ins/TypedArray/prototype/fill/name.js fails -built-ins/TypedArray/prototype/fill/prop-desc.js fails -built-ins/TypedArray/prototype/fill/return-abrupt-from-end.js fails -built-ins/TypedArray/prototype/fill/return-abrupt-from-set-value.js fails -built-ins/TypedArray/prototype/fill/return-abrupt-from-start.js fails -built-ins/TypedArray/prototype/fill/return-this.js fails -built-ins/TypedArray/prototype/filter/arraylength-internal.js fails -built-ins/TypedArray/prototype/filter/callbackfn-arguments-with-thisarg.js fails -built-ins/TypedArray/prototype/filter/callbackfn-arguments-without-thisarg.js fails -built-ins/TypedArray/prototype/filter/callbackfn-called-before-ctor.js fails -built-ins/TypedArray/prototype/filter/callbackfn-called-before-species.js fails -built-ins/TypedArray/prototype/filter/callbackfn-detachbuffer.js fails -built-ins/TypedArray/prototype/filter/callbackfn-no-iteration-over-non-integer.js fails -built-ins/TypedArray/prototype/filter/callbackfn-not-called-on-empty.js fails -built-ins/TypedArray/prototype/filter/callbackfn-return-does-not-change-instance.js fails -built-ins/TypedArray/prototype/filter/callbackfn-returns-abrupt.js fails -built-ins/TypedArray/prototype/filter/callbackfn-set-value-during-iteration.js fails -built-ins/TypedArray/prototype/filter/callbackfn-this.js fails -built-ins/TypedArray/prototype/filter/invoked-as-func.js fails -built-ins/TypedArray/prototype/filter/invoked-as-method.js fails -built-ins/TypedArray/prototype/filter/length.js fails -built-ins/TypedArray/prototype/filter/name.js fails -built-ins/TypedArray/prototype/filter/prop-desc.js fails -built-ins/TypedArray/prototype/filter/result-does-not-share-buffer.js fails -built-ins/TypedArray/prototype/filter/result-empty-callbackfn-returns-false.js fails -built-ins/TypedArray/prototype/filter/result-full-callbackfn-returns-true.js fails -built-ins/TypedArray/prototype/filter/speciesctor-get-ctor-abrupt.js fails -built-ins/TypedArray/prototype/filter/speciesctor-get-ctor-inherited.js fails -built-ins/TypedArray/prototype/filter/speciesctor-get-ctor.js fails -built-ins/TypedArray/prototype/filter/speciesctor-get-species-abrupt.js fails -built-ins/TypedArray/prototype/filter/speciesctor-get-species-custom-ctor-invocation.js fails -built-ins/TypedArray/prototype/filter/speciesctor-get-species-custom-ctor-length.js fails -built-ins/TypedArray/prototype/filter/speciesctor-get-species-custom-ctor-returns-another-instance.js fails -built-ins/TypedArray/prototype/filter/speciesctor-get-species-custom-ctor.js fails -built-ins/TypedArray/prototype/filter/speciesctor-get-species-use-default-ctor.js fails -built-ins/TypedArray/prototype/filter/speciesctor-get-species.js fails -built-ins/TypedArray/prototype/filter/values-are-not-cached.js fails -built-ins/TypedArray/prototype/filter/values-are-set.js fails -built-ins/TypedArray/prototype/find/get-length-ignores-length-prop.js fails -built-ins/TypedArray/prototype/find/invoked-as-func.js fails -built-ins/TypedArray/prototype/find/invoked-as-method.js fails -built-ins/TypedArray/prototype/find/length.js fails -built-ins/TypedArray/prototype/find/name.js fails -built-ins/TypedArray/prototype/find/predicate-call-changes-value.js fails -built-ins/TypedArray/prototype/find/predicate-call-parameters.js fails -built-ins/TypedArray/prototype/find/predicate-call-this-non-strict.js sloppyFails -built-ins/TypedArray/prototype/find/predicate-call-this-strict.js strictFails -built-ins/TypedArray/prototype/find/predicate-may-detach-buffer.js fails -built-ins/TypedArray/prototype/find/predicate-not-called-on-empty-array.js fails -built-ins/TypedArray/prototype/find/prop-desc.js fails -built-ins/TypedArray/prototype/find/return-abrupt-from-predicate-call.js fails -built-ins/TypedArray/prototype/find/return-found-value-predicate-result-is-true.js fails -built-ins/TypedArray/prototype/find/return-undefined-if-predicate-returns-false-value.js fails -built-ins/TypedArray/prototype/findIndex/get-length-ignores-length-prop.js fails -built-ins/TypedArray/prototype/findIndex/invoked-as-func.js fails -built-ins/TypedArray/prototype/findIndex/invoked-as-method.js fails -built-ins/TypedArray/prototype/findIndex/length.js fails -built-ins/TypedArray/prototype/findIndex/name.js fails -built-ins/TypedArray/prototype/findIndex/predicate-call-changes-value.js fails -built-ins/TypedArray/prototype/findIndex/predicate-call-parameters.js fails -built-ins/TypedArray/prototype/findIndex/predicate-call-this-non-strict.js sloppyFails -built-ins/TypedArray/prototype/findIndex/predicate-call-this-strict.js strictFails -built-ins/TypedArray/prototype/findIndex/predicate-may-detach-buffer.js fails -built-ins/TypedArray/prototype/findIndex/predicate-not-called-on-empty-array.js fails -built-ins/TypedArray/prototype/findIndex/prop-desc.js fails -built-ins/TypedArray/prototype/findIndex/return-abrupt-from-predicate-call.js fails -built-ins/TypedArray/prototype/findIndex/return-index-predicate-result-is-true.js fails -built-ins/TypedArray/prototype/findIndex/return-negative-one-if-predicate-returns-false-value.js fails -built-ins/TypedArray/prototype/forEach/arraylength-internal.js fails -built-ins/TypedArray/prototype/forEach/callbackfn-arguments-with-thisarg.js fails -built-ins/TypedArray/prototype/forEach/callbackfn-arguments-without-thisarg.js fails -built-ins/TypedArray/prototype/forEach/callbackfn-detachbuffer.js fails -built-ins/TypedArray/prototype/forEach/callbackfn-no-interaction-over-non-integer.js fails -built-ins/TypedArray/prototype/forEach/callbackfn-not-called-on-empty.js fails -built-ins/TypedArray/prototype/forEach/callbackfn-return-does-not-change-instance.js fails -built-ins/TypedArray/prototype/forEach/callbackfn-returns-abrupt.js fails -built-ins/TypedArray/prototype/forEach/callbackfn-set-value-during-interaction.js fails -built-ins/TypedArray/prototype/forEach/callbackfn-this.js fails -built-ins/TypedArray/prototype/forEach/invoked-as-func.js fails -built-ins/TypedArray/prototype/forEach/invoked-as-method.js fails -built-ins/TypedArray/prototype/forEach/length.js fails -built-ins/TypedArray/prototype/forEach/name.js fails -built-ins/TypedArray/prototype/forEach/prop-desc.js fails -built-ins/TypedArray/prototype/forEach/returns-undefined.js fails -built-ins/TypedArray/prototype/forEach/values-are-not-cached.js fails -built-ins/TypedArray/prototype/includes/fromIndex-equal-or-greater-length-returns-false.js fails -built-ins/TypedArray/prototype/includes/fromIndex-infinity.js fails -built-ins/TypedArray/prototype/includes/fromIndex-minus-zero.js fails -built-ins/TypedArray/prototype/includes/get-length-uses-internal-arraylength.js fails -built-ins/TypedArray/prototype/includes/invoked-as-func.js fails -built-ins/TypedArray/prototype/includes/invoked-as-method.js fails -built-ins/TypedArray/prototype/includes/length-zero-returns-false.js fails -built-ins/TypedArray/prototype/includes/length.js fails -built-ins/TypedArray/prototype/includes/name.js fails -built-ins/TypedArray/prototype/includes/prop-desc.js fails -built-ins/TypedArray/prototype/includes/return-abrupt-tointeger-fromindex.js fails -built-ins/TypedArray/prototype/includes/samevaluezero.js fails -built-ins/TypedArray/prototype/includes/search-found-returns-true.js fails -built-ins/TypedArray/prototype/includes/search-not-found-returns-false.js fails -built-ins/TypedArray/prototype/includes/tointeger-fromindex.js fails -built-ins/TypedArray/prototype/indexOf/fromIndex-equal-or-greater-length-returns-minus-one.js fails -built-ins/TypedArray/prototype/indexOf/fromIndex-infinity.js fails -built-ins/TypedArray/prototype/indexOf/fromIndex-minus-zero.js fails -built-ins/TypedArray/prototype/indexOf/get-length-uses-internal-arraylength.js fails -built-ins/TypedArray/prototype/indexOf/invoked-as-func.js fails -built-ins/TypedArray/prototype/indexOf/invoked-as-method.js fails -built-ins/TypedArray/prototype/indexOf/length-zero-returns-minus-one.js fails -built-ins/TypedArray/prototype/indexOf/length.js fails -built-ins/TypedArray/prototype/indexOf/name.js fails -built-ins/TypedArray/prototype/indexOf/prop-desc.js fails -built-ins/TypedArray/prototype/indexOf/return-abrupt-tointeger-fromindex.js fails -built-ins/TypedArray/prototype/indexOf/search-found-returns-index.js fails -built-ins/TypedArray/prototype/indexOf/search-not-found-returns-minus-one.js fails -built-ins/TypedArray/prototype/indexOf/strict-comparison.js fails -built-ins/TypedArray/prototype/indexOf/tointeger-fromindex.js fails -built-ins/TypedArray/prototype/join/custom-separator-result-from-tostring-on-each-simple-value.js fails -built-ins/TypedArray/prototype/join/custom-separator-result-from-tostring-on-each-value.js fails -built-ins/TypedArray/prototype/join/empty-instance-empty-string.js fails -built-ins/TypedArray/prototype/join/get-length-uses-internal-arraylength.js fails -built-ins/TypedArray/prototype/join/invoked-as-func.js fails -built-ins/TypedArray/prototype/join/invoked-as-method.js fails -built-ins/TypedArray/prototype/join/length.js fails -built-ins/TypedArray/prototype/join/name.js fails -built-ins/TypedArray/prototype/join/prop-desc.js fails -built-ins/TypedArray/prototype/join/result-from-tostring-on-each-simple-value.js fails -built-ins/TypedArray/prototype/join/result-from-tostring-on-each-value.js fails -built-ins/TypedArray/prototype/join/return-abrupt-from-separator.js fails -built-ins/TypedArray/prototype/keys/detached-buffer.js fails -built-ins/TypedArray/prototype/lastIndexOf/fromIndex-infinity.js fails -built-ins/TypedArray/prototype/lastIndexOf/fromIndex-minus-zero.js fails -built-ins/TypedArray/prototype/lastIndexOf/get-length-uses-internal-arraylength.js fails -built-ins/TypedArray/prototype/lastIndexOf/invoked-as-func.js fails -built-ins/TypedArray/prototype/lastIndexOf/invoked-as-method.js fails -built-ins/TypedArray/prototype/lastIndexOf/length-zero-returns-minus-one.js fails -built-ins/TypedArray/prototype/lastIndexOf/length.js fails -built-ins/TypedArray/prototype/lastIndexOf/name.js fails -built-ins/TypedArray/prototype/lastIndexOf/prop-desc.js fails -built-ins/TypedArray/prototype/lastIndexOf/return-abrupt-tointeger-fromindex.js fails -built-ins/TypedArray/prototype/lastIndexOf/search-found-returns-index.js fails -built-ins/TypedArray/prototype/lastIndexOf/search-not-found-returns-minus-one.js fails -built-ins/TypedArray/prototype/lastIndexOf/strict-comparison.js fails -built-ins/TypedArray/prototype/lastIndexOf/tointeger-fromindex.js fails -built-ins/TypedArray/prototype/length/detached-buffer.js fails -built-ins/TypedArray/prototype/map/arraylength-internal.js fails -built-ins/TypedArray/prototype/map/callbackfn-arguments-with-thisarg.js fails -built-ins/TypedArray/prototype/map/callbackfn-arguments-without-thisarg.js fails -built-ins/TypedArray/prototype/map/callbackfn-detachbuffer.js fails -built-ins/TypedArray/prototype/map/callbackfn-no-interaction-over-non-integer-properties.js fails -built-ins/TypedArray/prototype/map/callbackfn-not-called-on-empty.js fails -built-ins/TypedArray/prototype/map/callbackfn-return-affects-returned-object.js fails -built-ins/TypedArray/prototype/map/callbackfn-return-does-not-change-instance.js fails -built-ins/TypedArray/prototype/map/callbackfn-return-does-not-copy-non-integer-properties.js fails -built-ins/TypedArray/prototype/map/callbackfn-returns-abrupt.js fails -built-ins/TypedArray/prototype/map/callbackfn-set-value-during-interaction.js fails -built-ins/TypedArray/prototype/map/callbackfn-this.js fails -built-ins/TypedArray/prototype/map/invoked-as-func.js fails -built-ins/TypedArray/prototype/map/invoked-as-method.js fails -built-ins/TypedArray/prototype/map/length.js fails -built-ins/TypedArray/prototype/map/name.js fails -built-ins/TypedArray/prototype/map/prop-desc.js fails -built-ins/TypedArray/prototype/map/return-new-typedarray-conversion-operation-consistent-nan.js fails -built-ins/TypedArray/prototype/map/return-new-typedarray-conversion-operation.js fails -built-ins/TypedArray/prototype/map/return-new-typedarray-from-empty-length.js fails -built-ins/TypedArray/prototype/map/return-new-typedarray-from-positive-length.js fails -built-ins/TypedArray/prototype/map/values-are-not-cached.js fails -built-ins/TypedArray/prototype/reduce/callbackfn-arguments-custom-accumulator.js fails -built-ins/TypedArray/prototype/reduce/callbackfn-arguments-default-accumulator.js fails -built-ins/TypedArray/prototype/reduce/callbackfn-detachbuffer.js fails -built-ins/TypedArray/prototype/reduce/callbackfn-no-iteration-over-non-integer-properties.js fails -built-ins/TypedArray/prototype/reduce/callbackfn-not-called-on-empty.js fails -built-ins/TypedArray/prototype/reduce/callbackfn-return-does-not-change-instance.js fails -built-ins/TypedArray/prototype/reduce/callbackfn-returns-abrupt.js fails -built-ins/TypedArray/prototype/reduce/callbackfn-set-value-during-iteration.js fails -built-ins/TypedArray/prototype/reduce/callbackfn-this.js fails -built-ins/TypedArray/prototype/reduce/empty-instance-return-initialvalue.js fails -built-ins/TypedArray/prototype/reduce/get-length-uses-internal-arraylength.js fails -built-ins/TypedArray/prototype/reduce/invoked-as-func.js fails -built-ins/TypedArray/prototype/reduce/invoked-as-method.js fails -built-ins/TypedArray/prototype/reduce/length.js fails -built-ins/TypedArray/prototype/reduce/name.js fails -built-ins/TypedArray/prototype/reduce/prop-desc.js fails -built-ins/TypedArray/prototype/reduce/result-is-last-callbackfn-return.js fails -built-ins/TypedArray/prototype/reduce/result-of-any-type.js fails -built-ins/TypedArray/prototype/reduce/return-first-value-without-callbackfn.js fails -built-ins/TypedArray/prototype/reduce/values-are-not-cached.js fails -built-ins/TypedArray/prototype/reduceRight/callbackfn-arguments-custom-accumulator.js fails -built-ins/TypedArray/prototype/reduceRight/callbackfn-arguments-default-accumulator.js fails -built-ins/TypedArray/prototype/reduceRight/callbackfn-detachbuffer.js fails -built-ins/TypedArray/prototype/reduceRight/callbackfn-no-iteration-over-non-integer-properties.js fails -built-ins/TypedArray/prototype/reduceRight/callbackfn-not-called-on-empty.js fails -built-ins/TypedArray/prototype/reduceRight/callbackfn-return-does-not-change-instance.js fails -built-ins/TypedArray/prototype/reduceRight/callbackfn-returns-abrupt.js fails -built-ins/TypedArray/prototype/reduceRight/callbackfn-set-value-during-iteration.js fails -built-ins/TypedArray/prototype/reduceRight/callbackfn-this.js fails -built-ins/TypedArray/prototype/reduceRight/empty-instance-return-initialvalue.js fails -built-ins/TypedArray/prototype/reduceRight/get-length-uses-internal-arraylength.js fails -built-ins/TypedArray/prototype/reduceRight/invoked-as-func.js fails -built-ins/TypedArray/prototype/reduceRight/invoked-as-method.js fails -built-ins/TypedArray/prototype/reduceRight/length.js fails -built-ins/TypedArray/prototype/reduceRight/name.js fails -built-ins/TypedArray/prototype/reduceRight/prop-desc.js fails -built-ins/TypedArray/prototype/reduceRight/result-is-last-callbackfn-return.js fails -built-ins/TypedArray/prototype/reduceRight/result-of-any-type.js fails -built-ins/TypedArray/prototype/reduceRight/return-first-value-without-callbackfn.js fails -built-ins/TypedArray/prototype/reduceRight/values-are-not-cached.js fails -built-ins/TypedArray/prototype/reverse/get-length-uses-internal-arraylength.js fails -built-ins/TypedArray/prototype/reverse/invoked-as-func.js fails -built-ins/TypedArray/prototype/reverse/invoked-as-method.js fails -built-ins/TypedArray/prototype/reverse/length.js fails -built-ins/TypedArray/prototype/reverse/name.js fails -built-ins/TypedArray/prototype/reverse/preserves-non-numeric-properties.js fails -built-ins/TypedArray/prototype/reverse/prop-desc.js fails -built-ins/TypedArray/prototype/reverse/returns-original-object.js fails -built-ins/TypedArray/prototype/reverse/reverts.js fails -built-ins/TypedArray/prototype/set/array-arg-set-values-in-order.js fails -built-ins/TypedArray/prototype/set/array-arg-targetbuffer-detached-on-get-src-value-throws.js fails -built-ins/TypedArray/prototype/slice/arraylength-internal.js fails built-ins/TypedArray/prototype/slice/bit-precision.js fails -built-ins/TypedArray/prototype/slice/detached-buffer-zero-count-custom-ctor-other-targettype.js fails -built-ins/TypedArray/prototype/slice/detached-buffer-zero-count-custom-ctor-same-targettype.js fails -built-ins/TypedArray/prototype/slice/infinity.js fails -built-ins/TypedArray/prototype/slice/invoked-as-func.js fails -built-ins/TypedArray/prototype/slice/invoked-as-method.js fails -built-ins/TypedArray/prototype/slice/length.js fails -built-ins/TypedArray/prototype/slice/minus-zero.js fails -built-ins/TypedArray/prototype/slice/name.js fails -built-ins/TypedArray/prototype/slice/prop-desc.js fails -built-ins/TypedArray/prototype/slice/result-does-not-copy-ordinary-properties.js fails -built-ins/TypedArray/prototype/slice/results-with-different-length.js fails -built-ins/TypedArray/prototype/slice/results-with-empty-length.js fails -built-ins/TypedArray/prototype/slice/results-with-same-length.js fails -built-ins/TypedArray/prototype/slice/return-abrupt-from-end.js fails -built-ins/TypedArray/prototype/slice/return-abrupt-from-start.js fails -built-ins/TypedArray/prototype/slice/set-values-from-different-ctor-type.js fails -built-ins/TypedArray/prototype/slice/speciesctor-get-ctor-abrupt.js fails -built-ins/TypedArray/prototype/slice/speciesctor-get-ctor-inherited.js fails -built-ins/TypedArray/prototype/slice/speciesctor-get-ctor.js fails -built-ins/TypedArray/prototype/slice/speciesctor-get-species-abrupt.js fails -built-ins/TypedArray/prototype/slice/speciesctor-get-species-custom-ctor-invocation.js fails -built-ins/TypedArray/prototype/slice/speciesctor-get-species-custom-ctor-length.js fails -built-ins/TypedArray/prototype/slice/speciesctor-get-species-custom-ctor-returns-another-instance.js fails -built-ins/TypedArray/prototype/slice/speciesctor-get-species-custom-ctor.js fails -built-ins/TypedArray/prototype/slice/speciesctor-get-species-use-default-ctor.js fails -built-ins/TypedArray/prototype/slice/speciesctor-get-species.js fails -built-ins/TypedArray/prototype/slice/tointeger-end.js fails -built-ins/TypedArray/prototype/slice/tointeger-start.js fails -built-ins/TypedArray/prototype/some/callbackfn-arguments-with-thisarg.js fails -built-ins/TypedArray/prototype/some/callbackfn-arguments-without-thisarg.js fails -built-ins/TypedArray/prototype/some/callbackfn-detachbuffer.js fails -built-ins/TypedArray/prototype/some/callbackfn-no-interaction-over-non-integer.js fails -built-ins/TypedArray/prototype/some/callbackfn-not-called-on-empty.js fails -built-ins/TypedArray/prototype/some/callbackfn-return-does-not-change-instance.js fails -built-ins/TypedArray/prototype/some/callbackfn-returns-abrupt.js fails -built-ins/TypedArray/prototype/some/callbackfn-set-value-during-interaction.js fails -built-ins/TypedArray/prototype/some/callbackfn-this.js fails -built-ins/TypedArray/prototype/some/get-length-uses-internal-arraylength.js fails -built-ins/TypedArray/prototype/some/invoked-as-func.js fails -built-ins/TypedArray/prototype/some/invoked-as-method.js fails -built-ins/TypedArray/prototype/some/length.js fails -built-ins/TypedArray/prototype/some/name.js fails -built-ins/TypedArray/prototype/some/prop-desc.js fails -built-ins/TypedArray/prototype/some/returns-false-if-every-cb-returns-false.js fails -built-ins/TypedArray/prototype/some/returns-true-if-any-cb-returns-true.js fails -built-ins/TypedArray/prototype/some/values-are-not-cached.js fails built-ins/TypedArray/prototype/sort/arraylength-internal.js fails built-ins/TypedArray/prototype/sort/comparefn-call-throws.js fails built-ins/TypedArray/prototype/sort/comparefn-calls.js fails @@ -1128,35 +679,6 @@ built-ins/TypedArray/prototype/sort/return-same-instance.js fails built-ins/TypedArray/prototype/sort/sortcompare-with-no-tostring.js fails built-ins/TypedArray/prototype/sort/sorted-values-nan.js fails built-ins/TypedArray/prototype/sort/sorted-values.js fails -built-ins/TypedArray/prototype/subarray/detached-buffer.js fails -built-ins/TypedArray/prototype/subarray/length.js fails -built-ins/TypedArray/prototype/subarray/speciesctor-get-ctor-inherited.js fails -built-ins/TypedArray/prototype/subarray/speciesctor-get-ctor.js fails -built-ins/TypedArray/prototype/subarray/speciesctor-get-species-abrupt.js fails -built-ins/TypedArray/prototype/subarray/speciesctor-get-species-custom-ctor-invocation.js fails -built-ins/TypedArray/prototype/subarray/speciesctor-get-species-custom-ctor-returns-another-instance.js fails -built-ins/TypedArray/prototype/subarray/speciesctor-get-species-custom-ctor.js fails -built-ins/TypedArray/prototype/subarray/speciesctor-get-species-use-default-ctor.js fails -built-ins/TypedArray/prototype/subarray/speciesctor-get-species.js fails -built-ins/TypedArray/prototype/toLocaleString/calls-tolocalestring-from-each-value.js fails -built-ins/TypedArray/prototype/toLocaleString/calls-tostring-from-each-value.js fails -built-ins/TypedArray/prototype/toLocaleString/calls-valueof-from-each-value.js fails -built-ins/TypedArray/prototype/toLocaleString/detached-buffer.js fails -built-ins/TypedArray/prototype/toLocaleString/empty-instance-returns-empty-string.js fails -built-ins/TypedArray/prototype/toLocaleString/invoked-as-method.js fails -built-ins/TypedArray/prototype/toLocaleString/prop-desc.js fails -built-ins/TypedArray/prototype/toLocaleString/return-abrupt-from-firstelement-tolocalestring.js fails -built-ins/TypedArray/prototype/toLocaleString/return-abrupt-from-firstelement-tostring.js fails -built-ins/TypedArray/prototype/toLocaleString/return-abrupt-from-firstelement-valueof.js fails -built-ins/TypedArray/prototype/toLocaleString/return-abrupt-from-nextelement-tolocalestring.js fails -built-ins/TypedArray/prototype/toLocaleString/return-abrupt-from-nextelement-tostring.js fails -built-ins/TypedArray/prototype/toLocaleString/return-abrupt-from-nextelement-valueof.js fails -built-ins/TypedArray/prototype/toLocaleString/return-result.js fails -built-ins/TypedArray/prototype/toLocaleString/this-is-not-object.js fails -built-ins/TypedArray/prototype/toLocaleString/this-is-not-typedarray-instance.js fails -built-ins/TypedArray/prototype/toString.js fails -built-ins/TypedArray/prototype/toString/detached-buffer.js fails -built-ins/TypedArray/prototype/values/detached-buffer.js fails built-ins/TypedArrays/ctors/buffer-arg/custom-proto-access-throws.js fails built-ins/TypedArrays/ctors/buffer-arg/defined-negative-length.js fails built-ins/TypedArrays/ctors/buffer-arg/proto-from-ctor-realm.js fails @@ -1249,16 +771,7 @@ built-ins/TypedArrays/internals/Set/detached-buffer.js fails built-ins/TypedArrays/internals/Set/key-is-minus-zero.js fails built-ins/TypedArrays/internals/Set/key-is-not-integer.js fails built-ins/TypedArrays/internals/Set/key-is-out-of-bounds.js fails -built-ins/TypedArrays/internals/Set/tonumber-value-detached-buffer.js fails built-ins/TypedArrays/internals/Set/tonumber-value-throws.js strictFails -built-ins/TypedArrays/of/argument-number-value-throws.js fails -built-ins/TypedArrays/of/custom-ctor-returns-other-instance.js fails -built-ins/TypedArrays/of/custom-ctor.js fails -built-ins/TypedArrays/of/nan-conversion.js fails -built-ins/TypedArrays/of/new-instance-empty.js fails -built-ins/TypedArrays/of/new-instance-from-zero.js fails -built-ins/TypedArrays/of/new-instance-using-custom-ctor.js fails -built-ins/TypedArrays/of/new-instance.js fails built-ins/WeakMap/constructor.js fails built-ins/WeakMap/empty-iterable.js fails built-ins/WeakMap/get-set-method-failure.js fails @@ -1694,7 +1207,6 @@ language/expressions/generators/yield-identifier-non-strict.js sloppyFails language/expressions/generators/yield-star-before-newline.js fails language/expressions/logical-and/tco-right.js strictFails language/expressions/logical-or/tco-right.js strictFails -language/expressions/new.target/value-via-reflect-construct.js fails language/expressions/new.target/value-via-super-call.js fails language/expressions/new.target/value-via-super-property.js fails language/expressions/new/non-ctor-err-realm.js fails @@ -1960,7 +1472,6 @@ language/statements/class/subclass/builtin-objects/WeakSet/super-must-be-called. language/statements/class/subclass/builtins.js fails language/statements/class/subclass/class-definition-null-proto-super.js fails language/statements/class/subclass/class-definition-null-proto-this.js fails -language/statements/class/subclass/class-definition-superclass-generator.js fails language/statements/class/subclass/default-constructor-spread-override.js fails language/statements/class/super/in-methods.js fails language/statements/const/block-local-closure-get-before-initialization.js fails diff --git a/tests/auto/qml/ecmascripttests/test262 b/tests/auto/qml/ecmascripttests/test262 -Subproject 6b0c42c63c2492bd0a7a96d3179d122b5f71793 +Subproject 3c69133cc419840c1be34638039cd8c48a7ef58 diff --git a/tests/auto/quick/pointerhandlers/pointerhandlers.pro b/tests/auto/quick/pointerhandlers/pointerhandlers.pro index df9869315f..950d6835eb 100644 --- a/tests/auto/quick/pointerhandlers/pointerhandlers.pro +++ b/tests/auto/quick/pointerhandlers/pointerhandlers.pro @@ -4,10 +4,10 @@ qtConfig(private_tests) { SUBDIRS += \ flickableinterop \ multipointtoucharea_interop \ - qquickpointerhandler \ - qquickpointhandler \ qquickdraghandler \ + qquickhoverhandler \ qquickpinchhandler \ + qquickpointerhandler \ + qquickpointhandler \ qquicktaphandler \ } - diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/lesHoverables.qml b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/lesHoverables.qml new file mode 100644 index 0000000000..9045247e94 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/data/lesHoverables.qml @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 + +Rectangle { + id: root + width: 640 + height: 480 + color: "#444" + + Component { + id: buttonsAndStuff + Column { + anchors.fill: parent + anchors.margins: 8 + spacing: 8 + + Rectangle { + objectName: "buttonWithMA" + width: parent.width + height: 30 + color: buttonMA.pressed ? "lightsteelblue" : "#999" + border.color: buttonMA.containsMouse ? "cyan" : "transparent" + + MouseArea { + id: buttonMA + objectName: "buttonMA" + hoverEnabled: true + anchors.fill: parent + onClicked: console.log("clicked MA") + } + + Text { + anchors.centerIn: parent + text: "MouseArea" + } + } + + Rectangle { + objectName: "buttonWithHH" + width: parent.width + height: 30 + color: flash ? "#999" : "white" + border.color: buttonHH.hovered ? "cyan" : "transparent" + property bool flash: true + + HoverHandler { + id: buttonHH + objectName: "buttonHH" + acceptedDevices: PointerDevice.AllDevices + } + + TapHandler { } + + Text { + anchors.centerIn: parent + text: "HoverHandler" + } + } + } + } + + Rectangle { + id: paddle + objectName: "paddle" + width: 100 + height: 40 + color: paddleHH.hovered ? "indianred" : "#888" + x: (parent.width - width) / 2 + y: parent.height - 100 + radius: 10 + + HoverHandler { + id: paddleHH + objectName: "paddleHH" + } + } + + Rectangle { + objectName: "topSidebar" + radius: 5 + antialiasing: true + x: -radius + y: -radius + width: 120 + height: 200 + border.color: topSidebarHH.hovered ? "cyan" : "black" + color: "#777" + + Rectangle { + color: "cyan" + width: 10 + height: width + radius: width / 2 + visible: topSidebarHH.hovered + x: topSidebarHH.point.position.x - width / 2 + y: topSidebarHH.point.position.y - height / 2 + z: 100 + } + + HoverHandler { + id: topSidebarHH + objectName: "topSidebarHH" + } + + Loader { + objectName: "topSidebarLoader" + sourceComponent: buttonsAndStuff + anchors.fill: parent + } + } + + Rectangle { + objectName: "bottomSidebar" + radius: 5 + antialiasing: true + x: -radius + anchors.bottom: parent.bottom + anchors.bottomMargin: -radius + width: 120 + height: 200 + border.color: bottomSidebarMA.containsMouse ? "cyan" : "black" + color: "#777" + + MouseArea { + id: bottomSidebarMA + objectName: "bottomSidebarMA" + hoverEnabled: true + anchors.fill: parent + } + + Loader { + objectName: "bottomSidebarLoader" + sourceComponent: buttonsAndStuff + anchors.fill: parent + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/qquickhoverhandler.pro b/tests/auto/quick/pointerhandlers/qquickhoverhandler/qquickhoverhandler.pro new file mode 100644 index 0000000000..34633e2532 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/qquickhoverhandler.pro @@ -0,0 +1,15 @@ +CONFIG += testcase + +TARGET = tst_qquickhoverhandler +QT += core-private gui-private qml-private quick-private testlib + +macos:CONFIG -= app_bundle + +SOURCES += tst_qquickhoverhandler.cpp + +include (../../../shared/util.pri) +include (../../shared/util.pri) + +TESTDATA = data/* + +OTHER_FILES += data/lesHoverables.qml \ diff --git a/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp new file mode 100644 index 0000000000..52074aec4f --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickhoverhandler/tst_qquickhoverhandler.cpp @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QtQuick/qquickview.h> +#include <QtQuick/qquickitem.h> +#include <QtQuick/private/qquickhoverhandler_p.h> +#include <QtQuick/private/qquickmousearea_p.h> +#include <qpa/qwindowsysteminterface.h> + +#include <private/qquickwindow_p.h> + +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlproperty.h> + +#include "../../../shared/util.h" +#include "../../shared/viewtestutil.h" + +Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") + +static bool isPlatformWayland() +{ + return !QGuiApplication::platformName().compare(QLatin1String("wayland"), Qt::CaseInsensitive); +} + +class tst_HoverHandler : public QQmlDataTest +{ + Q_OBJECT +public: + tst_HoverHandler() + {} + +private slots: + void hoverHandlerAndUnderlyingHoverHandler(); + void mouseAreaAndUnderlyingHoverHandler(); + void hoverHandlerAndUnderlyingMouseArea(); + +private: + void createView(QScopedPointer<QQuickView> &window, const char *fileName); +}; + +void tst_HoverHandler::createView(QScopedPointer<QQuickView> &window, const char *fileName) +{ + window.reset(new QQuickView); + window->setSource(testFileUrl(fileName)); + QTRY_COMPARE(window->status(), QQuickView::Ready); + QQuickViewTestUtil::centerOnScreen(window.data()); + QQuickViewTestUtil::moveMouseAway(window.data()); + + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QVERIFY(window->rootObject() != nullptr); +} + +void tst_HoverHandler::hoverHandlerAndUnderlyingHoverHandler() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "lesHoverables.qml"); + QQuickView * window = windowPtr.data(); + QQuickItem * topSidebar = window->rootObject()->findChild<QQuickItem *>("topSidebar"); + QVERIFY(topSidebar); + QQuickItem * button = topSidebar->findChild<QQuickItem *>("buttonWithHH"); + QVERIFY(button); + QQuickHoverHandler *topSidebarHH = topSidebar->findChild<QQuickHoverHandler *>("topSidebarHH"); + QVERIFY(topSidebarHH); + QQuickHoverHandler *buttonHH = button->findChild<QQuickHoverHandler *>("buttonHH"); + QVERIFY(buttonHH); + + QPoint buttonCenter(button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint()); + QPoint rightOfButton(button->mapToScene(QPointF(button->width() + 2, button->height() / 2)).toPoint()); + QPoint outOfSidebar(topSidebar->mapToScene(QPointF(topSidebar->width() + 2, topSidebar->height() / 2)).toPoint()); + QSignalSpy sidebarHoveredSpy(topSidebarHH, SIGNAL(hoveredChanged())); + QSignalSpy buttonHoveredSpy(buttonHH, SIGNAL(hoveredChanged())); + + QTest::mouseMove(window, outOfSidebar); + QCOMPARE(topSidebarHH->isHovered(), false); + QCOMPARE(sidebarHoveredSpy.count(), 0); + QCOMPARE(buttonHH->isHovered(), false); + QCOMPARE(buttonHoveredSpy.count(), 0); + + QTest::mouseMove(window, rightOfButton); + QCOMPARE(topSidebarHH->isHovered(), true); + QCOMPARE(sidebarHoveredSpy.count(), 1); + QCOMPARE(buttonHH->isHovered(), false); + QCOMPARE(buttonHoveredSpy.count(), 0); + + QTest::mouseMove(window, buttonCenter); + QCOMPARE(topSidebarHH->isHovered(), true); + QCOMPARE(sidebarHoveredSpy.count(), 1); + QCOMPARE(buttonHH->isHovered(), true); + QCOMPARE(buttonHoveredSpy.count(), 1); + + QTest::mouseMove(window, rightOfButton); + QCOMPARE(topSidebarHH->isHovered(), true); + QCOMPARE(sidebarHoveredSpy.count(), 1); + QCOMPARE(buttonHH->isHovered(), false); + QCOMPARE(buttonHoveredSpy.count(), 2); + + QTest::mouseMove(window, outOfSidebar); + QCOMPARE(topSidebarHH->isHovered(), false); + QCOMPARE(sidebarHoveredSpy.count(), 2); + QCOMPARE(buttonHH->isHovered(), false); + QCOMPARE(buttonHoveredSpy.count(), 2); +} + +void tst_HoverHandler::mouseAreaAndUnderlyingHoverHandler() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "lesHoverables.qml"); + QQuickView * window = windowPtr.data(); + QQuickItem * topSidebar = window->rootObject()->findChild<QQuickItem *>("topSidebar"); + QVERIFY(topSidebar); + QQuickMouseArea * buttonMA = topSidebar->findChild<QQuickMouseArea *>("buttonMA"); + QVERIFY(buttonMA); + QQuickHoverHandler *topSidebarHH = topSidebar->findChild<QQuickHoverHandler *>("topSidebarHH"); + QVERIFY(topSidebarHH); + + QPoint buttonCenter(buttonMA->mapToScene(QPointF(buttonMA->width() / 2, buttonMA->height() / 2)).toPoint()); + QPoint rightOfButton(buttonMA->mapToScene(QPointF(buttonMA->width() + 2, buttonMA->height() / 2)).toPoint()); + QPoint outOfSidebar(topSidebar->mapToScene(QPointF(topSidebar->width() + 2, topSidebar->height() / 2)).toPoint()); + QSignalSpy sidebarHoveredSpy(topSidebarHH, SIGNAL(hoveredChanged())); + QSignalSpy buttonHoveredSpy(buttonMA, SIGNAL(hoveredChanged())); + + QTest::mouseMove(window, outOfSidebar); + QCOMPARE(topSidebarHH->isHovered(), false); + QCOMPARE(sidebarHoveredSpy.count(), 0); + QCOMPARE(buttonMA->hovered(), false); + QCOMPARE(buttonHoveredSpy.count(), 0); + + QTest::mouseMove(window, rightOfButton); + QCOMPARE(topSidebarHH->isHovered(), true); + QCOMPARE(sidebarHoveredSpy.count(), 1); + QCOMPARE(buttonMA->hovered(), false); + QCOMPARE(buttonHoveredSpy.count(), 0); + + QTest::mouseMove(window, buttonCenter); + QCOMPARE(topSidebarHH->isHovered(), true); + QCOMPARE(sidebarHoveredSpy.count(), 1); + QCOMPARE(buttonMA->hovered(), true); + QCOMPARE(buttonHoveredSpy.count(), 1); + + QTest::mouseMove(window, rightOfButton); + QCOMPARE(topSidebarHH->isHovered(), true); + QCOMPARE(sidebarHoveredSpy.count(), 1); + QCOMPARE(buttonMA->hovered(), false); + QCOMPARE(buttonHoveredSpy.count(), 2); + + QTest::mouseMove(window, outOfSidebar); + QCOMPARE(topSidebarHH->isHovered(), false); + QCOMPARE(sidebarHoveredSpy.count(), 2); + QCOMPARE(buttonMA->hovered(), false); + QCOMPARE(buttonHoveredSpy.count(), 2); +} + +void tst_HoverHandler::hoverHandlerAndUnderlyingMouseArea() +{ + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "lesHoverables.qml"); + QQuickView * window = windowPtr.data(); + QQuickItem * bottomSidebar = window->rootObject()->findChild<QQuickItem *>("bottomSidebar"); + QVERIFY(bottomSidebar); + QQuickMouseArea *bottomSidebarMA = bottomSidebar->findChild<QQuickMouseArea *>("bottomSidebarMA"); + QVERIFY(bottomSidebarMA); + QQuickItem * button = bottomSidebar->findChild<QQuickItem *>("buttonWithHH"); + QVERIFY(button); + QQuickHoverHandler *buttonHH = button->findChild<QQuickHoverHandler *>("buttonHH"); + QVERIFY(buttonHH); + + QPoint buttonCenter(button->mapToScene(QPointF(button->width() / 2, button->height() / 2)).toPoint()); + QPoint rightOfButton(button->mapToScene(QPointF(button->width() + 2, button->height() / 2)).toPoint()); + QPoint outOfSidebar(bottomSidebar->mapToScene(QPointF(bottomSidebar->width() + 2, bottomSidebar->height() / 2)).toPoint()); + QSignalSpy sidebarHoveredSpy(bottomSidebarMA, SIGNAL(hoveredChanged())); + QSignalSpy buttonHoveredSpy(buttonHH, SIGNAL(hoveredChanged())); + + QTest::mouseMove(window, outOfSidebar); + QCOMPARE(bottomSidebarMA->hovered(), false); + QCOMPARE(sidebarHoveredSpy.count(), 0); + QCOMPARE(buttonHH->isHovered(), false); + QCOMPARE(buttonHoveredSpy.count(), 0); + + QTest::mouseMove(window, rightOfButton); + QCOMPARE(bottomSidebarMA->hovered(), true); + QCOMPARE(sidebarHoveredSpy.count(), 1); + QCOMPARE(buttonHH->isHovered(), false); + QCOMPARE(buttonHoveredSpy.count(), 0); + + QTest::mouseMove(window, buttonCenter); + QCOMPARE(bottomSidebarMA->hovered(), false); + QCOMPARE(sidebarHoveredSpy.count(), 2); + QCOMPARE(buttonHH->isHovered(), true); + QCOMPARE(buttonHoveredSpy.count(), 1); + + QTest::mouseMove(window, rightOfButton); + QCOMPARE(bottomSidebarMA->hovered(), true); + QCOMPARE(sidebarHoveredSpy.count(), 3); + QCOMPARE(buttonHH->isHovered(), false); + QCOMPARE(buttonHoveredSpy.count(), 2); + + QTest::mouseMove(window, outOfSidebar); + QCOMPARE(bottomSidebarMA->hovered(), false); + QCOMPARE(sidebarHoveredSpy.count(), 4); + QCOMPARE(buttonHH->isHovered(), false); + QCOMPARE(buttonHoveredSpy.count(), 2); +} + +QTEST_MAIN(tst_HoverHandler) + +#include "tst_qquickhoverhandler.moc" diff --git a/tests/auto/quick/qquicklistview/data/snapOneItemWrongDirection.qml b/tests/auto/quick/qquicklistview/data/snapOneItemWrongDirection.qml new file mode 100644 index 0000000000..f5b7b35d0c --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/snapOneItemWrongDirection.qml @@ -0,0 +1,18 @@ +import QtQuick 2.0 + +ListView { + width: 400 + height: 400 + focus: true + + model: 10 + delegate: Rectangle { + width: parent.width + height: 50 + color: index % 2 ? "blue" : "green" + } + + snapMode: ListView.SnapOneItem + Keys.onUpPressed: flick(0,500) + Keys.onDownPressed: flick(0,-500) +} diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 64a186eade..9c11957894 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -182,6 +182,7 @@ private slots: void snapOneItem_data(); void snapOneItem(); void snapOneItemCurrentIndexRemoveAnimation(); + void snapOneItemWrongDirection(); void QTBUG_9791(); void QTBUG_33568(); @@ -5672,6 +5673,24 @@ void tst_QQuickListView::snapOneItemCurrentIndexRemoveAnimation() QCOMPARE(currentIndexSpy.count(), 0); } +void tst_QQuickListView::snapOneItemWrongDirection() +{ + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("snapOneItemWrongDirection.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickListView *listview = qobject_cast<QQuickListView*>(window->rootObject()); + QTRY_VERIFY(listview != nullptr); + + QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false); + QTRY_COMPARE(listview->currentIndex(), 0); + + listview->flick(0,500); + QTRY_VERIFY(!listview->isMovingHorizontally()); + QCOMPARE(listview->contentX(), qreal(0)); +} + void tst_QQuickListView::attachedProperties_QTBUG_32836() { QScopedPointer<QQuickView> window(createView()); diff --git a/tests/auto/quick/qquicktableview/data/changemodelfromdelegate.qml b/tests/auto/quick/qquicktableview/data/changemodelfromdelegate.qml new file mode 100644 index 0000000000..79a8e4351a --- /dev/null +++ b/tests/auto/quick/qquicktableview/data/changemodelfromdelegate.qml @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Window 2.3 +import TestModel 0.1 + +Item { + width: 640 + height: 450 + + property alias tableView: tableView + property bool addRowFromDelegate: false + + onAddRowFromDelegateChanged: { + if (!addRowFromDelegate) + return; + tableModel.addRow(0); + tableView.forceLayout(); + } + + TestModel { + id: tableModel + rowCount: 1 + columnCount: 4 + } + + TableView { + id: tableView + width: 600 + height: 400 + clip: true + model: tableModel + delegate: tableViewDelegate + } + + Component { + id: tableViewDelegate + Rectangle { + objectName: "tableViewDelegate" + implicitWidth: 100 + implicitHeight: 100 + color: "lightgray" + border.width: 1 + + Text { + anchors.centerIn: parent + text: modelData + } + + Component.onCompleted: { + if (!addRowFromDelegate) + return; + addRowFromDelegate = false; + tableModel.addRow(0); + } + } + } + +} + diff --git a/tests/auto/quick/qquicktableview/testmodel.h b/tests/auto/quick/qquicktableview/testmodel.h index 56d5021cec..28ea466b82 100644 --- a/tests/auto/quick/qquicktableview/testmodel.h +++ b/tests/auto/quick/qquicktableview/testmodel.h @@ -136,6 +136,11 @@ public: endResetModel(); } + Q_INVOKABLE void addRow(int row) + { + insertRow(row, QModelIndex()); + } + signals: void rowCountChanged(); void columnCountChanged(); diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index ea3531ba13..5e1c21fce8 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -128,6 +128,7 @@ private slots: void checkContextPropertiesQQmlListProperyModel_data(); void checkContextPropertiesQQmlListProperyModel(); void checkRowAndColumnChangedButNotIndex(); + void checkChangingModelFromDelegate(); }; tst_QQuickTableView::tst_QQuickTableView() @@ -1622,6 +1623,34 @@ void tst_QQuickTableView::checkRowAndColumnChangedButNotIndex() QCOMPARE(contextColumn, 1); } +void tst_QQuickTableView::checkChangingModelFromDelegate() +{ + // Check that we don't restart a rebuild of the table + // while we're in the middle of rebuilding it from before + LOAD_TABLEVIEW("changemodelfromdelegate.qml"); + + // Set addRowFromDelegate. This will trigger the QML code to add a new + // row and call forceLayout(). When TableView instantiates the first + // delegate in the new row, the Component.onCompleted handler will try to + // add a new row. But since we're currently rebuilding, this should be + // scheduled for later. + view->rootObject()->setProperty("addRowFromDelegate", true); + + // We now expect two rows in the table, one more than initially + QCOMPARE(tableViewPrivate->tableSize.height(), 2); + QCOMPARE(tableViewPrivate->loadedTable.height(), 2); + + // And since the QML code tried to add another row as well, we + // expect rebuildScheduled to be true, and a polish event to be pending. + QCOMPARE(tableViewPrivate->rebuildScheduled, true); + QCOMPARE(tableViewPrivate->polishScheduled, true); + WAIT_UNTIL_POLISHED; + + // After handling the polish event, we expect also the third row to now be added + QCOMPARE(tableViewPrivate->tableSize.height(), 3); + QCOMPARE(tableViewPrivate->loadedTable.height(), 3); +} + QTEST_MAIN(tst_QQuickTableView) #include "tst_qquicktableview.moc" |