diff options
Diffstat (limited to 'src/qml/jsruntime')
97 files changed, 7043 insertions, 1736 deletions
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 4bc877bd9d..a2408d7d2a 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -14,13 +14,16 @@ SOURCES += \ $$PWD/qv4sparsearray.cpp \ $$PWD/qv4arraydata.cpp \ $$PWD/qv4arrayobject.cpp \ + $$PWD/qv4arrayiterator.cpp \ $$PWD/qv4argumentsobject.cpp \ $$PWD/qv4booleanobject.cpp \ $$PWD/qv4dateobject.cpp \ $$PWD/qv4errorobject.cpp \ $$PWD/qv4function.cpp \ $$PWD/qv4functionobject.cpp \ + $$PWD/qv4generatorobject.cpp \ $$PWD/qv4globalobject.cpp \ + $$PWD/qv4iterator.cpp \ $$PWD/qv4jsonobject.cpp \ $$PWD/qv4mathobject.cpp \ $$PWD/qv4memberdata.cpp \ @@ -28,7 +31,9 @@ SOURCES += \ $$PWD/qv4object.cpp \ $$PWD/qv4objectproto.cpp \ $$PWD/qv4qmlcontext.cpp \ + $$PWD/qv4reflect.cpp \ $$PWD/qv4regexpobject.cpp \ + $$PWD/qv4stringiterator.cpp \ $$PWD/qv4stringobject.cpp \ $$PWD/qv4variantobject.cpp \ $$PWD/qv4objectiterator.cpp \ @@ -36,13 +41,17 @@ SOURCES += \ $$PWD/qv4runtimecodegen.cpp \ $$PWD/qv4serialize.cpp \ $$PWD/qv4script.cpp \ - $$PWD/qv4sequenceobject.cpp \ + $$PWD/qv4symbol.cpp \ + $$PWD/qv4setobject.cpp \ + $$PWD/qv4setiterator.cpp \ $$PWD/qv4include.cpp \ $$PWD/qv4qobjectwrapper.cpp \ $$PWD/qv4arraybuffer.cpp \ $$PWD/qv4typedarray.cpp \ $$PWD/qv4dataview.cpp \ - $$PWD/qv4vme_moth.cpp + $$PWD/qv4vme_moth.cpp \ + $$PWD/qv4mapobject.cpp \ + $$PWD/qv4mapiterator.cpp qtConfig(qml-debug): SOURCES += $$PWD/qv4profiling.cpp @@ -64,13 +73,16 @@ HEADERS += \ $$PWD/qv4sparsearray_p.h \ $$PWD/qv4arraydata_p.h \ $$PWD/qv4arrayobject_p.h \ + $$PWD/qv4arrayiterator_p.h \ $$PWD/qv4argumentsobject_p.h \ $$PWD/qv4booleanobject_p.h \ $$PWD/qv4dateobject_p.h \ $$PWD/qv4errorobject_p.h \ $$PWD/qv4function_p.h \ $$PWD/qv4functionobject_p.h \ + $$PWD/qv4generatorobject_p.h \ $$PWD/qv4globalobject_p.h \ + $$PWD/qv4iterator_p.h \ $$PWD/qv4jsonobject_p.h \ $$PWD/qv4mathobject_p.h \ $$PWD/qv4memberdata_p.h \ @@ -78,8 +90,10 @@ HEADERS += \ $$PWD/qv4object_p.h \ $$PWD/qv4objectproto_p.h \ $$PWD/qv4qmlcontext_p.h \ + $$PWD/qv4reflect_p.h \ $$PWD/qv4regexpobject_p.h \ $$PWD/qv4runtimecodegen_p.h \ + $$PWD/qv4stringiterator_p.h \ $$PWD/qv4stringobject_p.h \ $$PWD/qv4variantobject_p.h \ $$PWD/qv4property_p.h \ @@ -87,16 +101,28 @@ HEADERS += \ $$PWD/qv4regexp_p.h \ $$PWD/qv4serialize_p.h \ $$PWD/qv4script_p.h \ + $$PWD/qv4symbol_p.h \ + $$PWD/qv4setobject_p.h \ + $$PWD/qv4setiterator_p.h \ $$PWD/qv4scopedvalue_p.h \ $$PWD/qv4executableallocator_p.h \ - $$PWD/qv4sequenceobject_p.h \ $$PWD/qv4include_p.h \ $$PWD/qv4qobjectwrapper_p.h \ $$PWD/qv4profiling_p.h \ $$PWD/qv4arraybuffer_p.h \ $$PWD/qv4typedarray_p.h \ $$PWD/qv4dataview_p.h \ - $$PWD/qv4vme_moth_p.h + $$PWD/qv4vme_moth_p.h \ + $$PWD/qv4mapobject_p.h \ + $$PWD/qv4mapiterator_p.h + +qtConfig(qml-sequence-object) { + HEADERS += \ + $$PWD/qv4sequenceobject_p.h + + SOURCES += \ + $$PWD/qv4sequenceobject.cpp +} } diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 075e7afd8a..58951d043c 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -37,11 +37,13 @@ ** ****************************************************************************/ #include <qv4argumentsobject_p.h> +#include <qv4arrayobject_p.h> #include <qv4alloca_p.h> #include <qv4scopedvalue_p.h> #include <qv4string_p.h> #include <qv4function_p.h> #include <qv4jscall_p.h> +#include <qv4symbol_p.h> using namespace QV4; @@ -61,10 +63,12 @@ void Heap::ArgumentsObject::init(QV4::CppStackFrame *frame) this->context.set(v4, context->d()); Q_ASSERT(vtable() == QV4::ArgumentsObject::staticVTable()); - Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee())); + Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee()->identifier())); setProperty(v4, CalleePropertyIndex, context->d()->function); - Q_ASSERT(LengthPropertyIndex == internalClass->find(v4->id_length())); + Q_ASSERT(LengthPropertyIndex == internalClass->find(v4->id_length()->identifier())); setProperty(v4, LengthPropertyIndex, Primitive::fromInt32(context->argc())); + Q_ASSERT(SymbolIteratorPropertyIndex == internalClass->find(v4->symbol_iterator()->identifier())); + setProperty(v4, SymbolIteratorPropertyIndex, *v4->arrayProtoValues()); } void Heap::StrictArgumentsObject::init(QV4::CppStackFrame *frame) @@ -74,19 +78,18 @@ void Heap::StrictArgumentsObject::init(QV4::CppStackFrame *frame) Object::init(); - Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee())); - Q_ASSERT(CallerPropertyIndex == internalClass->find(v4->id_caller())); + Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee()->identifier())); + Q_ASSERT(SymbolIteratorPropertyIndex == internalClass->find(v4->symbol_iterator()->identifier())); + setProperty(v4, SymbolIteratorPropertyIndex, *v4->arrayProtoValues()); setProperty(v4, CalleePropertyIndex + QV4::Object::GetterOffset, *v4->thrower()); setProperty(v4, CalleePropertyIndex + QV4::Object::SetterOffset, *v4->thrower()); - setProperty(v4, CallerPropertyIndex + QV4::Object::GetterOffset, *v4->thrower()); - setProperty(v4, CallerPropertyIndex + QV4::Object::SetterOffset, *v4->thrower()); Scope scope(v4); Scoped<QV4::StrictArgumentsObject> args(scope, this); args->arrayReserve(frame->originalArgumentsCount); args->arrayPut(0, frame->originalArguments, frame->originalArgumentsCount); - Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(v4->id_length())); + Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(v4->id_length()->identifier())); setProperty(v4, LengthPropertyIndex, Primitive::fromInt32(frame->originalArgumentsCount)); } @@ -136,7 +139,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con mapAttrs = arrayData()->attributes(index); arrayData()->getProperty(index, map, &mapAttrs); setArrayAttributes(index, Attr_Data); - ArrayData::Index arrayIndex{ arrayData(), arrayData()->mappedIndex(index) }; + PropertyIndex arrayIndex{ arrayData(), arrayData()->values.values + arrayData()->mappedIndex(index) }; arrayIndex.set(scope.engine, d()->mappedArguments->values[index]); } @@ -245,10 +248,8 @@ ReturnedValue ArgumentsSetterFunction::call(const FunctionObject *setter, const return Encode::undefined(); } -uint ArgumentsObject::getLength(const Managed *m) +qint64 ArgumentsObject::getLength(const Managed *m) { const ArgumentsObject *a = static_cast<const ArgumentsObject *>(m); - if (a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->isInteger()) - return a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->integerValue(); - return Primitive::toUInt32(a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->doubleValue()); + return a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->toLength(); } diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index ac281f555a..f246f66019 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -85,7 +85,8 @@ DECLARE_HEAP_OBJECT(ArgumentsObject, Object) { DECLARE_MARKOBJECTS(ArgumentsObject); enum { LengthPropertyIndex = 0, - CalleePropertyIndex = 1 + SymbolIteratorPropertyIndex = 1, + CalleePropertyIndex = 2 }; void init(CppStackFrame *frame); }; @@ -95,8 +96,8 @@ DECLARE_HEAP_OBJECT(ArgumentsObject, Object) { DECLARE_HEAP_OBJECT(StrictArgumentsObject, Object) { enum { LengthPropertyIndex = 0, - CalleePropertyIndex = 1, - CallerPropertyIndex = 3 + SymbolIteratorPropertyIndex = 1, + CalleePropertyIndex = 2 }; void init(CppStackFrame *frame); }; @@ -142,7 +143,7 @@ struct ArgumentsObject: Object { bool fullyCreated() const { return d()->fullyCreated; } static bool isNonStrictArgumentsObject(Managed *m) { - return m->d()->vtable() == staticVTable(); + return m->vtable() == staticVTable(); } bool defineOwnProperty(ExecutionEngine *engine, uint index, const Property *desc, PropertyAttributes attrs); @@ -150,7 +151,7 @@ struct ArgumentsObject: Object { static bool putIndexed(Managed *m, uint index, const Value &value); static bool deleteIndexedProperty(Managed *m, uint index); static PropertyAttributes queryIndexed(const Managed *m, uint index); - static uint getLength(const Managed *m); + static qint64 getLength(const Managed *m); void fullyCreate(); diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp index 59a2b9d913..b0c2ba597a 100644 --- a/src/qml/jsruntime/qv4arraybuffer.cpp +++ b/src/qml/jsruntime/qv4arraybuffer.cpp @@ -41,6 +41,7 @@ #include "qv4dataview_p.h" #include "qv4string_p.h" #include "qv4jscall_p.h" +#include "qv4symbol_p.h" using namespace QV4; @@ -94,7 +95,8 @@ ReturnedValue ArrayBufferCtor::method_isView(const FunctionObject *, const Value void Heap::ArrayBuffer::init(size_t length) { Object::init(); - data = QTypedArrayData<char>::allocate(length + 1); + if (length < UINT_MAX) + data = QTypedArrayData<char>::allocate(length + 1); if (!data) { internalClass->engine->throwRangeError(QStringLiteral("ArrayBuffer: out of memory")); return; @@ -147,13 +149,17 @@ void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); ctor->defineDefaultProperty(QStringLiteral("isView"), ArrayBufferCtor::method_isView, 1); + ctor->addSymbolSpecies(); + defineDefaultProperty(engine->id_constructor(), (o = ctor)); defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr); defineDefaultProperty(QStringLiteral("slice"), method_slice, 2); defineDefaultProperty(QStringLiteral("toString"), method_toString, 0); + ScopedString name(scope, engine->newString(QStringLiteral("ArrayBuffer"))); + defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name); } ReturnedValue ArrayBufferPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int) diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index 855407e6f7..ecc0b138c0 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -59,8 +59,9 @@ const QV4::VTable QV4::ArrayData::static_vtbl = { QV4::ArrayData::IsFunctionObject, QV4::ArrayData::IsErrorObject, QV4::ArrayData::IsArrayData, - 0, + QV4::ArrayData::IsStringOrSymbol, QV4::ArrayData::MyType, + { 0, 0, 0, 0 }, "ArrayData", Q_VTABLE_FUNCTION(QV4::ArrayData, destroy), ArrayData::Data::markObjects, @@ -104,13 +105,6 @@ QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF Q_STATIC_ASSERT(sizeof(Heap::ArrayData) == sizeof(Heap::SimpleArrayData)); Q_STATIC_ASSERT(sizeof(Heap::ArrayData) == sizeof(Heap::SparseArrayData)); -static Q_ALWAYS_INLINE void storeValue(ReturnedValue *target, uint value) -{ - Value v; - v.setEmpty(value); - *target = v.asReturnedValue(); -} - void Heap::ArrayData::markObjects(Heap::Base *base, MarkStack *stack) { ArrayData *a = static_cast<ArrayData *>(base); @@ -195,7 +189,7 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt Heap::SparseArrayData *sparse = static_cast<Heap::SparseArrayData *>(newData->d()); - ReturnedValue *lastFree; + Value *lastFree; if (d && d->type() == Heap::ArrayData::Sparse) { Heap::SparseArrayData *old = static_cast<Heap::SparseArrayData *>(d->d()); sparse->sparse = old->sparse; @@ -204,29 +198,29 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt } else { sparse->sparse = new SparseArray; lastFree = &sparse->sparse->freeList; - storeValue(lastFree, 0); + *lastFree = Encode(0); for (uint i = 0; i < toCopy; ++i) { if (!sparse->values[i].isEmpty()) { SparseArrayNode *n = sparse->sparse->insert(i); n->value = i; } else { - storeValue(lastFree, i); + *lastFree = Encode(i); sparse->values.values[i].setEmpty(); - lastFree = &sparse->values.values[i].rawValueRef(); + lastFree = &sparse->values.values[i]; } } } if (toCopy < sparse->values.alloc) { for (uint i = toCopy; i < sparse->values.alloc; ++i) { - storeValue(lastFree, i); + *lastFree = Encode(i); sparse->values.values[i].setEmpty(); - lastFree = &sparse->values.values[i].rawValueRef(); + lastFree = &sparse->values.values[i]; } } - storeValue(lastFree, UINT_MAX); + *lastFree = Encode(-1); - Q_ASSERT(Value::fromReturnedValue(sparse->sparse->freeList).isEmpty()); + Q_ASSERT(sparse->sparse->freeList.isInteger()); // ### Could explicitly free the old data } @@ -368,12 +362,12 @@ void SparseArrayData::free(Heap::ArrayData *d, uint idx) Value *v = d->values.values + idx; if (d->attrs && d->attrs[idx].isAccessor()) { // double slot, free both. Order is important, so we have a double slot for allocation again afterwards. - v[1].setEmpty(Value::fromReturnedValue(d->sparse->freeList).emptyValue()); - v[0].setEmpty(idx + 1); + v[1] = d->sparse->freeList; + v[0] = Encode(idx + 1); } else { - v->setEmpty(Value::fromReturnedValue(d->sparse->freeList).emptyValue()); + *v = d->sparse->freeList; } - d->sparse->freeList = Primitive::emptyValue(idx).asReturnedValue(); + d->sparse->freeList = Encode(idx); if (d->attrs) d->attrs[idx].clear(); } @@ -390,36 +384,34 @@ uint SparseArrayData::allocate(Object *o, bool doubleSlot) Q_ASSERT(o->d()->arrayData->type == Heap::ArrayData::Sparse); Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (doubleSlot) { - ReturnedValue *last = &dd->sparse->freeList; + Value *last = &dd->sparse->freeList; while (1) { - if (Value::fromReturnedValue(*last).value() == UINT_MAX) { + if (last->int_32() == -1) { reallocate(o, dd->values.alloc + 2, true); dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); last = &dd->sparse->freeList; - Q_ASSERT(Value::fromReturnedValue(*last).value() != UINT_MAX); + Q_ASSERT(last->int_32() != -1); } - Q_ASSERT(dd->values[Value::fromReturnedValue(*last).value()].value() != Value::fromReturnedValue(*last).value()); - if (dd->values[Value::fromReturnedValue(*last).value()].value() == (Value::fromReturnedValue(*last).value() + 1)) { + Q_ASSERT(dd->values[static_cast<uint>(last->int_32())].int_32() != last->int_32()); + if (dd->values[static_cast<uint>(last->int_32())].int_32() == last->int_32() + 1) { // found two slots in a row - uint idx = Value::fromReturnedValue(*last).emptyValue(); - Value lastV = Value::fromReturnedValue(*last); - lastV.setEmpty(dd->values[lastV.emptyValue() + 1].value()); - *last = lastV.rawValue(); + uint idx = static_cast<uint>(last->int_32()); + *last = Encode(dd->values[static_cast<uint>(last->int_32()) + 1].int_32()); dd->attrs[idx] = Attr_Accessor; return idx; } - last = &dd->values.values[Value::fromReturnedValue(*last).value()].rawValueRef(); + last = &dd->values.values[last->int_32()]; } } else { - if (Value::fromReturnedValue(dd->sparse->freeList).value() == UINT_MAX) { + if (dd->sparse->freeList.int_32() == -1) { reallocate(o, dd->values.alloc + 1, false); dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } - uint idx = Value::fromReturnedValue(dd->sparse->freeList).value(); - Q_ASSERT(idx != UINT_MAX); - dd->sparse->freeList = dd->values[idx].asReturnedValue(); - Q_ASSERT(Value::fromReturnedValue(dd->sparse->freeList).isEmpty()); + Q_ASSERT(dd->sparse->freeList.int_32() != -1); + uint idx = static_cast<uint>(dd->sparse->freeList.int_32()); + dd->sparse->freeList = dd->values[idx]; + Q_ASSERT(dd->sparse->freeList.isInteger()); if (dd->attrs) dd->attrs[idx] = Attr_Data; return idx; @@ -474,14 +466,14 @@ bool SparseArrayData::del(Object *o, uint index) if (isAccessor) { // free up both indices - dd->values.values[pidx + 1].setEmpty(Value::fromReturnedValue(dd->sparse->freeList).emptyValue()); - dd->values.values[pidx].setEmpty(pidx + 1); + dd->values.values[pidx + 1] = dd->sparse->freeList; + dd->values.values[pidx] = Encode(pidx + 1); } else { Q_ASSERT(dd->type == Heap::ArrayData::Sparse); - dd->values.values[pidx].setEmpty(Value::fromReturnedValue(dd->sparse->freeList).emptyValue()); + dd->values.values[pidx] = dd->sparse->freeList; } - dd->sparse->freeList = Primitive::emptyValue(pidx).asReturnedValue(); + dd->sparse->freeList = Encode(pidx); dd->sparse->erase(n); return true; } diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h index 7ec060f9c6..ac5b430356 100644 --- a/src/qml/jsruntime/qv4arraydata_p.h +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -103,28 +103,16 @@ DECLARE_HEAP_OBJECT(ArrayData, Base) { enum Type { Simple = 0, Complex = 1, Sparse = 2, Custom = 3 }; - struct Index { - Heap::ArrayData *arrayData; - uint index; - - void set(EngineBase *e, Value newVal) { - arrayData->values.set(e, index, newVal); - } - const Value *operator->() const { return &arrayData->values[index]; } - const Value &operator*() const { return arrayData->values[index]; } - bool isNull() const { return !arrayData; } - }; - bool isSparse() const { return type == Sparse; } - const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(Base::vtable()); } + const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(internalClass->vtable); } inline ReturnedValue get(uint i) const { return vtable()->get(this, i); } inline bool getProperty(uint index, Property *p, PropertyAttributes *attrs); inline void setProperty(EngineBase *e, uint index, const Property *p); - inline Index getValueOrSetter(uint index, PropertyAttributes *attrs); + inline PropertyIndex getValueOrSetter(uint index, PropertyAttributes *attrs); inline PropertyAttributes attributes(uint i) const; bool isEmpty(uint i) const { @@ -187,8 +175,6 @@ struct Q_QML_EXPORT ArrayData : public Managed IsArrayData = true }; - typedef Heap::ArrayData::Index Index; - uint alloc() const { return d()->values.alloc; } uint &alloc() { return d()->values.alloc; } void setAlloc(uint a) { d()->values.alloc = a; } @@ -303,9 +289,9 @@ bool ArrayData::getProperty(uint index, Property *p, PropertyAttributes *attrs) *attrs = attributes(index); if (p) { - p->value = *(Index{ this, mapped }); + p->value = *(PropertyIndex{ this, values.values + mapped }); if (attrs->isAccessor()) - p->set = *(Index{ this, mapped + 1 /*Object::SetterOffset*/ }); + p->set = *(PropertyIndex{ this, values.values + mapped + 1 /*Object::SetterOffset*/ }); } return true; } @@ -326,16 +312,18 @@ inline PropertyAttributes ArrayData::attributes(uint i) const return static_cast<const SimpleArrayData *>(this)->attributes(i); } -ArrayData::Index ArrayData::getValueOrSetter(uint index, PropertyAttributes *attrs) +PropertyIndex ArrayData::getValueOrSetter(uint index, PropertyAttributes *attrs) { uint idx = mappedIndex(index); if (idx == UINT_MAX) { *attrs = Attr_Invalid; - return { nullptr, 0 }; + return { nullptr, nullptr }; } *attrs = attributes(index); - return { this, attrs->isAccessor() ? idx + 1 /* QV4::Object::SetterOffset*/ : idx }; + if (attrs->isAccessor()) + ++idx; + return { this, values.values + idx }; } diff --git a/src/qml/jsruntime/qv4arrayiterator.cpp b/src/qml/jsruntime/qv4arrayiterator.cpp new file mode 100644 index 0000000000..d6f787c01d --- /dev/null +++ b/src/qml/jsruntime/qv4arrayiterator.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Crimson AS <info@crimson.no> +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qv4iterator_p.h> +#include <private/qv4arrayiterator_p.h> +#include <private/qv4typedarray_p.h> +#include <private/qv4symbol_p.h> + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(ArrayIteratorObject); + +void ArrayIteratorPrototype::init(ExecutionEngine *e) +{ + defineDefaultProperty(QStringLiteral("next"), method_next, 0); + + Scope scope(e); + ScopedString val(scope, e->newString(QLatin1String("Array Iterator"))); + defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val); +} + +ReturnedValue ArrayIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int) +{ + Scope scope(b); + const ArrayIteratorObject *thisObject = that->as<ArrayIteratorObject>(); + if (!thisObject) + return scope.engine->throwTypeError(QLatin1String("Not an Array Iterator instance")); + + ScopedObject a(scope, thisObject->d()->iteratedObject); + if (!a) { + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); + } + + quint32 index = thisObject->d()->nextIndex; + IteratorKind itemKind = thisObject->d()->iterationKind; + + Scoped<TypedArray> ta(scope, a->as<TypedArray>()); + quint32 len = a->getLength(); + + if (index >= len) { + thisObject->d()->iteratedObject.set(scope.engine, nullptr); + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); + } + + thisObject->d()->nextIndex = index + 1; + if (itemKind == KeyIteratorKind) { + return IteratorPrototype::createIterResultObject(scope.engine, Primitive::fromInt32(index), false); + } + + ReturnedValue elementValue = a->getIndexed(index); + CHECK_EXCEPTION(); + + if (itemKind == ValueIteratorKind) { + return IteratorPrototype::createIterResultObject(scope.engine, Value::fromReturnedValue(elementValue), false); + } else { + Q_ASSERT(itemKind == KeyValueIteratorKind); + + ScopedArrayObject resultArray(scope, scope.engine->newArrayObject()); + resultArray->arrayReserve(2); + resultArray->arrayPut(0, Primitive::fromInt32(index)); + resultArray->arrayPut(1, Value::fromReturnedValue(elementValue)); + resultArray->setArrayLengthUnchecked(2); + + return IteratorPrototype::createIterResultObject(scope.engine, resultArray, false); + } +} + diff --git a/src/qml/jsruntime/qv4arrayiterator_p.h b/src/qml/jsruntime/qv4arrayiterator_p.h new file mode 100644 index 0000000000..6d6bb466f1 --- /dev/null +++ b/src/qml/jsruntime/qv4arrayiterator_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Crimson AS <info@crimson.no> +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ARRAYITERATOR_P_H +#define QV4ARRAYITERATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" +#include "qv4iterator_p.h" +#include "qv4arraydata_p.h" + +QT_BEGIN_NAMESPACE + + +namespace QV4 { + +namespace Heap { + +#define ArrayIteratorObjectMembers(class, Member) \ + Member(class, Pointer, Object *, iteratedObject) \ + Member(class, NoMark, IteratorKind, iterationKind) \ + Member(class, NoMark, quint32, nextIndex) + +DECLARE_HEAP_OBJECT(ArrayIteratorObject, Object) { + DECLARE_MARKOBJECTS(ArrayIteratorObject); + void init(Object *obj, QV4::ExecutionEngine *engine) + { + Object::init(); + this->iteratedObject.set(engine, obj); + this->nextIndex = 0; + } +}; + +} + +struct ArrayIteratorPrototype : Object +{ + V4_PROTOTYPE(iteratorPrototype) + void init(ExecutionEngine *engine); + + static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); +}; + +struct ArrayIteratorObject : Object +{ + V4_OBJECT2(ArrayIteratorObject, Object) + Q_MANAGED_TYPE(ArrayIteratorObject) + V4_PROTOTYPE(arrayIteratorPrototype) + + void init(ExecutionEngine *engine); +}; + + +} + +QT_END_NAMESPACE + +#endif // QV4ARRAYITERATOR_P_H + diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index bd019d3bcb..434f6781a8 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2018 Crimson AS <info@crimson.no> ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -38,12 +39,15 @@ ****************************************************************************/ #include "qv4arrayobject_p.h" +#include "qv4objectiterator_p.h" +#include "qv4arrayiterator_p.h" #include "qv4sparsearray_p.h" #include "qv4objectproto_p.h" #include "qv4jscall_p.h" #include "qv4argumentsobject_p.h" #include "qv4runtime_p.h" #include "qv4string_p.h" +#include "qv4symbol_p.h" #include <QtCore/qscopedvaluerollback.h> using namespace QV4; @@ -89,13 +93,19 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); ctor->defineDefaultProperty(QStringLiteral("isArray"), method_isArray, 1); + ctor->defineDefaultProperty(QStringLiteral("of"), method_of, 0); + ctor->defineDefaultProperty(QStringLiteral("from"), method_from, 1); + ctor->addSymbolSpecies(); + defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(engine->id_toString(), method_toString, 0); defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString, 0); defineDefaultProperty(QStringLiteral("concat"), method_concat, 1); + defineDefaultProperty(QStringLiteral("copyWithin"), method_copyWithin, 2); + defineDefaultProperty(QStringLiteral("entries"), method_entries, 0); defineDefaultProperty(QStringLiteral("find"), method_find, 1); defineDefaultProperty(QStringLiteral("findIndex"), method_findIndex, 1); defineDefaultProperty(QStringLiteral("join"), method_join, 1); @@ -107,15 +117,23 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("sort"), method_sort, 1); defineDefaultProperty(QStringLiteral("splice"), method_splice, 2); defineDefaultProperty(QStringLiteral("unshift"), method_unshift, 1); + defineDefaultProperty(QStringLiteral("includes"), method_includes, 1); defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1); + defineDefaultProperty(QStringLiteral("keys"), method_keys, 0); defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); defineDefaultProperty(QStringLiteral("every"), method_every, 1); + defineDefaultProperty(QStringLiteral("fill"), method_fill, 1); defineDefaultProperty(QStringLiteral("some"), method_some, 1); defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1); defineDefaultProperty(QStringLiteral("map"), method_map, 1); defineDefaultProperty(QStringLiteral("filter"), method_filter, 1); defineDefaultProperty(QStringLiteral("reduce"), method_reduce, 1); defineDefaultProperty(QStringLiteral("reduceRight"), method_reduceRight, 1); + ScopedString valuesString(scope, engine->newIdentifier(QStringLiteral("values"))); + ScopedObject values(scope, FunctionObject::createBuiltinFunction(engine, valuesString, method_values, 0)); + engine->jsObjects[ExecutionEngine::ArrayProtoValues] = values; + defineDefaultProperty(QStringLiteral("values"), values); + defineDefaultProperty(engine->symbol_iterator(), values); } ReturnedValue ArrayPrototype::method_isArray(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -124,6 +142,200 @@ ReturnedValue ArrayPrototype::method_isArray(const FunctionObject *, const Value return Encode(isArray); } +ScopedObject createObjectFromCtorOrArray(Scope &scope, ScopedFunctionObject ctor, bool useLen, int len) +{ + 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: + // 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) { + a = scope.engine->newArrayObject(len); + } + + return a; +} + +ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(builtin); + ScopedFunctionObject thatCtor(scope, thisObject); + ScopedObject itemsObject(scope, argv[0]); + bool usingIterator = false; + + if (itemsObject) { + // If the object claims to support iterators, then let's try use them. + ScopedValue it(scope, itemsObject->get(scope.engine->symbol_iterator())); + if (!it->isNullOrUndefined()) { + ScopedFunctionObject itfunc(scope, it); + if (!itfunc) + return scope.engine->throwTypeError(); + usingIterator = true; + } + } + + ScopedFunctionObject mapfn(scope, Primitive::undefinedValue()); + Value *mapArguments = nullptr; + if (argc > 1) { + mapfn = ScopedFunctionObject(scope, argv[1]); + if (!mapfn) + return scope.engine->throwTypeError(QString::fromLatin1("%1 is not a function").arg(argv[1].toQStringNoThrow())); + mapArguments = scope.alloc(2); + } + + ScopedValue thisArg(scope); + if (argc > 2) + thisArg = argv[2]; + + if (usingIterator) { + // Item iteration supported, so let's go ahead and try use that. + ScopedObject a(createObjectFromCtorOrArray(scope, thatCtor, false, 0)); + CHECK_EXCEPTION(); + ScopedObject iterator(scope, Runtime::method_getIterator(scope.engine, itemsObject, true)); + CHECK_EXCEPTION(); // symbol_iterator threw; whoops. + if (!iterator) { + return scope.engine->throwTypeError(); // symbol_iterator wasn't an object. + } + + qint64 k = 0; + ScopedValue mappedValue(scope); + Value *nextValue = scope.alloc(1); + ScopedValue done(scope); + + // The loop below pulls out all the properties using the iterator, and + // sets them into the created array. + forever { + if (k > (static_cast<qint64>(1) << 53) - 1) { + ScopedValue falsey(scope, Encode(false)); + ScopedValue error(scope, scope.engine->throwTypeError()); + return Runtime::method_iteratorClose(scope.engine, iterator, falsey); + } + + // Retrieve the next value. If the iteration ends, we're done here. + done = Value::fromReturnedValue(Runtime::method_iteratorNext(scope.engine, iterator, nextValue)); + CHECK_EXCEPTION(); + if (done->toBoolean()) { + if (ArrayObject *ao = a->as<ArrayObject>()) { + ao->setArrayLengthUnchecked(k); + } else { + a->set(scope.engine->id_length(), Primitive::fromDouble(k), QV4::Object::DoThrowOnRejection); + CHECK_EXCEPTION(); + } + return a.asReturnedValue(); + } + + if (mapfn) { + mapArguments[0] = *nextValue; + mapArguments[1] = Primitive::fromDouble(k); + mappedValue = mapfn->call(thisArg, mapArguments, 2); + if (scope.engine->hasException) + return Runtime::method_iteratorClose(scope.engine, iterator, Primitive::fromBoolean(false)); + } else { + mappedValue = *nextValue; + } + + if (!a->hasOwnProperty(k)) { + a->arraySet(k, mappedValue); + } else { + // Don't return: we need to close the iterator. + scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k)); + } + + if (scope.engine->hasException) { + ScopedValue falsey(scope, Encode(false)); + return Runtime::method_iteratorClose(scope.engine, iterator, falsey); + } + + k++; + } + + // the return is hidden up in the loop above, when iteration finishes. + } else { + // Array-like fallback. We request properties by index, and set them on + // the return object. + ScopedObject arrayLike(scope, argv[0].toObject(scope.engine)); + if (!arrayLike) + return scope.engine->throwTypeError(QString::fromLatin1("Cannot convert %1 to object").arg(argv[0].toQStringNoThrow())); + qint64 len = arrayLike->getLength(); + ScopedObject a(createObjectFromCtorOrArray(scope, thatCtor, true, len)); + CHECK_EXCEPTION(); + + qint64 k = 0; + ScopedValue mappedValue(scope, Primitive::undefinedValue()); + ScopedValue kValue(scope); + while (k < len) { + kValue = arrayLike->getIndexed(k); + CHECK_EXCEPTION(); + + if (mapfn) { + mapArguments[0] = kValue; + mapArguments[1] = Primitive::fromDouble(k); + mappedValue = mapfn->call(thisArg, mapArguments, 2); + CHECK_EXCEPTION(); + } else { + mappedValue = kValue; + } + + if (a->hasOwnProperty(k)) + return scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k)); + + a->arraySet(k, mappedValue); + CHECK_EXCEPTION(); + + k++; + } + + if (ArrayObject *ao = a->as<ArrayObject>()) { + ao->setArrayLengthUnchecked(k); + } else { + a->set(scope.engine->id_length(), Primitive::fromDouble(k), QV4::Object::DoThrowOnRejection); + CHECK_EXCEPTION(); + } + return a.asReturnedValue(); + } + +} + +ReturnedValue ArrayPrototype::method_of(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(builtin); + ScopedFunctionObject that(scope, thisObject); + ScopedObject a(createObjectFromCtorOrArray(scope, that, true, argc)); + CHECK_EXCEPTION(); + + int k = 0; + while (k < argc) { + if (a->hasOwnProperty(k)) { + return scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k)); + } + a->arraySet(k, argv[k]); + CHECK_EXCEPTION(); + + k++; + } + + // ArrayObject updates its own length, and will throw if we try touch it. + if (!a->as<ArrayObject>()) { + a->set(scope.engine->id_length(), Primitive::fromDouble(argc), QV4::Object::DoThrowOnRejection); + CHECK_EXCEPTION(); + } + + return a.asReturnedValue(); +} + ReturnedValue ArrayPrototype::method_toString(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) { Scope scope(builtin); @@ -183,6 +395,88 @@ ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value return result.asReturnedValue(); } +ReturnedValue ArrayPrototype::method_copyWithin(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); + if (!instance) + RETURN_UNDEFINED(); + + double len = instance->getLength(); + double target = argv[0].toInteger(); + double start = argv[1].toInteger(); + double end = len; + + if (argc > 2 && !argv[2].isUndefined()) { + end = argv[2].toInteger(); + } + + double relativeTarget = target; + double relativeStart = start; + double relativeEnd = end; + double from = 0; + double to = 0; + + if (relativeTarget < 0) { + to = std::max(len+relativeTarget, 0.0); + } else { + to = std::min(relativeTarget, len); + } + if (relativeStart < 0) { + from = std::max(len+relativeStart, 0.0); + } else { + from = std::min(relativeStart, len); + } + + double fin = 0; + if (relativeEnd < 0) { + fin = std::max(len+relativeEnd, 0.0); + } else { + fin = std::min(relativeEnd, len); + } + double count = std::min(fin-from, len-to); + double direction = 1; + if (from < to && to < from+count) { + direction = -1; + from = from + count - 1; + to = to + count - 1; + } + + while (count > 0) { + bool fromPresent = false; + ScopedValue fromVal(scope, instance->getIndexed(from, &fromPresent)); + + if (fromPresent) { + instance->setIndexed(to, fromVal, QV4::Object::DoThrowOnRejection); + CHECK_EXCEPTION(); + } else { + bool didDelete = instance->deleteIndexedProperty(to); + CHECK_EXCEPTION(); + if (!didDelete) { + return scope.engine->throwTypeError(); + } + } + + from = from + direction; + to = to + direction; + count = count - 1; + } + + return instance.asReturnedValue(); +} + +ReturnedValue ArrayPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + ScopedObject O(scope, thisObject->toObject(scope.engine)); + if (!O) + RETURN_UNDEFINED(); + + Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O)); + ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind; + return ao->asReturnedValue(); +} + ReturnedValue ArrayPrototype::method_find(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); @@ -352,9 +646,9 @@ ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value * instance->arrayCreate(); Q_ASSERT(instance->arrayData()); - uint len = instance->getLength(); + qint64 len = instance->getLength(); - if (len + argc < len) { + if (len + quint64(argc) >= UINT_MAX) { // ughh... this goes beyond UINT_MAX double l = len; ScopedString s(scope); @@ -393,7 +687,7 @@ ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value * return scope.engine->throwTypeError(); } - return Encode(len); + return Encode(uint(len)); } ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Value *thisObject, const Value *, int) @@ -403,7 +697,10 @@ ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Valu if (!instance) RETURN_UNDEFINED(); - uint length = instance->getLength(); + qint64 length = instance->getLength(); + // ### FIXME + if (length >= UINT_MAX) + return scope.engine->throwRangeError(QLatin1String("Array.prototype.reverse: Length out of range.")); int lo = 0, hi = length - 1; @@ -548,19 +845,31 @@ ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value if (!instance) RETURN_UNDEFINED(); - uint len = instance->getLength(); - - ScopedArrayObject newArray(scope, scope.engine->newArrayObject()); + qint64 len = instance->getLength(); double rs = (argc ? argv[0] : Primitive::undefinedValue()).toInteger(); - uint start; + qint64 start; if (rs < 0) - start = (uint) qMax(0., len + rs); + start = static_cast<qint64>(qMax(0., len + rs)); else - start = (uint) qMin(rs, (double)len); + start = static_cast<qint64>(qMin(rs, static_cast<double>(len))); + + qint64 deleteCount = 0; + qint64 itemCount = 0; + if (argc == 1) { + deleteCount = len - start; + } else if (argc > 1){ + itemCount = argc - 2; + double dc = argv[1].toInteger(); + deleteCount = static_cast<qint64>(qMin(qMax(dc, 0.), double(len - start))); + } - uint deleteCount = (uint)qMin(qMax((argc > 1 ? argv[1] : Primitive::undefinedValue()).toInteger(), 0.), (double)(len - start)); + if (len + itemCount - deleteCount > /*(static_cast<qint64>(1) << 53) - 1*/ UINT_MAX - 1) + return scope.engine->throwTypeError(); + if (deleteCount > /*(static_cast<qint64>(1) << 53) - 1*/ UINT_MAX - 1) + return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range.")); + ScopedArrayObject newArray(scope, scope.engine->newArrayObject()); newArray->arrayReserve(deleteCount); ScopedValue v(scope); for (uint i = 0; i < deleteCount; ++i) { @@ -572,7 +881,6 @@ ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value } newArray->setArrayLengthUnchecked(deleteCount); - uint itemCount = argc < 2 ? 0 : argc - 2; if (itemCount < deleteCount) { for (uint k = start; k < len - deleteCount; ++k) { @@ -663,6 +971,44 @@ ReturnedValue ArrayPrototype::method_unshift(const FunctionObject *b, const Valu return Encode(newLen); } +ReturnedValue ArrayPrototype::method_includes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); + if (!instance) + RETURN_UNDEFINED(); + + qint64 len = instance->getLength(); + 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, instance->getIndexed(k)); + if (val->sameValueZero(argv[0])) { + return Encode(true); + } + k++; + } + + return Encode(false); +} + ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); @@ -729,6 +1075,18 @@ ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Valu return Encode(-1); } +ReturnedValue ArrayPrototype::method_keys(const FunctionObject *f, const Value *thisObject, const Value *, int) +{ + Scope scope(f); + ScopedObject O(scope, thisObject->toObject(scope.engine)); + if (!O) + RETURN_UNDEFINED(); + + Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O)); + ao->d()->iterationKind = IteratorKind::KeyIteratorKind; + return ao->asReturnedValue(); +} + ReturnedValue ArrayPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); @@ -805,6 +1163,42 @@ ReturnedValue ArrayPrototype::method_every(const FunctionObject *b, const Value return Encode(ok); } +ReturnedValue ArrayPrototype::method_fill(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + ScopedObject instance(scope, thisObject->toObject(scope.engine)); + if (!instance) + RETURN_UNDEFINED(); + + uint len = instance->getLength(); + int relativeStart = argc > 1 ? argv[1].toInteger() : 0; + int relativeEnd = len; + if (argc > 2 && !argv[2].isUndefined()) { + relativeEnd = argv[2].toInteger(); + } + uint k = 0; + uint fin = 0; + + if (relativeStart < 0) { + k = std::max(len+relativeStart, uint(0)); + } else { + k = std::min(uint(relativeStart), len); + } + + if (relativeEnd < 0) { + fin = std::max(len + relativeEnd, uint(0)); + } else { + fin = std::min(uint(relativeEnd), len); + } + + while (k < fin) { + instance->setIndexed(k, argv[0], QV4::Object::DoThrowOnRejection); + k++; + } + + return instance.asReturnedValue(); +} + ReturnedValue ArrayPrototype::method_some(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); @@ -873,12 +1267,15 @@ ReturnedValue ArrayPrototype::method_map(const FunctionObject *b, const Value *t if (!instance) RETURN_UNDEFINED(); - uint len = instance->getLength(); + qint64 len = instance->getLength(); if (!argc || !argv->isFunctionObject()) THROW_TYPE_ERROR(); const FunctionObject *callback = static_cast<const FunctionObject *>(argv); + if (len > UINT_MAX - 1) + return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range.")); + ScopedArrayObject a(scope, scope.engine->newArrayObject()); a->arrayReserve(len); a->setArrayLengthUnchecked(len); @@ -1041,3 +1438,20 @@ ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const return acc->asReturnedValue(); } +ReturnedValue ArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + ScopedObject O(scope, thisObject->toObject(scope.engine)); + if (!O) + RETURN_UNDEFINED(); + + Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O)); + ao->d()->iterationKind = IteratorKind::ValueIteratorKind; + return ao->asReturnedValue(); +} + +ReturnedValue ArrayPrototype::method_get_species(const FunctionObject *, const Value *thisObject, const Value *, int) +{ + return thisObject->asReturnedValue(); +} + diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h index 3825a600a2..f1acfed7e7 100644 --- a/src/qml/jsruntime/qv4arrayobject_p.h +++ b/src/qml/jsruntime/qv4arrayobject_p.h @@ -79,9 +79,13 @@ struct ArrayPrototype: ArrayObject void init(ExecutionEngine *engine, Object *ctor); static ReturnedValue method_isArray(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_from(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_of(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_concat(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_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_join(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); @@ -93,15 +97,23 @@ struct ArrayPrototype: ArrayObject static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_splice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_unshift(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_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_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_some(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_map(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_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_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + // while this function is implemented here, it's the same for many other JS classes, so the corresponding JS function + // is instantiated in the engine, and it can be added to any JS object through Object::addSymbolSpecies() + static ReturnedValue method_get_species(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 020e519e74..78cca8d525 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -53,7 +53,42 @@ using namespace QV4; DEFINE_MANAGED_VTABLE(ExecutionContext); DEFINE_MANAGED_VTABLE(CallContext); -DEFINE_MANAGED_VTABLE(CatchContext); + +Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int blockIndex) +{ + Function *function = frame->v4Function; + + Heap::InternalClass *ic = function->compilationUnit->runtimeBlocks.at(blockIndex); + uint nLocals = ic->size; + size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals; + + ExecutionEngine *v4 = function->internalClass->engine; + Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, ic); + c->init(); + c->type = Heap::ExecutionContext::Type_BlockContext; + + Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m()); + c->outer.set(v4, outer); + c->function.set(v4, static_cast<Heap::FunctionObject *>(frame->jsFrame->function.m())); + + c->locals.size = nLocals; + c->locals.alloc = nLocals; + + return c; +} + +Heap::CallContext *ExecutionContext::cloneBlockContext(Heap::CallContext *context) +{ + uint nLocals = context->locals.alloc; + size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals; + + ExecutionEngine *v4 = context->internalClass->engine; + Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, context->internalClass); + memcpy(c, context, requiredMemory); + + return c; + +} Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame) { @@ -75,7 +110,7 @@ Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame) uint nLocals = compiledFunction->nLocals; c->locals.size = nLocals; c->locals.alloc = localsAndFormals; - // memory allocated from the JS heap is 0 initialized, so check if undefined is 0 + // memory allocated from the JS heap is 0 initialized, so check if empty is 0 Q_ASSERT(Primitive::undefinedValue().asReturnedValue() == 0); Value *args = c->locals.values + nLocals; @@ -96,11 +131,14 @@ Heap::ExecutionContext *ExecutionContext::newWithContext(Heap::Object *with) return c; } -Heap::CatchContext *ExecutionContext::newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue) +Heap::ExecutionContext *ExecutionContext::newCatchContext(CppStackFrame *frame, int blockIndex, Heap::String *exceptionVarName) { - Scope scope(this); - ScopedValue e(scope, exceptionValue); - return engine()->memoryManager->alloc<CatchContext>(d(), exceptionVarName, e); + Scope scope(frame->context()); + ScopedString name(scope, exceptionVarName); + ScopedValue val(scope, scope.engine->catchException(nullptr)); + ScopedContext ctx(scope, newBlockContext(frame, blockIndex)); + ctx->setProperty(name, val); + return ctx->d(); } void ExecutionContext::createMutableBinding(String *name, bool deletable) @@ -132,6 +170,8 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) activation = ctx->d()->activation; break; } + case Heap::ExecutionContext::Type_BlockContext: + // never create activation records on block contexts default: break; } @@ -143,33 +183,19 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) ScopedProperty desc(scope); PropertyAttributes attrs(Attr_Data); attrs.setConfigurable(deletable); - activation->__defineOwnProperty__(scope.engine, name, desc, attrs); -} - -void Heap::CatchContext::init(ExecutionContext *outerContext, String *exceptionVarName, - const Value &exceptionValue) -{ - Heap::ExecutionContext::init(Heap::ExecutionContext::Type_CatchContext); - outer.set(internalClass->engine, outerContext); - - this->exceptionVarName.set(internalClass->engine, exceptionVarName); - this->exceptionValue.set(internalClass->engine, exceptionValue); + if (!activation->__defineOwnProperty__(scope.engine, name, desc, attrs)) + scope.engine->throwTypeError(); } bool ExecutionContext::deleteProperty(String *name) { name->makeIdentifier(); - Identifier *id = name->identifier(); + Identifier id = name->identifier(); Heap::ExecutionContext *ctx = d(); for (; ctx; ctx = ctx->outer) { switch (ctx->type) { - case Heap::ExecutionContext::Type_CatchContext: { - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx); - if (c->exceptionVarName->isEqualTo(name->d())) - return false; - break; - } + case Heap::ExecutionContext::Type_BlockContext: case Heap::ExecutionContext::Type_CallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); uint index = c->internalClass->find(id); @@ -200,21 +226,13 @@ bool ExecutionContext::deleteProperty(String *name) ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value &value) { name->makeIdentifier(); - Identifier *id = name->identifier(); + Identifier id = name->identifier(); QV4::ExecutionEngine *v4 = engine(); Heap::ExecutionContext *ctx = d(); for (; ctx; ctx = ctx->outer) { switch (ctx->type) { - case Heap::ExecutionContext::Type_CatchContext: { - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx); - if (c->exceptionVarName->isEqualTo(name->d())) { - c->exceptionValue.set(v4, value); - return NoError; - } - break; - } case Heap::ExecutionContext::Type_WithContext: { Scope scope(v4); ScopedObject w(scope, ctx->activation); @@ -225,6 +243,7 @@ ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value } break; } + case Heap::ExecutionContext::Type_BlockContext: case Heap::ExecutionContext::Type_CallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); uint index = c->internalClass->find(id); @@ -267,15 +286,10 @@ ReturnedValue ExecutionContext::getProperty(String *name) Heap::ExecutionContext *ctx = d(); for (; ctx; ctx = ctx->outer) { switch (ctx->type) { - case Heap::ExecutionContext::Type_CatchContext: { - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx); - if (c->exceptionVarName->isEqualTo(name->d())) - return c->exceptionValue.asReturnedValue(); - break; - } + case Heap::ExecutionContext::Type_BlockContext: case Heap::ExecutionContext::Type_CallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); - Identifier *id = name->identifier(); + Identifier id = name->identifier(); uint index = c->internalClass->find(id); if (index < UINT_MAX) @@ -308,16 +322,11 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) Heap::ExecutionContext *ctx = d(); for (; ctx; ctx = ctx->outer) { switch (ctx->type) { - case Heap::ExecutionContext::Type_CatchContext: { - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx); - if (c->exceptionVarName->isEqualTo(name->d())) - return c->exceptionValue.asReturnedValue(); - break; - } + case Heap::ExecutionContext::Type_BlockContext: case Heap::ExecutionContext::Type_CallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); name->makeIdentifier(); - Identifier *id = name->identifier(); + Identifier id = name->identifier(); uint index = c->internalClass->find(id); if (index < UINT_MAX) diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index 512bfa06d8..26e64728dc 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -55,23 +55,8 @@ QT_BEGIN_NAMESPACE -class QObject; -class QQmlContextData; - namespace QV4 { -namespace CompiledData { -struct CompilationUnitBase; -struct Function; -} - -struct Function; -struct Identifier; -struct CallContext; -struct CatchContext; -struct QmlContext; -struct QQmlContextWrapper; - struct CallData { enum Offsets { @@ -113,8 +98,6 @@ Q_STATIC_ASSERT(offsetof(CallData, args) == 5*sizeof(Value)); namespace Heap { -struct QmlContext; - #define ExecutionContextMembers(class, Member) \ Member(class, Pointer, ExecutionContext *, outer) \ Member(class, Pointer, Object *, activation) @@ -124,9 +107,9 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) { enum ContextType { Type_GlobalContext = 0x1, - Type_CatchContext = 0x2, - Type_WithContext = 0x3, - Type_QmlContext = 0x4, + Type_WithContext = 0x2, + Type_QmlContext = 0x3, + Type_BlockContext = 0x4, Type_CallContext = 0x5 }; @@ -137,6 +120,10 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) { type = t; } + const VTable *vtable() const { + return internalClass->vtable; + } + quint32 type : 8; quint32 nArgs : 24; #if QT_POINTER_SIZE == 8 @@ -181,16 +168,6 @@ Q_STATIC_ASSERT(offsetof(CallContextData, function) == 0); //Q_STATIC_ASSERT(sizeof(CallContext) == sizeof(ExecutionContext) + sizeof(CallContextData)); //#endif -#define CatchContextMembers(class, Member) \ - Member(class, Pointer, String *, exceptionVarName) \ - Member(class, HeapValue, HeapValue, exceptionValue) - -DECLARE_HEAP_OBJECT(CatchContext, ExecutionContext) { - DECLARE_MARKOBJECTS(CatchContext); - - void init(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue); -}; -Q_STATIC_ASSERT(std::is_trivial< CatchContext >::value); } @@ -204,9 +181,11 @@ struct Q_QML_EXPORT ExecutionContext : public Managed Q_MANAGED_TYPE(ExecutionContext) V4_INTERNALCLASS(ExecutionContext) + static Heap::CallContext *newBlockContext(QV4::CppStackFrame *frame, int blockIndex); + static Heap::CallContext *cloneBlockContext(Heap::CallContext *context); static Heap::CallContext *newCallContext(QV4::CppStackFrame *frame); Heap::ExecutionContext *newWithContext(Heap::Object *with); - Heap::CatchContext *newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue); + static Heap::ExecutionContext *newCatchContext(CppStackFrame *frame, int blockIndex, Heap::String *exceptionVarName); void createMutableBinding(String *name, bool deletable); @@ -239,11 +218,6 @@ struct Q_QML_EXPORT CallContext : public ExecutionContext } }; -struct CatchContext : public ExecutionContext -{ - V4_MANAGED(CatchContext, ExecutionContext) -}; - inline CallContext *ExecutionContext::asCallContext() { return d()->type == Heap::ExecutionContext::Type_CallContext ? static_cast<CallContext *>(this) : nullptr; diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp index d894d909ff..354eaad7dc 100644 --- a/src/qml/jsruntime/qv4dataview.cpp +++ b/src/qml/jsruntime/qv4dataview.cpp @@ -40,6 +40,7 @@ #include "qv4dataview_p.h" #include "qv4arraybuffer_p.h" #include "qv4string_p.h" +#include "qv4symbol_p.h" #include <QtCore/private/qnumeric_p.h> #include "qendian.h" @@ -69,7 +70,7 @@ ReturnedValue DataViewCtor::callAsConstructor(const FunctionObject *f, const Val if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength) return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range")); - Scoped<DataView> a(scope, scope.engine->memoryManager->allocObject<DataView>()); + 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; @@ -85,38 +86,41 @@ void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(3)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(3)); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); defineDefaultProperty(engine->id_constructor(), (o = ctor)); defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr); defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr); defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr); - defineDefaultProperty(QStringLiteral("getInt8"), method_getChar<signed char>, 0); - defineDefaultProperty(QStringLiteral("getUint8"), method_getChar<unsigned char>, 0); - defineDefaultProperty(QStringLiteral("getInt16"), method_get<short>, 0); - defineDefaultProperty(QStringLiteral("getUint16"), method_get<unsigned short>, 0); - defineDefaultProperty(QStringLiteral("getInt32"), method_get<int>, 0); - defineDefaultProperty(QStringLiteral("getUint32"), method_get<unsigned int>, 0); - defineDefaultProperty(QStringLiteral("getFloat32"), method_getFloat<float>, 0); - defineDefaultProperty(QStringLiteral("getFloat64"), method_getFloat<double>, 0); - - defineDefaultProperty(QStringLiteral("setInt8"), method_setChar<signed char>, 0); - defineDefaultProperty(QStringLiteral("setUint8"), method_setChar<unsigned char>, 0); - defineDefaultProperty(QStringLiteral("setInt16"), method_set<short>, 0); - defineDefaultProperty(QStringLiteral("setUint16"), method_set<unsigned short>, 0); - defineDefaultProperty(QStringLiteral("setInt32"), method_set<int>, 0); - defineDefaultProperty(QStringLiteral("setUint32"), method_set<unsigned int>, 0); - defineDefaultProperty(QStringLiteral("setFloat32"), method_setFloat<float>, 0); - defineDefaultProperty(QStringLiteral("setFloat64"), method_setFloat<double>, 0); + defineDefaultProperty(QStringLiteral("getInt8"), method_getChar<signed char>, 1); + defineDefaultProperty(QStringLiteral("getUint8"), method_getChar<unsigned char>, 1); + defineDefaultProperty(QStringLiteral("getInt16"), method_get<short>, 1); + defineDefaultProperty(QStringLiteral("getUint16"), method_get<unsigned short>, 1); + defineDefaultProperty(QStringLiteral("getInt32"), method_get<int>, 1); + defineDefaultProperty(QStringLiteral("getUint32"), method_get<unsigned int>, 1); + defineDefaultProperty(QStringLiteral("getFloat32"), method_getFloat<float>, 1); + defineDefaultProperty(QStringLiteral("getFloat64"), method_getFloat<double>, 1); + + defineDefaultProperty(QStringLiteral("setInt8"), method_setChar<signed char>, 2); + defineDefaultProperty(QStringLiteral("setUint8"), method_setChar<unsigned char>, 2); + defineDefaultProperty(QStringLiteral("setInt16"), method_set<short>, 2); + defineDefaultProperty(QStringLiteral("setUint16"), method_set<unsigned short>, 2); + defineDefaultProperty(QStringLiteral("setInt32"), method_set<int>, 2); + defineDefaultProperty(QStringLiteral("setUint32"), method_set<unsigned int>, 2); + defineDefaultProperty(QStringLiteral("setFloat32"), method_setFloat<float>, 2); + defineDefaultProperty(QStringLiteral("setFloat64"), method_setFloat<double>, 2); + + ScopedString name(scope, engine->newString(QStringLiteral("DataView"))); + defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name); // For backword compatibility - defineDefaultProperty(QStringLiteral("getUInt8"), method_getChar<unsigned char>, 0); - defineDefaultProperty(QStringLiteral("getUInt16"), method_get<unsigned short>, 0); - defineDefaultProperty(QStringLiteral("getUInt32"), method_get<unsigned int>, 0); - defineDefaultProperty(QStringLiteral("setUInt8"), method_setChar<unsigned char>, 0); - defineDefaultProperty(QStringLiteral("setUInt16"), method_set<unsigned short>, 0); - defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 0); + defineDefaultProperty(QStringLiteral("getUInt8"), method_getChar<unsigned char>, 1); + defineDefaultProperty(QStringLiteral("getUInt16"), method_get<unsigned short>, 1); + defineDefaultProperty(QStringLiteral("getUInt32"), method_get<unsigned int>, 1); + defineDefaultProperty(QStringLiteral("setUInt8"), method_setChar<unsigned char>, 1); + defineDefaultProperty(QStringLiteral("setUInt16"), method_set<unsigned short>, 1); + defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 1); } ReturnedValue DataViewPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int) diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 5bbe312146..092c36e52a 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -44,6 +44,7 @@ #include "qv4runtime_p.h" #include "qv4string_p.h" #include "qv4jscall_p.h" +#include "qv4symbol_p.h" #include <QtCore/QDebug> #include <QtCore/QDateTime> @@ -89,9 +90,6 @@ static const double msPerMinute = 60000.0; static const double msPerHour = 3600000.0; static const double msPerDay = 86400000.0; -// The current *standard* time offset, regardless of DST: -static double LocalTZA = 0.0; // initialized at startup - static inline double TimeWithinDay(double t) { double r = ::fmod(t, msPerDay); @@ -318,14 +316,14 @@ static inline double MakeDate(double day, double time) against the ECMAScript spec is https://github.com/tc39/ecma262/issues/725 */ -static inline double DaylightSavingTA(double t) // t is a UTC time +static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC time { return QTimeZone::systemTimeZone().offsetFromUtc( - QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC)) * 1e3 - LocalTZA; + QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC)) * 1e3 - localTZA; } #else // This implementation fails to take account of past changes in standard offset. -static inline double DaylightSavingTA(double t) +static inline double DaylightSavingTA(double t, double /*localTZA*/) { struct tm tmtm; #if defined(Q_CC_MSVC) @@ -348,19 +346,19 @@ static inline double DaylightSavingTA(double t) } #endif // USE_QTZ_SYSTEM_ZONE -static inline double LocalTime(double t) +static inline double LocalTime(double t, double localTZA) { // Flawed, yet verbatim from the spec: - return t + LocalTZA + DaylightSavingTA(t); + return t + localTZA + DaylightSavingTA(t, localTZA); } // The spec does note [*] that UTC and LocalTime are not quite mutually inverse. // [*] http://www.ecma-international.org/ecma-262/7.0/index.html#sec-utc-t -static inline double UTC(double t) +static inline double UTC(double t, double localTZA) { // Flawed, yet verbatim from the spec: - return t - LocalTZA - DaylightSavingTA(t - LocalTZA); + return t - localTZA - DaylightSavingTA(t - localTZA, localTZA); } static inline double currentTime() @@ -377,7 +375,7 @@ static inline double TimeClip(double t) return Primitive::toInteger(t) + 0; } -static inline double ParseString(const QString &s) +static inline double ParseString(const QString &s, double localTZA) { /* First, try the format defined in ECMA 262's "Date Time String Format"; @@ -533,7 +531,7 @@ static inline double ParseString(const QString &s) if (seenZ) t -= offset * offsetSign * 60 * 1000; else if (seenT) // No zone specified, treat date-time as local time - t = UTC(t); + t = UTC(t, localTZA); // else: treat plain date as already in UTC return t; } @@ -621,12 +619,12 @@ static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec) return QDateTime::fromMSecsSinceEpoch(t, Qt::UTC).toTimeSpec(spec); } -static inline QString ToString(double t) +static inline QString ToString(double t, double localTZA) { if (std::isnan(t)) return QStringLiteral("Invalid Date"); QString str = ToDateTime(t, Qt::LocalTime).toString() + QLatin1String(" GMT"); - double tzoffset = LocalTZA + DaylightSavingTA(t); + double tzoffset = localTZA + DaylightSavingTA(t, localTZA); if (tzoffset) { int hours = static_cast<int>(::fabs(tzoffset) / 1000 / 60 / 60); int mins = int(::fabs(tzoffset) / 1000 / 60) % 60; @@ -730,7 +728,7 @@ void Heap::DateObject::init(const QTime &time) */ static const double d = MakeDay(1925, 5, 8); double t = MakeTime(time.hour(), time.minute(), time.second(), time.msec()); - date = TimeClip(UTC(MakeDate(d, t))); + date = TimeClip(UTC(MakeDate(d, t), internalClass->engine->localTZA)); } QDateTime DateObject::toQDateTime() const @@ -747,13 +745,14 @@ void Heap::DateCtor::init(QV4::ExecutionContext *scope) ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Value *argv, int argc) { + ExecutionEngine *e = that->engine(); double t = 0; if (argc == 0) t = currentTime(); else if (argc == 1) { - Scope scope(that->engine()); + Scope scope(e); ScopedValue arg(scope, argv[0]); if (DateObject *d = arg->as<DateObject>()) { t = d->date(); @@ -761,7 +760,7 @@ ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Valu arg = RuntimeHelpers::toPrimitive(arg, PREFERREDTYPE_HINT); if (String *s = arg->stringValue()) - t = ParseString(s->toQString()); + t = ParseString(s->toQString(), e->localTZA); else t = TimeClip(arg->toNumber()); } @@ -778,16 +777,17 @@ ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Valu if (year >= 0 && year <= 99) year += 1900; t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); - t = TimeClip(UTC(t)); + t = TimeClip(UTC(t, e->localTZA)); } - return Encode(that->engine()->newDateObject(Primitive::fromDouble(t))); + return Encode(e->newDateObject(Primitive::fromDouble(t))); } ReturnedValue DateCtor::call(const FunctionObject *m, const Value *, const Value *, int) { + ExecutionEngine *e = m->engine(); double t = currentTime(); - return m->engine()->newString(ToString(t))->asReturnedValue(); + return e->newString(ToString(t, e->localTZA))->asReturnedValue(); } void DatePrototype::init(ExecutionEngine *engine, Object *ctor) @@ -796,7 +796,7 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor) ScopedObject o(scope); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(7)); - LocalTZA = getLocalTZA(); + engine->localTZA = getLocalTZA(); ctor->defineDefaultProperty(QStringLiteral("parse"), method_parse, 1); ctor->defineDefaultProperty(QStringLiteral("UTC"), method_UTC, 7); @@ -853,15 +853,14 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor) QString toGmtString(QStringLiteral("toGMTString")); ScopedString us(scope, engine->newIdentifier(toUtcString)); ScopedString gs(scope, engine->newIdentifier(toGmtString)); - ExecutionContext *global = engine->rootContext(); - ScopedFunctionObject toUtcGmtStringFn(scope, FunctionObject::createBuiltinFunction(global, us, method_toUTCString)); - toUtcGmtStringFn->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); + ScopedFunctionObject toUtcGmtStringFn(scope, FunctionObject::createBuiltinFunction(engine, us, method_toUTCString, 0)); defineDefaultProperty(us, toUtcGmtStringFn); defineDefaultProperty(gs, toUtcGmtStringFn); } defineDefaultProperty(QStringLiteral("toISOString"), method_toISOString, 0); defineDefaultProperty(QStringLiteral("toJSON"), method_toJSON, 1); + defineDefaultProperty(engine->symbol_toPrimitive(), method_symbolToPrimitive, 1, Attr_ReadOnly_ButConfigurable); } double DatePrototype::getThisDate(ExecutionEngine *v4, const Value *thisObject) @@ -872,12 +871,12 @@ double DatePrototype::getThisDate(ExecutionEngine *v4, const Value *thisObject) return 0; } -ReturnedValue DatePrototype::method_parse(const FunctionObject *, const Value *, const Value *argv, int argc) +ReturnedValue DatePrototype::method_parse(const FunctionObject *f, const Value *, const Value *argv, int argc) { if (!argc) return Encode(qt_qnan()); else - return Encode(ParseString(argv[0].toQString())); + return Encode(ParseString(argv[0].toQString(), f->engine()->localTZA)); } ReturnedValue DatePrototype::method_UTC(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -909,7 +908,7 @@ ReturnedValue DatePrototype::method_toString(const FunctionObject *b, const Valu { ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); - return Encode(v4->newString(ToString(t))); + return Encode(v4->newString(ToString(t, v4->localTZA))); } ReturnedValue DatePrototype::method_toDateString(const FunctionObject *b, const Value *thisObject, const Value *, int) @@ -966,7 +965,7 @@ ReturnedValue DatePrototype::method_getYear(const FunctionObject *b, const Value ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = YearFromTime(LocalTime(t)) - 1900; + t = YearFromTime(LocalTime(t, v4->localTZA)) - 1900; return Encode(t); } @@ -975,7 +974,7 @@ ReturnedValue DatePrototype::method_getFullYear(const FunctionObject *b, const V ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = YearFromTime(LocalTime(t)); + t = YearFromTime(LocalTime(t, v4->localTZA)); return Encode(t); } @@ -993,7 +992,7 @@ ReturnedValue DatePrototype::method_getMonth(const FunctionObject *b, const Valu ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = MonthFromTime(LocalTime(t)); + t = MonthFromTime(LocalTime(t, v4->localTZA)); return Encode(t); } @@ -1011,7 +1010,7 @@ ReturnedValue DatePrototype::method_getDate(const FunctionObject *b, const Value ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = DateFromTime(LocalTime(t)); + t = DateFromTime(LocalTime(t, v4->localTZA)); return Encode(t); } @@ -1029,7 +1028,7 @@ ReturnedValue DatePrototype::method_getDay(const FunctionObject *b, const Value ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = WeekDay(LocalTime(t)); + t = WeekDay(LocalTime(t, v4->localTZA)); return Encode(t); } @@ -1047,7 +1046,7 @@ ReturnedValue DatePrototype::method_getHours(const FunctionObject *b, const Valu ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = HourFromTime(LocalTime(t)); + t = HourFromTime(LocalTime(t, v4->localTZA)); return Encode(t); } @@ -1065,7 +1064,7 @@ ReturnedValue DatePrototype::method_getMinutes(const FunctionObject *b, const Va ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = MinFromTime(LocalTime(t)); + t = MinFromTime(LocalTime(t, v4->localTZA)); return Encode(t); } @@ -1083,7 +1082,7 @@ ReturnedValue DatePrototype::method_getSeconds(const FunctionObject *b, const Va ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = SecFromTime(LocalTime(t)); + t = SecFromTime(LocalTime(t, v4->localTZA)); return Encode(t); } @@ -1101,7 +1100,7 @@ ReturnedValue DatePrototype::method_getMilliseconds(const FunctionObject *b, con ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = msFromTime(LocalTime(t)); + t = msFromTime(LocalTime(t, v4->localTZA)); return Encode(t); } @@ -1119,7 +1118,7 @@ ReturnedValue DatePrototype::method_getTimezoneOffset(const FunctionObject *b, c ExecutionEngine *v4 = b->engine(); double t = getThisDate(v4, thisObject); if (!std::isnan(t)) - t = (t - LocalTime(t)) / msPerMinute; + t = (t - LocalTime(t, v4->localTZA)) / msPerMinute; return Encode(t); } @@ -1144,13 +1143,13 @@ ReturnedValue DatePrototype::method_setMilliseconds(const FunctionObject *b, con if (!self) return v4->throwTypeError(); - double t = LocalTime(self->date()); + double t = LocalTime(self->date(), v4->localTZA); if (v4->hasException) return QV4::Encode::undefined(); double ms = argc ? argv[0].toNumber() : qt_qnan(); if (v4->hasException) return QV4::Encode::undefined(); - self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); + self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)), v4->localTZA))); return Encode(self->date()); } @@ -1178,7 +1177,7 @@ ReturnedValue DatePrototype::method_setSeconds(const FunctionObject *b, const Va if (!self) return v4->throwTypeError(); - double t = LocalTime(self->date()); + double t = LocalTime(self->date(), v4->localTZA); if (v4->hasException) return QV4::Encode::undefined(); double sec = argc ? argv[0].toNumber() : qt_qnan(); @@ -1187,7 +1186,7 @@ ReturnedValue DatePrototype::method_setSeconds(const FunctionObject *b, const Va double ms = (argc < 2) ? msFromTime(t) : argv[1].toNumber(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)), v4->localTZA)); self->setDate(t); return Encode(self->date()); } @@ -1214,7 +1213,7 @@ ReturnedValue DatePrototype::method_setMinutes(const FunctionObject *b, const Va if (!self) return v4->throwTypeError(); - double t = LocalTime(self->date()); + double t = LocalTime(self->date(), v4->localTZA); if (v4->hasException) return QV4::Encode::undefined(); double min = argc ? argv[0].toNumber() : qt_qnan(); @@ -1226,7 +1225,7 @@ ReturnedValue DatePrototype::method_setMinutes(const FunctionObject *b, const Va double ms = (argc < 3) ? msFromTime(t) : argv[2].toNumber(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)), v4->localTZA)); self->setDate(t); return Encode(self->date()); } @@ -1254,7 +1253,7 @@ ReturnedValue DatePrototype::method_setHours(const FunctionObject *b, const Valu if (!self) return v4->throwTypeError(); - double t = LocalTime(self->date()); + double t = LocalTime(self->date(), v4->localTZA); if (v4->hasException) return QV4::Encode::undefined(); double hour = argc ? argv[0].toNumber() : qt_qnan(); @@ -1269,7 +1268,7 @@ ReturnedValue DatePrototype::method_setHours(const FunctionObject *b, const Valu double ms = (argc < 4) ? msFromTime(t) : argv[3].toNumber(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); + t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)), v4->localTZA)); self->setDate(t); return Encode(self->date()); } @@ -1298,13 +1297,13 @@ ReturnedValue DatePrototype::method_setDate(const FunctionObject *b, const Value if (!self) return v4->throwTypeError(); - double t = LocalTime(self->date()); + double t = LocalTime(self->date(), v4->localTZA); if (v4->hasException) return QV4::Encode::undefined(); double date = argc ? argv[0].toNumber() : qt_qnan(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)), v4->localTZA)); self->setDate(t); return Encode(self->date()); } @@ -1334,7 +1333,7 @@ ReturnedValue DatePrototype::method_setMonth(const FunctionObject *b, const Valu if (!self) return v4->throwTypeError(); - double t = LocalTime(self->date()); + double t = LocalTime(self->date(), v4->localTZA); if (v4->hasException) return QV4::Encode::undefined(); double month = argc ? argv[0].toNumber() : qt_qnan(); @@ -1343,7 +1342,7 @@ ReturnedValue DatePrototype::method_setMonth(const FunctionObject *b, const Valu double date = (argc < 2) ? DateFromTime(t) : argv[1].toNumber(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); + t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)), v4->localTZA)); self->setDate(t); return Encode(self->date()); } @@ -1374,7 +1373,7 @@ ReturnedValue DatePrototype::method_setYear(const FunctionObject *b, const Value if (std::isnan(t)) t = 0; else - t = LocalTime(t); + t = LocalTime(t, v4->localTZA); double year = argc ? argv[0].toNumber() : qt_qnan(); double r; if (std::isnan(year)) { @@ -1383,7 +1382,7 @@ ReturnedValue DatePrototype::method_setYear(const FunctionObject *b, const Value if ((Primitive::toInteger(year) >= 0) && (Primitive::toInteger(year) <= 99)) year += 1900; r = MakeDay(year, MonthFromTime(t), DateFromTime(t)); - r = UTC(MakeDate(r, TimeWithinDay(t))); + r = UTC(MakeDate(r, TimeWithinDay(t)), v4->localTZA); r = TimeClip(r); } self->setDate(r); @@ -1413,7 +1412,7 @@ ReturnedValue DatePrototype::method_setFullYear(const FunctionObject *b, const V if (!self) return v4->throwTypeError(); - double t = LocalTime(self->date()); + double t = LocalTime(self->date(), v4->localTZA); if (v4->hasException) return QV4::Encode::undefined(); if (std::isnan(t)) @@ -1427,7 +1426,7 @@ ReturnedValue DatePrototype::method_setFullYear(const FunctionObject *b, const V double date = (argc < 3) ? DateFromTime(t) : argv[2].toNumber(); if (v4->hasException) return QV4::Encode::undefined(); - t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); + t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)), v4->localTZA)); self->setDate(t); return Encode(self->date()); } @@ -1518,7 +1517,23 @@ ReturnedValue DatePrototype::method_toJSON(const FunctionObject *b, const Value return toIso->call(O, nullptr, 0); } -void DatePrototype::timezoneUpdated() +ReturnedValue DatePrototype::method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + ExecutionEngine *e = f->engine(); + if (!thisObject->isObject() || !argc || !argv->isString()) + return e->throwTypeError(); + + String *hint = argv->stringValue(); + Identifier id = hint->identifier(); + if (id == e->id_default()->identifier()) + hint = e->id_string(); + else if (id != e->id_string()->identifier() && id != e->id_number()->identifier()) + return e->throwTypeError(); + + return RuntimeHelpers::ordinaryToPrimitive(e, static_cast<const Object *>(thisObject), hint); +} + +void DatePrototype::timezoneUpdated(ExecutionEngine *e) { - LocalTZA = getLocalTZA(); + e->localTZA = getLocalTZA(); } diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index 2b9a580288..0a5a3954d1 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -101,7 +101,7 @@ struct DateObject: Object { template<> inline const DateObject *Value::as() const { - return isManaged() && m()->vtable()->type == Managed::Type_DateObject ? static_cast<const DateObject *>(this) : nullptr; + return isManaged() && m()->internalClass->vtable->type == Managed::Type_DateObject ? static_cast<const DateObject *>(this) : nullptr; } struct DateCtor: FunctionObject @@ -169,8 +169,9 @@ struct DatePrototype: Object static ReturnedValue method_toUTCString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_toISOString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_toJSON(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *, int); - static void timezoneUpdated(); + static void timezoneUpdated(ExecutionEngine *e); }; } diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 0ed0df89a9..1073a2abab 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -42,6 +42,9 @@ #include <qv4object_p.h> #include <qv4objectproto_p.h> #include <qv4objectiterator_p.h> +#include <qv4setiterator_p.h> +#include <qv4mapiterator_p.h> +#include <qv4arrayiterator_p.h> #include <qv4arrayobject_p.h> #include <qv4booleanobject_p.h> #include <qv4globalobject_p.h> @@ -52,6 +55,9 @@ #include <qv4numberobject_p.h> #include <qv4regexpobject_p.h> #include <qv4regexp_p.h> +#include "qv4symbol_p.h" +#include "qv4setobject_p.h" +#include "qv4mapobject_p.h" #include <qv4variantobject_p.h> #include <qv4runtime_p.h> #include <private/qv4mm_p.h> @@ -63,7 +69,15 @@ #include "qv4debugging_p.h" #include "qv4profiling_p.h" #include "qv4executableallocator_p.h" +#include "qv4iterator_p.h" +#include "qv4stringiterator_p.h" +#include "qv4generatorobject_p.h" +#include "qv4reflect_p.h" + +#if QT_CONFIG(qml_sequence_object) #include "qv4sequenceobject_p.h" +#endif + #include "qv4qobjectwrapper_p.h" #include "qv4memberdata_p.h" #include "qv4arraybuffer_p.h" @@ -76,7 +90,9 @@ #include <private/qqmlvaluetype_p.h> #include <private/qqmllistwrapper_p.h> #include <private/qqmllist_p.h> +#if QT_CONFIG(qml_locale) #include <private/qqmllocale_p.h> +#endif #include <QtCore/QTextStream> #include <QDateTime> @@ -110,7 +126,7 @@ ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const #ifdef V4_BOOTSTRAP QJSEngine *ExecutionEngine::jsEngine() const { - return v8Engine->publicEngine(); + return publicEngine; } QQmlEngine *ExecutionEngine::qmlEngine() const @@ -121,7 +137,7 @@ QQmlEngine *ExecutionEngine::qmlEngine() const qint32 ExecutionEngine::maxCallDepth = -1; -ExecutionEngine::ExecutionEngine() +ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) : executableAllocator(new QV4::ExecutableAllocator) , regExpAllocator(new QV4::ExecutableAllocator) , bumperPointerAllocator(new WTF::BumpPointerAllocator) @@ -129,6 +145,7 @@ ExecutionEngine::ExecutionEngine() , gcStack(new WTF::PageAllocation) , globalCode(nullptr) , v8Engine(nullptr) + , publicEngine(jsEngine) , argumentsAccessors(nullptr) , nArgumentsAccessors(0) , m_engineId(engineSerial.fetchAndAddOrdered(1)) @@ -181,27 +198,44 @@ ExecutionEngine::ExecutionEngine() } exceptionValue = jsAlloca(1); + *exceptionValue = Encode::undefined(); globalObject = static_cast<Object *>(jsAlloca(1)); jsObjects = jsAlloca(NJSObjects); typedArrayPrototype = static_cast<Object *>(jsAlloca(NTypedArrayTypes)); typedArrayCtors = static_cast<FunctionObject *>(jsAlloca(NTypedArrayTypes)); jsStrings = jsAlloca(NJSStrings); + jsSymbols = jsAlloca(NJSSymbols); // set up stack limits jsStackLimit = jsStackBase + JSStackLimit/sizeof(Value); identifierTable = new IdentifierTable(this); - classPool = new InternalClassPool; + memset(classes, 0, sizeof(classes)); + classes[Class_Empty] = memoryManager->allocIC<InternalClass>(); + classes[Class_Empty]->init(this); - internalClasses[Class_Empty] = new (classPool) InternalClass(this); - internalClasses[Class_String] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::String::staticVTable()); - internalClasses[Class_MemberData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::MemberData::staticVTable()); - internalClasses[Class_SimpleArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable()); - internalClasses[Class_SparseArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable()); - internalClasses[Class_ExecutionContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable()); - internalClasses[Class_QmlContext] = internalClasses[EngineBase::Class_ExecutionContext]->changeVTable(QV4::QmlContext::staticVTable()); - internalClasses[Class_CallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable()); + classes[Class_MemberData] = classes[Class_Empty]->changeVTable(QV4::MemberData::staticVTable()); + classes[Class_SimpleArrayData] = classes[Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable()); + classes[Class_SparseArrayData] = classes[Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable()); + classes[Class_ExecutionContext] = classes[Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable()); + classes[Class_CallContext] = classes[Class_Empty]->changeVTable(QV4::CallContext::staticVTable()); + classes[Class_QmlContext] = classes[Class_Empty]->changeVTable(QV4::QmlContext::staticVTable()); + + Scope scope(this); + Scoped<InternalClass> ic(scope); + ic = classes[Class_Empty]->changeVTable(QV4::Object::staticVTable()); + jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(ic->d()); + classes[Class_Object] = ic->changePrototype(objectPrototype()->d()); + classes[Class_QmlContextWrapper] = classes[Class_Object]->changeVTable(QV4::QQmlContextWrapper::staticVTable()); + + ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype()); + jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(ic->d(), /*init =*/ false); + classes[Class_String] = classes[Class_Empty]->changeVTable(QV4::String::staticVTable())->changePrototype(stringPrototype()->d()); + Q_ASSERT(stringPrototype()->d() && classes[Class_String]->prototype); + + jsObjects[SymbolProto] = memoryManager->allocate<SymbolPrototype>(); + classes[Class_Symbol] = classes[EngineBase::Class_Empty]->changeVTable(QV4::Symbol::staticVTable())->changePrototype(symbolPrototype()->d()); jsStrings[String_Empty] = newIdentifier(QString()); jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined")); @@ -211,6 +245,8 @@ ExecutionEngine::ExecutionEngine() jsStrings[String_boolean] = newIdentifier(QStringLiteral("boolean")); jsStrings[String_number] = newIdentifier(QStringLiteral("number")); jsStrings[String_string] = newIdentifier(QStringLiteral("string")); + jsStrings[String_default] = newIdentifier(QStringLiteral("default")); + jsStrings[String_symbol] = newIdentifier(QStringLiteral("symbol")); jsStrings[String_object] = newIdentifier(QStringLiteral("object")); jsStrings[String_function] = newIdentifier(QStringLiteral("function")); jsStrings[String_length] = newIdentifier(QStringLiteral("length")); @@ -239,141 +275,175 @@ ExecutionEngine::ExecutionEngine() jsStrings[String_byteOffset] = newIdentifier(QStringLiteral("byteOffset")); jsStrings[String_buffer] = newIdentifier(QStringLiteral("buffer")); jsStrings[String_lastIndex] = newIdentifier(QStringLiteral("lastIndex")); - - InternalClass *ic = internalClasses[Class_Empty]->changeVTable(QV4::Object::staticVTable()); - jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(ic); - internalClasses[Class_Object] = ic->changePrototype(objectPrototype()->d()); - internalClasses[EngineBase::Class_QmlContextWrapper] = internalClasses[Class_Object]->changeVTable(QV4::QQmlContextWrapper::staticVTable()); + jsStrings[String_next] = newIdentifier(QStringLiteral("next")); + jsStrings[String_done] = newIdentifier(QStringLiteral("done")); + jsStrings[String_return] = newIdentifier(QStringLiteral("return")); + + jsSymbols[Symbol_hasInstance] = Symbol::create(this, QStringLiteral("@Symbol.hasInstance")); + jsSymbols[Symbol_isConcatSpreadable] = Symbol::create(this, QStringLiteral("@Symbol.isConcatSpreadable")); + jsSymbols[Symbol_iterator] = Symbol::create(this, QStringLiteral("@Symbol.iterator")); + jsSymbols[Symbol_match] = Symbol::create(this, QStringLiteral("@Symbol.match")); + jsSymbols[Symbol_replace] = Symbol::create(this, QStringLiteral("@Symbol.replace")); + jsSymbols[Symbol_search] = Symbol::create(this, QStringLiteral("@Symbol.search")); + jsSymbols[Symbol_species] = Symbol::create(this, QStringLiteral("@Symbol.species")); + jsSymbols[Symbol_split] = Symbol::create(this, QStringLiteral("@Symbol.split")); + jsSymbols[Symbol_toPrimitive] = Symbol::create(this, QStringLiteral("@Symbol.toPrimitive")); + jsSymbols[Symbol_toStringTag] = Symbol::create(this, QStringLiteral("@Symbol.toStringTag")); + jsSymbols[Symbol_unscopables] = Symbol::create(this, QStringLiteral("@Symbol.unscopables")); ic = newInternalClass(ArrayPrototype::staticVTable(), objectPrototype()); - Q_ASSERT(ic->prototype); - ic = ic->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable); - Q_ASSERT(ic->prototype); - jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(ic, objectPrototype()); - internalClasses[Class_ArrayObject] = ic->changePrototype(arrayPrototype()->d()); - jsObjects[PropertyListProto] = memoryManager->allocObject<PropertyListPrototype>(); - - InternalClass *argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype()); - argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable); - internalClasses[EngineBase::Class_ArgumentsObject] = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable); + Q_ASSERT(ic->d()->prototype); + ic = ic->addMember(id_length()->identifier(), Attr_NotConfigurable|Attr_NotEnumerable); + Q_ASSERT(ic->d()->prototype); + jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(ic->d()); + classes[Class_ArrayObject] = ic->changePrototype(arrayPrototype()->d()); + jsObjects[PropertyListProto] = memoryManager->allocate<PropertyListPrototype>(); + + Scoped<InternalClass> argsClass(scope); + argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype()); + argsClass = argsClass->addMember(id_length()->identifier(), Attr_NotEnumerable); + argsClass = argsClass->addMember(symbol_iterator()->identifier(), Attr_Data|Attr_NotEnumerable); + classes[Class_ArgumentsObject] = argsClass->addMember(id_callee()->identifier(), Attr_Data|Attr_NotEnumerable); argsClass = newInternalClass(StrictArgumentsObject::staticVTable(), objectPrototype()); - argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable); - argsClass = argsClass->addMember(id_callee(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - internalClasses[EngineBase::Class_StrictArgumentsObject] = argsClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + argsClass = argsClass->addMember(id_length()->identifier(), Attr_NotEnumerable); + argsClass = argsClass->addMember(symbol_iterator()->identifier(), Attr_Data|Attr_NotEnumerable); + classes[Class_StrictArgumentsObject] = argsClass->addMember(id_callee()->identifier(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); *static_cast<Value *>(globalObject) = newObject(); Q_ASSERT(globalObject->d()->vtable()); initRootContext(); ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype()); - ic = ic->addMember(id_length(), Attr_ReadOnly); - jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(ic); - internalClasses[Class_StringObject] = ic->changePrototype(stringPrototype()->d()); - Q_ASSERT(internalClasses[EngineBase::Class_StringObject]->find(id_length()) == Heap::StringObject::LengthPropertyIndex); + ic = ic->addMember(id_length()->identifier(), Attr_ReadOnly); + classes[Class_StringObject] = ic->changePrototype(stringPrototype()->d()); + Q_ASSERT(classes[Class_StringObject]->find(id_length()->identifier()) == Heap::StringObject::LengthPropertyIndex); - jsObjects[NumberProto] = memoryManager->allocObject<NumberPrototype>(); - jsObjects[BooleanProto] = memoryManager->allocObject<BooleanPrototype>(); - jsObjects[DateProto] = memoryManager->allocObject<DatePrototype>(); + classes[Class_SymbolObject] = newInternalClass(QV4::SymbolObject::staticVTable(), symbolPrototype()); + + jsObjects[NumberProto] = memoryManager->allocate<NumberPrototype>(); + jsObjects[BooleanProto] = memoryManager->allocate<BooleanPrototype>(); + jsObjects[DateProto] = memoryManager->allocate<DatePrototype>(); uint index; ic = newInternalClass(QV4::FunctionPrototype::staticVTable(), objectPrototype()); - ic = ic->addMember(id_prototype(), Attr_NotEnumerable, &index); + ic = ic->addMember(id_prototype()->identifier(), Attr_NotEnumerable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_Prototype); - jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(ic, objectPrototype()); + jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(ic->d()); ic = newInternalClass(FunctionObject::staticVTable(), functionPrototype()); - ic = ic->addMember(id_prototype(), Attr_NotEnumerable|Attr_NotConfigurable, &index); + ic = ic->addMember(id_prototype()->identifier(), Attr_NotEnumerable|Attr_NotConfigurable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_Prototype); - internalClasses[EngineBase::Class_FunctionObject] = ic; - ic = ic->addMember(id_name(), Attr_ReadOnly, &index); + classes[Class_FunctionObject] = ic->d(); + ic = ic->addMember(id_name()->identifier(), Attr_ReadOnly, &index); Q_ASSERT(index == Heap::ScriptFunction::Index_Name); ic = ic->changeVTable(ScriptFunction::staticVTable()); - internalClasses[EngineBase::Class_ScriptFunction] = ic->addMember(id_length(), Attr_ReadOnly, &index); + ic = ic->addMember(id_length()->identifier(), Attr_ReadOnly_ButConfigurable, &index); Q_ASSERT(index == Heap::ScriptFunction::Index_Length); - internalClasses[EngineBase::Class_ObjectProto] = internalClasses[Class_Object]->addMember(id_constructor(), Attr_NotEnumerable, &index); + classes[Class_ScriptFunction] = ic->d(); + ic = ic->changeVTable(GeneratorFunction::staticVTable()); + classes[Class_GeneratorFunction] = ic->d(); + classes[Class_ObjectProto] = classes[Class_Object]->addMember(id_constructor()->identifier(), Attr_NotEnumerable, &index); Q_ASSERT(index == Heap::FunctionObject::Index_ProtoConstructor); - Scope scope(this); + jsObjects[GeneratorProto] = memoryManager->allocObject<GeneratorPrototype>(classes[Class_Object]); + classes[Class_GeneratorObject] = newInternalClass(QV4::GeneratorObject::staticVTable(), generatorPrototype()); + ScopedString str(scope); - internalClasses[Class_RegExp] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::RegExp::staticVTable()); + classes[Class_RegExp] = classes[Class_Empty]->changeVTable(QV4::RegExp::staticVTable()); ic = newInternalClass(QV4::RegExpObject::staticVTable(), objectPrototype()); - ic = ic->addMember(id_lastIndex(), Attr_NotEnumerable|Attr_NotConfigurable, &index); + ic = ic->addMember(id_lastIndex()->identifier(), Attr_NotEnumerable|Attr_NotConfigurable, &index); Q_ASSERT(index == RegExpObject::Index_LastIndex); - ic = ic->addMember((str = newIdentifier(QStringLiteral("source"))), Attr_ReadOnly, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("source")))->identifier(), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_Source); - ic = ic->addMember((str = newIdentifier(QStringLiteral("global"))), Attr_ReadOnly, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("global")))->identifier(), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_Global); - ic = ic->addMember((str = newIdentifier(QStringLiteral("ignoreCase"))), Attr_ReadOnly, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("ignoreCase")))->identifier(), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_IgnoreCase); - ic = ic->addMember((str = newIdentifier(QStringLiteral("multiline"))), Attr_ReadOnly, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("multiline")))->identifier(), Attr_ReadOnly, &index); Q_ASSERT(index == RegExpObject::Index_Multiline); - jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(ic, objectPrototype()); - internalClasses[Class_RegExpObject] = ic->changePrototype(regExpPrototype()->d()); + jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(ic->d()); + classes[Class_RegExpObject] = ic->changePrototype(regExpPrototype()->d()); - ic = internalClasses[Class_ArrayObject]->addMember(id_index(), Attr_Data, &index); + ic = classes[Class_ArrayObject]->addMember(id_index()->identifier(), Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayIndex); - internalClasses[EngineBase::Class_RegExpExecArray] = ic->addMember(id_input(), Attr_Data, &index); + classes[Class_RegExpExecArray] = ic->addMember(id_input()->identifier(), Attr_Data, &index); Q_ASSERT(index == RegExpObject::Index_ArrayInput); ic = newInternalClass(ErrorObject::staticVTable(), nullptr); - ic = ic->addMember((str = newIdentifier(QStringLiteral("stack"))), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("stack")))->identifier(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_Stack); - ic = ic->addMember((str = newIdentifier(QStringLiteral("fileName"))), Attr_Data|Attr_NotEnumerable, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("fileName")))->identifier(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_FileName); - ic = ic->addMember((str = newIdentifier(QStringLiteral("lineNumber"))), Attr_Data|Attr_NotEnumerable, &index); - internalClasses[EngineBase::Class_ErrorObject] = ic; + ic = ic->addMember((str = newIdentifier(QStringLiteral("lineNumber")))->identifier(), Attr_Data|Attr_NotEnumerable, &index); + classes[Class_ErrorObject] = ic->d(); Q_ASSERT(index == ErrorObject::Index_LineNumber); - internalClasses[EngineBase::Class_ErrorObjectWithMessage] = ic->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); + classes[Class_ErrorObjectWithMessage] = ic->addMember((str = newIdentifier(QStringLiteral("message")))->identifier(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorObject::Index_Message); ic = newInternalClass(ErrorObject::staticVTable(), objectPrototype()); - ic = ic->addMember(id_constructor(), Attr_Data|Attr_NotEnumerable, &index); + ic = ic->addMember(id_constructor()->identifier(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Constructor); - ic = ic->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index); + ic = ic->addMember((str = newIdentifier(QStringLiteral("message")))->identifier(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Message); - internalClasses[EngineBase::Class_ErrorProto] = ic->addMember(id_name(), Attr_Data|Attr_NotEnumerable, &index); + classes[Class_ErrorProto] = ic->addMember(id_name()->identifier(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Name); - jsObjects[GetStack_Function] = FunctionObject::createBuiltinFunction(rootContext(), str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack); - getStackFunction()->defineReadonlyProperty(id_length(), Primitive::fromInt32(0)); + jsObjects[GetStack_Function] = FunctionObject::createBuiltinFunction(this, str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack, 0); - jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto], objectPrototype()); - jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); - jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); - jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); - jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); - jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); - jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype()); + jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(classes[Class_ErrorProto]); + ic = classes[Class_ErrorProto]->changePrototype(errorPrototype()->d()); + jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(ic->d()); + jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(ic->d()); + jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(ic->d()); + jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(ic->d()); + jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(ic->d()); + jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(ic->d()); - jsObjects[VariantProto] = memoryManager->allocObject<VariantPrototype>(); + jsObjects[VariantProto] = memoryManager->allocate<VariantPrototype>(); Q_ASSERT(variantPrototype()->prototype() == objectPrototype()->d()); +#if QT_CONFIG(qml_sequence_object) ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this)); - jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic, SequencePrototype::defaultPrototype(this))); + jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic->d())); +#endif ExecutionContext *global = rootContext(); - jsObjects[Object_Ctor] = memoryManager->allocObject<ObjectCtor>(global); - jsObjects[String_Ctor] = memoryManager->allocObject<StringCtor>(global); - jsObjects[Number_Ctor] = memoryManager->allocObject<NumberCtor>(global); - jsObjects[Boolean_Ctor] = memoryManager->allocObject<BooleanCtor>(global); - jsObjects[Array_Ctor] = memoryManager->allocObject<ArrayCtor>(global); - jsObjects[Function_Ctor] = memoryManager->allocObject<FunctionCtor>(global); - jsObjects[Date_Ctor] = memoryManager->allocObject<DateCtor>(global); - jsObjects[RegExp_Ctor] = memoryManager->allocObject<RegExpCtor>(global); - jsObjects[Error_Ctor] = memoryManager->allocObject<ErrorCtor>(global); - jsObjects[EvalError_Ctor] = memoryManager->allocObject<EvalErrorCtor>(global); - jsObjects[RangeError_Ctor] = memoryManager->allocObject<RangeErrorCtor>(global); - jsObjects[ReferenceError_Ctor] = memoryManager->allocObject<ReferenceErrorCtor>(global); - jsObjects[SyntaxError_Ctor] = memoryManager->allocObject<SyntaxErrorCtor>(global); - jsObjects[TypeError_Ctor] = memoryManager->allocObject<TypeErrorCtor>(global); - jsObjects[URIError_Ctor] = memoryManager->allocObject<URIErrorCtor>(global); + + jsObjects[Object_Ctor] = memoryManager->allocate<ObjectCtor>(global); + jsObjects[String_Ctor] = memoryManager->allocate<StringCtor>(global); + jsObjects[Symbol_Ctor] = memoryManager->allocate<SymbolCtor>(global); + jsObjects[Number_Ctor] = memoryManager->allocate<NumberCtor>(global); + jsObjects[Boolean_Ctor] = memoryManager->allocate<BooleanCtor>(global); + jsObjects[Array_Ctor] = memoryManager->allocate<ArrayCtor>(global); + jsObjects[Function_Ctor] = memoryManager->allocate<FunctionCtor>(global); + jsObjects[GeneratorFunction_Ctor] = memoryManager->allocate<GeneratorFunctionCtor>(global); + jsObjects[Date_Ctor] = memoryManager->allocate<DateCtor>(global); + jsObjects[RegExp_Ctor] = memoryManager->allocate<RegExpCtor>(global); + jsObjects[Error_Ctor] = memoryManager->allocate<ErrorCtor>(global); + jsObjects[EvalError_Ctor] = memoryManager->allocate<EvalErrorCtor>(global); + jsObjects[RangeError_Ctor] = memoryManager->allocate<RangeErrorCtor>(global); + jsObjects[ReferenceError_Ctor] = memoryManager->allocate<ReferenceErrorCtor>(global); + jsObjects[SyntaxError_Ctor] = memoryManager->allocate<SyntaxErrorCtor>(global); + jsObjects[TypeError_Ctor] = memoryManager->allocate<TypeErrorCtor>(global); + jsObjects[URIError_Ctor] = memoryManager->allocate<URIErrorCtor>(global); + jsObjects[IteratorProto] = memoryManager->allocate<IteratorPrototype>(); + jsObjects[ForInIteratorProto] = memoryManager->allocObject<ForInIteratorPrototype>(newInternalClass(ForInIteratorPrototype::staticVTable(), iteratorPrototype())); + jsObjects[MapIteratorProto] = memoryManager->allocObject<MapIteratorPrototype>(newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype())); + jsObjects[SetIteratorProto] = memoryManager->allocObject<SetIteratorPrototype>(newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype())); + jsObjects[ArrayIteratorProto] = memoryManager->allocObject<ArrayIteratorPrototype>(newInternalClass(ArrayIteratorPrototype::staticVTable(), iteratorPrototype())); + jsObjects[StringIteratorProto] = memoryManager->allocObject<StringIteratorPrototype>(newInternalClass(StringIteratorPrototype::staticVTable(), iteratorPrototype())); + + str = newString(QStringLiteral("get [Symbol.species]")); + jsObjects[GetSymbolSpecies] = FunctionObject::createBuiltinFunction(this, str, ArrayPrototype::method_get_species, 0); static_cast<ObjectPrototype *>(objectPrototype())->init(this, objectCtor()); static_cast<StringPrototype *>(stringPrototype())->init(this, stringCtor()); + static_cast<SymbolPrototype *>(symbolPrototype())->init(this, symbolCtor()); static_cast<NumberPrototype *>(numberPrototype())->init(this, numberCtor()); static_cast<BooleanPrototype *>(booleanPrototype())->init(this, booleanCtor()); static_cast<ArrayPrototype *>(arrayPrototype())->init(this, arrayCtor()); static_cast<PropertyListPrototype *>(propertyListPrototype())->init(this); static_cast<DatePrototype *>(datePrototype())->init(this, dateCtor()); static_cast<FunctionPrototype *>(functionPrototype())->init(this, functionCtor()); + static_cast<GeneratorPrototype *>(generatorPrototype())->init(this, generatorFunctionCtor()); static_cast<RegExpPrototype *>(regExpPrototype())->init(this, regExpCtor()); static_cast<ErrorPrototype *>(errorPrototype())->init(this, errorCtor()); static_cast<EvalErrorPrototype *>(evalErrorPrototype())->init(this, evalErrorCtor()); @@ -383,25 +453,47 @@ ExecutionEngine::ExecutionEngine() static_cast<TypeErrorPrototype *>(typeErrorPrototype())->init(this, typeErrorCtor()); static_cast<URIErrorPrototype *>(uRIErrorPrototype())->init(this, uRIErrorCtor()); + static_cast<IteratorPrototype *>(iteratorPrototype())->init(this); + static_cast<ForInIteratorPrototype *>(forInIteratorPrototype())->init(this); + static_cast<MapIteratorPrototype *>(mapIteratorPrototype())->init(this); + static_cast<SetIteratorPrototype *>(setIteratorPrototype())->init(this); + static_cast<ArrayIteratorPrototype *>(arrayIteratorPrototype())->init(this); + static_cast<StringIteratorPrototype *>(stringIteratorPrototype())->init(this); + static_cast<VariantPrototype *>(variantPrototype())->init(); + +#if QT_CONFIG(qml_sequence_object) sequencePrototype()->cast<SequencePrototype>()->init(); +#endif + + jsObjects[Map_Ctor] = memoryManager->allocate<MapCtor>(global); + jsObjects[MapProto] = memoryManager->allocate<MapPrototype>(); + static_cast<MapPrototype *>(mapPrototype())->init(this, mapCtor()); + jsObjects[Set_Ctor] = memoryManager->allocate<SetCtor>(global); + jsObjects[SetProto] = memoryManager->allocate<SetPrototype>(); + static_cast<SetPrototype *>(setPrototype())->init(this, setCtor()); // typed arrays - jsObjects[ArrayBuffer_Ctor] = memoryManager->allocObject<ArrayBufferCtor>(global); - jsObjects[ArrayBufferProto] = memoryManager->allocObject<ArrayBufferPrototype>(); + jsObjects[ArrayBuffer_Ctor] = memoryManager->allocate<ArrayBufferCtor>(global); + jsObjects[ArrayBufferProto] = memoryManager->allocate<ArrayBufferPrototype>(); static_cast<ArrayBufferPrototype *>(arrayBufferPrototype())->init(this, arrayBufferCtor()); - jsObjects[DataView_Ctor] = memoryManager->allocObject<DataViewCtor>(global); - jsObjects[DataViewProto] = memoryManager->allocObject<DataViewPrototype>(); + jsObjects[DataView_Ctor] = memoryManager->allocate<DataViewCtor>(global); + jsObjects[DataViewProto] = memoryManager->allocate<DataViewPrototype>(); static_cast<DataViewPrototype *>(dataViewPrototype())->init(this, dataViewCtor()); jsObjects[ValueTypeProto] = (Heap::Base *) nullptr; jsObjects[SignalHandlerProto] = (Heap::Base *) nullptr; + jsObjects[IntrinsicTypedArray_Ctor] = memoryManager->allocate<IntrinsicTypedArrayCtor>(global); + jsObjects[IntrinsicTypedArrayProto] = memoryManager->allocate<IntrinsicTypedArrayPrototype>(); + static_cast<IntrinsicTypedArrayPrototype *>(intrinsicTypedArrayPrototype()) + ->init(this, static_cast<IntrinsicTypedArrayCtor *>(intrinsicTypedArrayCtor())); + for (int i = 0; i < Heap::TypedArray::NTypes; ++i) { - static_cast<Value &>(typedArrayCtors[i]) = memoryManager->allocObject<TypedArrayCtor>(global, Heap::TypedArray::Type(i)); - static_cast<Value &>(typedArrayPrototype[i]) = memoryManager->allocObject<TypedArrayPrototype>(Heap::TypedArray::Type(i)); + static_cast<Value &>(typedArrayCtors[i]) = memoryManager->allocate<TypedArrayCtor>(global, Heap::TypedArray::Type(i)); + static_cast<Value &>(typedArrayPrototype[i]) = memoryManager->allocate<TypedArrayPrototype>(Heap::TypedArray::Type(i)); typedArrayPrototype[i].as<TypedArrayPrototype>()->init(this, static_cast<TypedArrayCtor *>(typedArrayCtors[i].as<Object>())); } @@ -413,6 +505,7 @@ ExecutionEngine::ExecutionEngine() globalObject->defineDefaultProperty(QStringLiteral("Object"), *objectCtor()); globalObject->defineDefaultProperty(QStringLiteral("String"), *stringCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Symbol"), *symbolCtor()); FunctionObject *numberObject = numberCtor(); globalObject->defineDefaultProperty(QStringLiteral("Number"), *numberObject); globalObject->defineDefaultProperty(QStringLiteral("Boolean"), *booleanCtor()); @@ -430,18 +523,22 @@ ExecutionEngine::ExecutionEngine() globalObject->defineDefaultProperty(QStringLiteral("ArrayBuffer"), *arrayBufferCtor()); globalObject->defineDefaultProperty(QStringLiteral("DataView"), *dataViewCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Set"), *setCtor()); + globalObject->defineDefaultProperty(QStringLiteral("Map"), *mapCtor()); + for (int i = 0; i < Heap::TypedArray::NTypes; ++i) globalObject->defineDefaultProperty((str = typedArrayCtors[i].as<FunctionObject>()->name())->toQString(), typedArrayCtors[i]); ScopedObject o(scope); - globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocObject<MathObject>())); - globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocObject<JsonObject>())); + globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocate<MathObject>())); + globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocate<JsonObject>())); + globalObject->defineDefaultProperty(QStringLiteral("Reflect"), (o = memoryManager->allocate<Reflect>())); globalObject->defineReadonlyProperty(QStringLiteral("undefined"), Primitive::undefinedValue()); globalObject->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(std::numeric_limits<double>::quiet_NaN())); globalObject->defineReadonlyProperty(QStringLiteral("Infinity"), Primitive::fromDouble(Q_INFINITY)); - jsObjects[Eval_Function] = memoryManager->allocObject<EvalFunction>(global); + jsObjects[Eval_Function] = memoryManager->allocate<EvalFunction>(global); globalObject->defineDefaultProperty(QStringLiteral("eval"), *evalFunction()); // ES6: 20.1.2.12 & 20.1.2.13: @@ -453,11 +550,8 @@ ExecutionEngine::ExecutionEngine() Scope scope(this); ScopedString pi(scope, newIdentifier(piString)); ScopedString pf(scope, newIdentifier(pfString)); - ExecutionContext *global = rootContext(); - ScopedFunctionObject parseIntFn(scope, FunctionObject::createBuiltinFunction(global, pi, GlobalFunctions::method_parseInt)); - ScopedFunctionObject parseFloatFn(scope, FunctionObject::createBuiltinFunction(global, pf, GlobalFunctions::method_parseFloat)); - parseIntFn->defineReadonlyConfigurableProperty(id_length(), Primitive::fromInt32(2)); - parseFloatFn->defineReadonlyConfigurableProperty(id_length(), Primitive::fromInt32(1)); + ScopedFunctionObject parseIntFn(scope, FunctionObject::createBuiltinFunction(this, pi, GlobalFunctions::method_parseInt, 2)); + ScopedFunctionObject parseFloatFn(scope, FunctionObject::createBuiltinFunction(this, pf, GlobalFunctions::method_parseFloat, 1)); globalObject->defineDefaultProperty(piString, parseIntFn); globalObject->defineDefaultProperty(pfString, parseFloatFn); numberObject->defineDefaultProperty(piString, parseIntFn); @@ -473,8 +567,16 @@ ExecutionEngine::ExecutionEngine() globalObject->defineDefaultProperty(QStringLiteral("escape"), GlobalFunctions::method_escape, 1); globalObject->defineDefaultProperty(QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1); - ScopedString name(scope, newString(QStringLiteral("thrower"))); - jsObjects[ThrowerObject] = FunctionObject::createBuiltinFunction(global, name, ::throwTypeError); + ScopedFunctionObject t(scope, memoryManager->allocate<FunctionObject>(rootContext(), nullptr, ::throwTypeError)); + t->defineReadonlyProperty(id_length(), Primitive::fromInt32(0)); + t->setInternalClass(t->internalClass()->frozen()); + jsObjects[ThrowerObject] = t; + + ScopedProperty pd(scope); + pd->value = thrower(); + pd->set = thrower(); + functionPrototype()->insertMember(id_caller(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable); + functionPrototype()->insertMember(id_arguments(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable); } ExecutionEngine::~ExecutionEngine() @@ -487,8 +589,6 @@ ExecutionEngine::~ExecutionEngine() while (!compilationUnits.isEmpty()) (*compilationUnits.begin())->unlink(); - internalClasses[Class_Empty]->destroy(); - delete classPool; delete bumperPointerAllocator; delete regExpCache; delete regExpAllocator; @@ -521,27 +621,32 @@ void ExecutionEngine::initRootContext() r->d_unchecked()->init(Heap::ExecutionContext::Type_GlobalContext); r->d()->activation.set(this, globalObject->d()); jsObjects[RootContext] = r; + jsObjects[ScriptContext] = r; jsObjects[IntegerNull] = Encode((int)0); } -InternalClass *ExecutionEngine::newClass(const InternalClass &other) +Heap::InternalClass *ExecutionEngine::newClass(Heap::InternalClass *other) { - return new (classPool) InternalClass(other); + Heap::InternalClass *ic = memoryManager->allocIC<InternalClass>(); + ic->init(other); + return ic; } -InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype) +Heap::InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype) { - return internalClasses[EngineBase::Class_Empty]->changeVTable(vtable)->changePrototype(prototype ? prototype->d() : nullptr); + Scope scope(this); + Scoped<InternalClass> ic(scope, internalClasses(Class_Empty)->changeVTable(vtable)); + return ic->changePrototype(prototype ? prototype->d() : nullptr); } Heap::Object *ExecutionEngine::newObject() { - return memoryManager->allocObject<Object>(); + return memoryManager->allocate<Object>(); } -Heap::Object *ExecutionEngine::newObject(InternalClass *internalClass, QV4::Object *prototype) +Heap::Object *ExecutionEngine::newObject(Heap::InternalClass *internalClass) { - return memoryManager->allocObject<Object>(internalClass, prototype); + return memoryManager->allocObject<Object>(internalClass); } Heap::String *ExecutionEngine::newString(const QString &s) @@ -557,23 +662,28 @@ Heap::String *ExecutionEngine::newIdentifier(const QString &text) Heap::Object *ExecutionEngine::newStringObject(const String *string) { - return memoryManager->allocObject<StringObject>(string); + return memoryManager->allocate<StringObject>(string); +} + +Heap::Object *ExecutionEngine::newSymbolObject(const Symbol *symbol) +{ + return memoryManager->allocObject<SymbolObject>(classes[Class_SymbolObject], symbol); } Heap::Object *ExecutionEngine::newNumberObject(double value) { - return memoryManager->allocObject<NumberObject>(value); + return memoryManager->allocate<NumberObject>(value); } Heap::Object *ExecutionEngine::newBooleanObject(bool b) { - return memoryManager->allocObject<BooleanObject>(b); + return memoryManager->allocate<BooleanObject>(b); } Heap::ArrayObject *ExecutionEngine::newArrayObject(int count) { Scope scope(this); - ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>()); + ScopedArrayObject object(scope, memoryManager->allocate<ArrayObject>()); if (count) { if (count < 0x1000) @@ -586,7 +696,7 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(int count) Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int length) { Scope scope(this); - ScopedArrayObject a(scope, memoryManager->allocObject<ArrayObject>()); + ScopedArrayObject a(scope, memoryManager->allocate<ArrayObject>()); if (length) { size_t size = sizeof(Heap::ArrayData) + (length-1)*sizeof(Value); @@ -607,45 +717,41 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int leng Heap::ArrayObject *ExecutionEngine::newArrayObject(const QStringList &list) { - Scope scope(this); - ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>(list)); - return object->d(); + return memoryManager->allocate<ArrayObject>(list); } -Heap::ArrayObject *ExecutionEngine::newArrayObject(InternalClass *internalClass, Object *prototype) +Heap::ArrayObject *ExecutionEngine::newArrayObject(Heap::InternalClass *internalClass) { - Scope scope(this); - ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>(internalClass, prototype)); - return object->d(); + return memoryManager->allocObject<ArrayObject>(internalClass); } Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(const QByteArray &array) { - return memoryManager->allocObject<ArrayBuffer>(array); + return memoryManager->allocate<ArrayBuffer>(array); } Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(size_t length) { - return memoryManager->allocObject<ArrayBuffer>(length); + return memoryManager->allocate<ArrayBuffer>(length); } Heap::DateObject *ExecutionEngine::newDateObject(const Value &value) { - return memoryManager->allocObject<DateObject>(value); + return memoryManager->allocate<DateObject>(value); } Heap::DateObject *ExecutionEngine::newDateObject(const QDateTime &dt) { Scope scope(this); - Scoped<DateObject> object(scope, memoryManager->allocObject<DateObject>(dt)); + Scoped<DateObject> object(scope, memoryManager->allocate<DateObject>(dt)); return object->d(); } Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t) { Scope scope(this); - Scoped<DateObject> object(scope, memoryManager->allocObject<DateObject>(t)); + Scoped<DateObject> object(scope, memoryManager->allocate<DateObject>(t)); return object->d(); } @@ -662,12 +768,12 @@ Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int Heap::RegExpObject *ExecutionEngine::newRegExpObject(RegExp *re) { - return memoryManager->allocObject<RegExpObject>(re); + return memoryManager->allocate<RegExpObject>(re); } Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re) { - return memoryManager->allocObject<RegExpObject>(re); + return memoryManager->allocate<RegExpObject>(re); } Heap::Object *ExecutionEngine::newErrorObject(const Value &value) @@ -714,16 +820,31 @@ Heap::Object *ExecutionEngine::newURIErrorObject(const Value &message) Heap::Object *ExecutionEngine::newVariantObject(const QVariant &v) { - return memoryManager->allocObject<VariantObject>(v); + return memoryManager->allocate<VariantObject>(v); } -Heap::Object *ExecutionEngine::newForEachIteratorObject(Object *o) +Heap::Object *ExecutionEngine::newForInIteratorObject(Object *o) { Scope scope(this); - ScopedObject obj(scope, memoryManager->allocObject<ForEachIteratorObject>(o)); + ScopedObject obj(scope, memoryManager->allocate<ForInIteratorObject>(o)); return obj->d(); } +Heap::Object *ExecutionEngine::newMapIteratorObject(Object *o) +{ + return memoryManager->allocate<MapIteratorObject>(o->d(), this); +} + +Heap::Object *ExecutionEngine::newSetIteratorObject(Object *o) +{ + return memoryManager->allocate<SetIteratorObject>(o->d(), this); +} + +Heap::Object *ExecutionEngine::newArrayIteratorObject(Object *o) +{ + return memoryManager->allocate<ArrayIteratorObject>(o->d(), this); +} + Heap::QmlContext *ExecutionEngine::qmlContext() const { if (!currentStackFrame) @@ -891,16 +1012,14 @@ void ExecutionEngine::requireArgumentsAccessors(int n) } ExecutionContext *global = rootContext(); for (int i = oldSize; i < nArgumentsAccessors; ++i) { - argumentsAccessors[i].value = ScopedValue(scope, memoryManager->allocObject<ArgumentsGetterFunction>(global, i)); - argumentsAccessors[i].set = ScopedValue(scope, memoryManager->allocObject<ArgumentsSetterFunction>(global, i)); + argumentsAccessors[i].value = ScopedValue(scope, memoryManager->allocate<ArgumentsGetterFunction>(global, i)); + argumentsAccessors[i].set = ScopedValue(scope, memoryManager->allocate<ArgumentsSetterFunction>(global, i)); } } } void ExecutionEngine::markObjects(MarkStack *markStack) { - identifierTable->mark(markStack); - for (int i = 0; i < nArgumentsAccessors; ++i) { const Property &pd = argumentsAccessors[i]; if (Heap::FunctionObject *getter = pd.getter()) @@ -909,9 +1028,13 @@ void ExecutionEngine::markObjects(MarkStack *markStack) setter->mark(markStack); } - classPool->markObjects(markStack); + for (int i = 0; i < NClasses; ++i) + if (classes[i]) + classes[i]->mark(markStack); markStack->drain(); + identifierTable->markObjects(markStack); + for (auto compilationUnit: compilationUnits) { compilationUnit->markObjects(markStack); markStack->drain(); @@ -1053,12 +1176,7 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError() error.setColumn(frame.column); } QV4::Scoped<QV4::ErrorObject> errorObj(scope, exception); - if (!!errorObj && errorObj->asSyntaxError()) { - QV4::ScopedString m(scope, newString(QStringLiteral("message"))); - QV4::ScopedValue v(scope, errorObj->get(m)); - error.setDescription(v->toQStringNoThrow()); - } else - error.setDescription(exception->toQStringNoThrow()); + error.setDescription(exception->toQStringNoThrow()); return error; } @@ -1117,8 +1235,11 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return v->toVariant(); } else if (QV4::QmlListWrapper *l = object->as<QV4::QmlListWrapper>()) { return l->toVariant(); - } else if (object->isListType()) +#if QT_CONFIG(qml_sequence_object) + } else if (object->isListType()) { return QV4::SequencePrototype::toVariant(object); +#endif + } } if (value.as<ArrayObject>()) { @@ -1141,10 +1262,12 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return QVariant::fromValue(QV4::JsonObject::toJsonArray(a)); } +#if QT_CONFIG(qml_sequence_object) bool succeeded = false; QVariant retn = QV4::SequencePrototype::toVariant(value, typeHint, &succeeded); if (succeeded) return retn; +#endif } if (value.isUndefined()) @@ -1164,8 +1287,10 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return str.at(0); return str; } +#if QT_CONFIG(qml_locale) if (const QV4::QQmlLocaleData *ld = value.as<QV4::QQmlLocaleData>()) return *ld->d()->locale; +#endif if (const QV4::DateObject *d = value.as<DateObject>()) return d->toQDateTime(); if (const ArrayBuffer *d = value.as<ArrayBuffer>()) @@ -1321,6 +1446,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(ptr))); case QMetaType::QObjectStar: return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr)); +#if QT_CONFIG(qml_sequence_object) case QMetaType::QStringList: { bool succeeded = false; @@ -1330,6 +1456,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return retn->asReturnedValue(); return QV4::Encode(newArrayObject(*reinterpret_cast<const QStringList *>(ptr))); } +#endif case QMetaType::QVariantList: return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr)); case QMetaType::QVariantMap: @@ -1340,8 +1467,10 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return QV4::JsonObject::fromJsonObject(this, *reinterpret_cast<const QJsonObject *>(ptr)); case QMetaType::QJsonArray: return QV4::JsonObject::fromJsonArray(this, *reinterpret_cast<const QJsonArray *>(ptr)); +#if QT_CONFIG(qml_locale) case QMetaType::QLocale: return QQmlLocale::wrap(this, *reinterpret_cast<const QLocale*>(ptr)); +#endif default: break; } @@ -1381,10 +1510,12 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) if (objOk) return QV4::QObjectWrapper::wrap(this, obj); +#if QT_CONFIG(qml_sequence_object) bool succeeded = false; QV4::ScopedValue retn(scope, QV4::SequencePrototype::fromVariant(this, variant, &succeeded)); if (succeeded) return retn->asReturnedValue(); +#endif if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(type)) return QV4::QQmlValueTypeWrapper::create(this, variant, vtmo, type); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index c7fb743088..b007e65c4b 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -88,8 +88,6 @@ struct CompilationUnit; } struct Function; -struct InternalClass; -struct InternalClassPool; struct Q_QML_EXPORT CppStackFrame { CppStackFrame *parent; @@ -98,6 +96,10 @@ struct Q_QML_EXPORT CppStackFrame { const Value *originalArguments; int originalArgumentsCount; int instructionPointer; + const char *yield; + const char *unwindHandler; + const char *unwindLabel; + int unwindLevel; QString source() const; QString function() const; @@ -142,8 +144,6 @@ public: QML_NEARLY_ALWAYS_INLINE Value *jsAlloca(int nValues) { Value *ptr = jsStackTop; jsStackTop = ptr + nValues; - for (int i = 0; i < nValues; ++i) - ptr[i] = Primitive::undefinedValue(); return ptr; } @@ -153,22 +153,27 @@ public: QJSEngine *jsEngine() const; QQmlEngine *qmlEngine() const; #else // !V4_BOOTSTRAP - QJSEngine *jsEngine() const { return v8Engine->publicEngine(); } + QJSEngine *jsEngine() const { return publicEngine; } QQmlEngine *qmlEngine() const { return v8Engine ? v8Engine->engine() : nullptr; } #endif // V4_BOOTSTRAP QV8Engine *v8Engine; + QJSEngine *publicEngine; enum JSObjects { RootContext, + ScriptContext, IntegerNull, // Has to come after the RootContext to make the context stack safe ObjectProto, + SymbolProto, ArrayProto, + ArrayProtoValues, PropertyListProto, StringProto, NumberProto, BooleanProto, DateProto, FunctionProto, + GeneratorProto, RegExpProto, ErrorProto, EvalErrorProto, @@ -178,18 +183,31 @@ public: TypeErrorProto, URIErrorProto, VariantProto, +#if QT_CONFIG(qml_sequence_object) SequenceProto, +#endif ArrayBufferProto, DataViewProto, + SetProto, + MapProto, + IntrinsicTypedArrayProto, ValueTypeProto, SignalHandlerProto, + IteratorProto, + ForInIteratorProto, + SetIteratorProto, + MapIteratorProto, + ArrayIteratorProto, + StringIteratorProto, Object_Ctor, String_Ctor, + Symbol_Ctor, Number_Ctor, Boolean_Ctor, Array_Ctor, Function_Ctor, + GeneratorFunction_Ctor, Date_Ctor, RegExp_Ctor, Error_Ctor, @@ -201,6 +219,11 @@ public: URIError_Ctor, ArrayBuffer_Ctor, DataView_Ctor, + Set_Ctor, + Map_Ctor, + IntrinsicTypedArray_Ctor, + + GetSymbolSpecies, Eval_Function, GetStack_Function, @@ -211,12 +234,16 @@ public: enum { NTypedArrayTypes = 9 }; // == TypedArray::NValues, avoid header dependency ExecutionContext *rootContext() const { return reinterpret_cast<ExecutionContext *>(jsObjects + RootContext); } + ExecutionContext *scriptContext() const { return reinterpret_cast<ExecutionContext *>(jsObjects + ScriptContext); } + void setScriptContext(ReturnedValue c) { jsObjects[ScriptContext] = c; } FunctionObject *objectCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Object_Ctor); } FunctionObject *stringCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + String_Ctor); } + FunctionObject *symbolCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Symbol_Ctor); } FunctionObject *numberCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Number_Ctor); } FunctionObject *booleanCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Boolean_Ctor); } FunctionObject *arrayCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Array_Ctor); } FunctionObject *functionCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Function_Ctor); } + FunctionObject *generatorFunctionCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + GeneratorFunction_Ctor); } FunctionObject *dateCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Date_Ctor); } FunctionObject *regExpCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + RegExp_Ctor); } FunctionObject *errorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Error_Ctor); } @@ -228,16 +255,24 @@ public: FunctionObject *uRIErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + URIError_Ctor); } FunctionObject *arrayBufferCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + ArrayBuffer_Ctor); } FunctionObject *dataViewCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + DataView_Ctor); } + FunctionObject *setCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Set_Ctor); } + FunctionObject *mapCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Map_Ctor); } + FunctionObject *intrinsicTypedArrayCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + IntrinsicTypedArray_Ctor); } FunctionObject *typedArrayCtors; + FunctionObject *getSymbolSpecies() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetSymbolSpecies); } + Object *objectPrototype() const { return reinterpret_cast<Object *>(jsObjects + ObjectProto); } + Object *symbolPrototype() const { return reinterpret_cast<Object *>(jsObjects + SymbolProto); } Object *arrayPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayProto); } + Object *arrayProtoValues() const { return reinterpret_cast<Object *>(jsObjects + ArrayProtoValues); } Object *propertyListPrototype() const { return reinterpret_cast<Object *>(jsObjects + PropertyListProto); } Object *stringPrototype() const { return reinterpret_cast<Object *>(jsObjects + StringProto); } Object *numberPrototype() const { return reinterpret_cast<Object *>(jsObjects + NumberProto); } Object *booleanPrototype() const { return reinterpret_cast<Object *>(jsObjects + BooleanProto); } Object *datePrototype() const { return reinterpret_cast<Object *>(jsObjects + DateProto); } Object *functionPrototype() const { return reinterpret_cast<Object *>(jsObjects + FunctionProto); } + Object *generatorPrototype() const { return reinterpret_cast<Object *>(jsObjects + GeneratorProto); } Object *regExpPrototype() const { return reinterpret_cast<Object *>(jsObjects + RegExpProto); } Object *errorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ErrorProto); } Object *evalErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + EvalErrorProto); } @@ -247,16 +282,26 @@ public: Object *typeErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + TypeErrorProto); } Object *uRIErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + URIErrorProto); } Object *variantPrototype() const { return reinterpret_cast<Object *>(jsObjects + VariantProto); } +#if QT_CONFIG(qml_sequence_object) Object *sequencePrototype() const { return reinterpret_cast<Object *>(jsObjects + SequenceProto); } +#endif Object *arrayBufferPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayBufferProto); } Object *dataViewPrototype() const { return reinterpret_cast<Object *>(jsObjects + DataViewProto); } + Object *setPrototype() const { return reinterpret_cast<Object *>(jsObjects + SetProto); } + Object *mapPrototype() const { return reinterpret_cast<Object *>(jsObjects + MapProto); } + Object *intrinsicTypedArrayPrototype() const { return reinterpret_cast<Object *>(jsObjects + IntrinsicTypedArrayProto); } Object *typedArrayPrototype; Object *valueTypeWrapperPrototype() const { return reinterpret_cast<Object *>(jsObjects + ValueTypeProto); } Object *signalHandlerPrototype() const { return reinterpret_cast<Object *>(jsObjects + SignalHandlerProto); } + Object *iteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + IteratorProto); } + Object *forInIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ForInIteratorProto); } + Object *setIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + SetIteratorProto); } + Object *mapIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + MapIteratorProto); } + Object *arrayIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayIteratorProto); } + Object *stringIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + StringIteratorProto); } - InternalClassPool *classPool; EvalFunction *evalFunction() const { return reinterpret_cast<EvalFunction *>(jsObjects + Eval_Function); } FunctionObject *getStackFunction() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetStack_Function); } FunctionObject *thrower() const { return reinterpret_cast<FunctionObject *>(jsObjects + ThrowerObject); } @@ -273,6 +318,8 @@ public: String_boolean, String_number, String_string, + String_default, + String_symbol, String_object, String_function, String_length, @@ -301,10 +348,30 @@ public: String_byteOffset, String_buffer, String_lastIndex, + String_next, + String_done, + String_return, + NJSStrings }; Value *jsStrings; + enum JSSymbols { + Symbol_hasInstance, + Symbol_isConcatSpreadable, + Symbol_iterator, + Symbol_match, + Symbol_replace, + Symbol_search, + Symbol_species, + Symbol_split, + Symbol_toPrimitive, + Symbol_toStringTag, + Symbol_unscopables, + NJSSymbols + }; + Value *jsSymbols; + String *id_empty() const { return reinterpret_cast<String *>(jsStrings + String_Empty); } String *id_undefined() const { return reinterpret_cast<String *>(jsStrings + String_undefined); } String *id_null() const { return reinterpret_cast<String *>(jsStrings + String_null); } @@ -313,6 +380,8 @@ public: String *id_boolean() const { return reinterpret_cast<String *>(jsStrings + String_boolean); } String *id_number() const { return reinterpret_cast<String *>(jsStrings + String_number); } String *id_string() const { return reinterpret_cast<String *>(jsStrings + String_string); } + String *id_default() const { return reinterpret_cast<String *>(jsStrings + String_default); } + String *id_symbol() const { return reinterpret_cast<String *>(jsStrings + String_symbol); } String *id_object() const { return reinterpret_cast<String *>(jsStrings + String_object); } String *id_function() const { return reinterpret_cast<String *>(jsStrings + String_function); } String *id_length() const { return reinterpret_cast<String *>(jsStrings + String_length); } @@ -341,6 +410,21 @@ public: String *id_byteOffset() const { return reinterpret_cast<String *>(jsStrings + String_byteOffset); } String *id_buffer() const { return reinterpret_cast<String *>(jsStrings + String_buffer); } String *id_lastIndex() const { return reinterpret_cast<String *>(jsStrings + String_lastIndex); } + String *id_next() const { return reinterpret_cast<String *>(jsStrings + String_next); } + String *id_done() const { return reinterpret_cast<String *>(jsStrings + String_done); } + String *id_return() const { return reinterpret_cast<String *>(jsStrings + String_return); } + + Symbol *symbol_hasInstance() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_hasInstance); } + Symbol *symbol_isConcatSpreadable() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_isConcatSpreadable); } + Symbol *symbol_iterator() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_iterator); } + Symbol *symbol_match() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_match); } + Symbol *symbol_replace() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_replace); } + Symbol *symbol_search() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_search); } + Symbol *symbol_species() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_species); } + Symbol *symbol_split() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_split); } + Symbol *symbol_toPrimitive() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_toPrimitive); } + Symbol *symbol_toStringTag() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_toStringTag); } + Symbol *symbol_unscopables() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_unscopables); } #ifndef V4_BOOTSTRAP QIntrusiveList<CompiledData::CompilationUnit, &CompiledData::CompilationUnit::nextCompilationUnit> compilationUnits; @@ -372,9 +456,9 @@ public: const bool m_canAllocateExecutableMemory; #endif - int internalClassIdCount = 0; + quintptr protoIdCount = 1; - ExecutionEngine(); + ExecutionEngine(QJSEngine *jsEngine = nullptr); ~ExecutionEngine(); #if !QT_CONFIG(qml_debug) @@ -396,24 +480,26 @@ public: return static_cast<ExecutionContext *>(¤tStackFrame->jsFrame->context); } - int newInternalClassId() { return ++internalClassIdCount; } + // ensure we always get odd prototype IDs. This helps make marking in QV4::Lookup fast + quintptr newProtoId() { return (protoIdCount += 2); } - InternalClass *newInternalClass(const VTable *vtable, Object *prototype); + Heap::InternalClass *newInternalClass(const VTable *vtable, Object *prototype); Heap::Object *newObject(); - Heap::Object *newObject(InternalClass *internalClass, Object *prototype); + Heap::Object *newObject(Heap::InternalClass *internalClass); Heap::String *newString(const QString &s = QString()); Heap::String *newIdentifier(const QString &text); Heap::Object *newStringObject(const String *string); + Heap::Object *newSymbolObject(const Symbol *symbol); Heap::Object *newNumberObject(double value); Heap::Object *newBooleanObject(bool b); Heap::ArrayObject *newArrayObject(int count = 0); Heap::ArrayObject *newArrayObject(const Value *values, int length); Heap::ArrayObject *newArrayObject(const QStringList &list); - Heap::ArrayObject *newArrayObject(InternalClass *ic, Object *prototype); + Heap::ArrayObject *newArrayObject(Heap::InternalClass *ic); Heap::ArrayBuffer *newArrayBuffer(const QByteArray &array); Heap::ArrayBuffer *newArrayBuffer(size_t length); @@ -437,7 +523,10 @@ public: Heap::Object *newVariantObject(const QVariant &v); - Heap::Object *newForEachIteratorObject(Object *o); + Heap::Object *newForInIteratorObject(Object *o); + Heap::Object *newSetIteratorObject(Object *o); + Heap::Object *newMapIteratorObject(Object *o); + Heap::Object *newArrayIteratorObject(Object *o); Heap::QmlContext *qmlContext() const; QObject *qmlScopeObject() const; @@ -453,7 +542,7 @@ public: void initRootContext(); - InternalClass *newClass(const InternalClass &other); + Heap::InternalClass *newClass(Heap::InternalClass *other); StackTrace exceptionStackTrace; @@ -492,7 +581,7 @@ public: if (!m_canAllocateExecutableMemory) return false; if (f) - return f->interpreterCallCount >= jitCallCountThreshold; + return !f->isGenerator() && f->interpreterCallCount >= jitCallCountThreshold; return true; #else Q_UNUSED(f); @@ -502,6 +591,7 @@ public: QV4::ReturnedValue global(); + double localTZA = 0.0; // local timezone, initialized at startup private: #if QT_CONFIG(qml_debug) QScopedPointer<QV4::Debugging::Debugger> m_debugger; diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index 59fb4a564a..03ff25d5b5 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -88,7 +88,7 @@ struct Q_QML_EXPORT EngineBase { // Exception handling Value *exceptionValue = nullptr; - enum { + enum InternalClassType { Class_Empty, Class_String, Class_MemberData, @@ -96,10 +96,14 @@ struct Q_QML_EXPORT EngineBase { Class_SparseArrayData, Class_ExecutionContext, Class_CallContext, + Class_QmlContext, Class_Object, Class_ArrayObject, Class_FunctionObject, + Class_GeneratorFunction, + Class_GeneratorObject, Class_StringObject, + Class_SymbolObject, Class_ScriptFunction, Class_ObjectProto, Class_RegExp, @@ -111,10 +115,11 @@ struct Q_QML_EXPORT EngineBase { Class_ErrorObjectWithMessage, Class_ErrorProto, Class_QmlContextWrapper, - Class_QmlContext, + Class_Symbol, NClasses }; - InternalClass *internalClasses[NClasses]; + Heap::InternalClass *classes[NClasses]; + Heap::InternalClass *internalClasses(InternalClassType icType) { return classes[icType]; } }; #if defined(Q_CC_MSVC) || defined(Q_CC_GNU) #pragma pack(pop) diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index 90e158ba37..6541f00c8a 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -47,10 +47,6 @@ #include "qv4string_p.h" #include <private/qv4mm_p.h> -#include <private/qqmljsengine_p.h> -#include <private/qqmljslexer_p.h> -#include <private/qqmljsparser_p.h> -#include <private/qqmljsast_p.h> #include <qv4codegen_p.h> #ifndef Q_OS_WIN @@ -74,7 +70,7 @@ void Heap::ErrorObject::init() Scope scope(internalClass->engine); Scoped<QV4::ErrorObject> e(scope, this); - if (internalClass == scope.engine->internalClasses[EngineBase::Class_ErrorProto]) + if (internalClass == scope.engine->internalClasses(EngineBase::Class_ErrorProto)) return; setProperty(scope.engine, QV4::ErrorObject::Index_Stack, scope.engine->getStackFunction()->d()); @@ -316,7 +312,7 @@ void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, He ScopedString s(scope); ScopedObject o(scope); ctor->defineReadonlyProperty(engine->id_prototype(), (o = obj)); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); obj->setProperty(Index_Constructor, ctor->d()); obj->setProperty(Index_Message, engine->id_empty()->d()); obj->setProperty(Index_Name, engine->newString(QString::fromLatin1(ErrorObject::className(t)))); diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index 6b578e8c38..44b88f0d31 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -180,7 +180,7 @@ struct ErrorObject: Object { template<> inline const ErrorObject *Value::as() const { - return isManaged() && m()->vtable()->isErrorObject ? reinterpret_cast<const ErrorObject *>(this) : nullptr; + return isManaged() && m()->internalClass->vtable->isErrorObject ? reinterpret_cast<const ErrorObject *>(this) : nullptr; } struct EvalErrorObject: ErrorObject { @@ -328,25 +328,26 @@ inline SyntaxErrorObject *ErrorObject::asSyntaxError() template <typename T> Heap::Object *ErrorObject::create(ExecutionEngine *e, const Value &message) { - InternalClass *ic = e->internalClasses[message.isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage]; - ic = ic->changePrototype(T::defaultPrototype(e)->d()); - return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), message); + EngineBase::InternalClassType klass = message.isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage; + Scope scope(e); + Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(T::defaultPrototype(e)->d())); + return e->memoryManager->allocObject<T>(ic->d(), message); } template <typename T> Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message) { Scope scope(e); ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue()); - InternalClass *ic = e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage]; - ic = ic->changePrototype(T::defaultPrototype(e)->d()); - return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), v); + EngineBase::InternalClassType klass = v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage; + Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(T::defaultPrototype(e)->d())); + return e->memoryManager->allocObject<T>(ic->d(), v); } template <typename T> Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message, const QString &filename, int line, int column) { Scope scope(e); ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue()); - InternalClass *ic = e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage]; - ic = ic->changePrototype(T::defaultPrototype(e)->d()); - return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), v, filename, line, column); + EngineBase::InternalClassType klass = v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage; + Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(T::defaultPrototype(e)->d())); + return e->memoryManager->allocObject<T>(ic->d(), v, filename, line, column); } diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 6fca9ecd45..131d3406d2 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -52,27 +52,26 @@ QT_BEGIN_NAMESPACE using namespace QV4; -Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, Code codePtr) +Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function) : compiledFunction(function) , compilationUnit(unit) - , code(codePtr) , codeData(function->code()) , jittedCode(nullptr) , codeRef(nullptr) , hasQmlDependencies(function->hasQmlDependencies()) { - Q_UNUSED(engine); - - internalClass = engine->internalClasses[EngineBase::Class_CallContext]; + Scope scope(engine); + Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext)); // first locals const quint32_le *localsIndices = compiledFunction->localsTable(); for (quint32 i = 0; i < compiledFunction->nLocals; ++i) - internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); + ic = ic->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); const quint32_le *formalsIndices = compiledFunction->formalsTable(); for (quint32 i = 0; i < compiledFunction->nFormals; ++i) - internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable); + ic = ic->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable); + internalClass = ic->d(); nFormals = compiledFunction->nFormals; } @@ -110,7 +109,7 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr } - internalClass = engine->internalClasses[EngineBase::Class_CallContext]; + internalClass = engine->internalClasses(EngineBase::Class_CallContext); // first locals const quint32_le *localsIndices = compiledFunction->localsTable(); @@ -120,8 +119,8 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr Scope scope(engine); ScopedString arg(scope); for (const QString ¶meterName : parameterNames) { - arg = engine->newString(parameterName); - internalClass = internalClass->addMember(arg, Attr_NotConfigurable); + arg = engine->newIdentifier(parameterName); + internalClass = internalClass->addMember(arg->identifier(), Attr_NotConfigurable); } nFormals = parameters.size(); } diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 59a94e5dde..ff2d86b89f 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -72,21 +72,19 @@ struct Q_QML_EXPORT Function { return Moth::VME::exec(this, thisObject, argv, argc, context); } - typedef ReturnedValue (*Code)(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc); - Code code; - const uchar *codeData; + const char *codeData; typedef ReturnedValue (*JittedCode)(CppStackFrame *, ExecutionEngine *); JittedCode jittedCode; JSC::MacroAssemblerCodeRef *codeRef; // first nArguments names in internalClass are the actual arguments - InternalClass *internalClass; + Heap::InternalClass *internalClass; uint nFormals; int interpreterCallCount = 0; bool hasQmlDependencies; - Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, Code codePtr); + Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function); ~Function(); // used when dynamically assigning signal handlers (QQmlConnection) @@ -98,8 +96,9 @@ struct Q_QML_EXPORT Function { inline QString sourceFile() const { return compilationUnit->fileName(); } inline QUrl finalUrl() const { return compilationUnit->finalUrl(); } - inline bool usesArgumentsObject() const { return compiledFunction->flags & CompiledData::Function::UsesArgumentsObject; } inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; } + inline bool isArrowFunction() const { return compiledFunction->flags & CompiledData::Function::IsArrowFunction; } + inline bool isGenerator() const { return compiledFunction->flags & CompiledData::Function::IsGenerator; } QQmlSourceLocation sourceLocation() const { diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 83608070ec..a9aaa344cb 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -41,6 +41,7 @@ #include "qv4objectproto_p.h" #include "qv4stringobject_p.h" #include "qv4function_p.h" +#include "qv4symbol_p.h" #include <private/qv4mm_p.h> #include "qv4arrayobject_p.h" @@ -121,7 +122,7 @@ void Heap::FunctionObject::init() Object::init(); this->scope.set(internalClass->engine, internalClass->engine->rootContext()->d()); - Q_ASSERT(internalClass && internalClass->find(internalClass->engine->id_prototype()) == Index_Prototype); + Q_ASSERT(internalClass && internalClass->find(internalClass->engine->id_prototype()->identifier()) == Index_Prototype); setProperty(internalClass->engine, Index_Prototype, Primitive::undefinedValue()); } @@ -144,10 +145,10 @@ void FunctionObject::init(String *n, bool createProto) Scope s(internalClass()->engine); ScopedValue protectThis(s, this); - Q_ASSERT(internalClass() && internalClass()->find(s.engine->id_prototype()) == Heap::FunctionObject::Index_Prototype); + Q_ASSERT(internalClass() && internalClass()->find(s.engine->id_prototype()->identifier()) == Heap::FunctionObject::Index_Prototype); if (createProto) { - ScopedObject proto(s, s.engine->newObject(s.engine->internalClasses[EngineBase::Class_ObjectProto], s.engine->objectPrototype())); - Q_ASSERT(s.engine->internalClasses[EngineBase::Class_ObjectProto]->find(s.engine->id_constructor()) == Heap::FunctionObject::Index_ProtoConstructor); + ScopedObject proto(s, s.engine->newObject(s.engine->internalClasses(EngineBase::Class_ObjectProto))); + Q_ASSERT(s.engine->internalClasses(EngineBase::Class_ObjectProto)->find(s.engine->id_constructor()->identifier()) == Heap::FunctionObject::Index_ProtoConstructor); proto->setProperty(Heap::FunctionObject::Index_ProtoConstructor, d()); setProperty(Heap::FunctionObject::Index_Prototype, proto); } else { @@ -175,7 +176,19 @@ ReturnedValue FunctionObject::call(const FunctionObject *, const Value *, const Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function) { - return scope->engine()->memoryManager->allocObject<ScriptFunction>(scope, function); + return scope->engine()->memoryManager->allocate<ScriptFunction>(scope, function); +} + +Heap::FunctionObject *FunctionObject::createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, jsCallFunction code, int argumentCount) +{ + Scope scope(engine); + ScopedString name(scope, nameOrSymbol); + if (!name) + name = engine->newString(QChar::fromLatin1('[') + nameOrSymbol->toQString().midRef(1) + QChar::fromLatin1(']')); + + ScopedFunctionObject function(scope, engine->memoryManager->allocate<FunctionObject>(engine->rootContext(), name, code)); + function->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(argumentCount)); + return function->d(); } bool FunctionObject::isBinding() const @@ -201,10 +214,8 @@ void Heap::FunctionCtor::init(QV4::ExecutionContext *scope) } // 15.3.2 -ReturnedValue FunctionCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +QQmlRefPointer<CompiledData::CompilationUnit> FunctionCtor::parse(ExecutionEngine *engine, const Value *argv, int argc, Type t) { - Scope scope(f->engine()); - QString arguments; QString body; if (argc > 0) { @@ -215,35 +226,51 @@ ReturnedValue FunctionCtor::callAsConstructor(const FunctionObject *f, const Val } body = argv[argc - 1].toQString(); } - if (scope.engine->hasException) - return Encode::undefined(); + if (engine->hasException) + return nullptr; - QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1Char('}'); + QString function = (t == Type_Function ? QLatin1String("function anonymous(") : QLatin1String("function* anonymous(")) + arguments + QLatin1String("\n){") + body + QLatin1String("\n}"); - QQmlJS::Engine ee, *engine = ⅇ - QQmlJS::Lexer lexer(engine); + QQmlJS::Engine ee; + QQmlJS::Lexer lexer(&ee); lexer.setCode(function, 1, false); - QQmlJS::Parser parser(engine); + QQmlJS::Parser parser(&ee); const bool parsed = parser.parseExpression(); - if (!parsed) - return scope.engine->throwSyntaxError(QLatin1String("Parse error")); + if (!parsed) { + engine->throwSyntaxError(QLatin1String("Parse error")); + return nullptr; + } QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(parser.rootNode()); - if (!fe) - return scope.engine->throwSyntaxError(QLatin1String("Parse error")); + if (!fe) { + engine->throwSyntaxError(QLatin1String("Parse error")); + return nullptr; + } - Compiler::Module module(scope.engine->debugger() != nullptr); + Compiler::Module module(engine->debugger() != nullptr); Compiler::JSUnitGenerator jsGenerator(&module); - RuntimeCodegen cg(scope.engine, &jsGenerator, false); + RuntimeCodegen cg(engine, &jsGenerator, false); cg.generateFromFunctionExpression(QString(), function, fe, &module); - QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = cg.generateCompilationUnit(); - Function *vmf = compilationUnit->linkToEngine(scope.engine); + if (engine->hasException) + return nullptr; - ExecutionContext *global = scope.engine->rootContext(); + return cg.generateCompilationUnit(); +} + +ReturnedValue FunctionCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +{ + ExecutionEngine *engine = f->engine(); + + QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Function); + if (engine->hasException) + return Encode::undefined(); + + Function *vmf = compilationUnit->linkToEngine(engine); + ExecutionContext *global = engine->scriptContext(); return Encode(FunctionObject::createScriptFunction(global, vmf)); } @@ -268,13 +295,14 @@ void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor) ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + defineReadonlyConfigurableProperty(engine->id_name(), *engine->id_empty()); defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(engine->id_toString(), method_toString, 0); defineDefaultProperty(QStringLiteral("apply"), method_apply, 2); defineDefaultProperty(QStringLiteral("call"), method_call, 1); defineDefaultProperty(QStringLiteral("bind"), method_bind, 1); - + defineDefaultProperty(engine->symbol_hasInstance(), method_hasInstance, 1, Attr_ReadOnly); } ReturnedValue FunctionPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) @@ -284,7 +312,19 @@ ReturnedValue FunctionPrototype::method_toString(const FunctionObject *b, const if (!fun) return v4->throwTypeError(); - return Encode(v4->newString(QStringLiteral("function() { [code] }"))); + const Scope scope(fun->engine()); + const ScopedString scopedFunctionName(scope, fun->name()); + const QString functionName(scopedFunctionName ? scopedFunctionName->toQString() : QString()); + QString functionAsString = QStringLiteral("function"); + + // If fun->name() is empty, then there is no function name + // to append because the function is anonymous. + if (!functionName.isEmpty()) + functionAsString.append(QLatin1Char(' ') + functionName); + + functionAsString.append(QStringLiteral("() { [code] }")); + + return Encode(v4->newString(functionAsString)); } ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, const Value *thisObject, const Value *argv, int argc) @@ -304,7 +344,7 @@ ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, cons uint len = arr->getLength(); Scope scope(v4); - Value *arguments = v4->jsAlloca(len); + Value *arguments = scope.alloc(len); if (len) { if (ArgumentsObject::isNonStrictArgumentsObject(arr) && !arr->cast<ArgumentsObject>()->fullyCreated()) { QV4::ArgumentsObject *a = arr->cast<ArgumentsObject>(); @@ -383,6 +423,17 @@ ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Valu return bound->asReturnedValue(); } +ReturnedValue FunctionPrototype::method_hasInstance(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + if (!argc) + return false; + const Object *o = thisObject->as<Object>(); + if (!o) + return f->engine()->throwTypeError(); + + return Object::instanceOf(o, argv[0]); +} + DEFINE_OBJECT_VTABLE(ScriptFunction); ReturnedValue ScriptFunction::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc) @@ -391,8 +442,7 @@ ReturnedValue ScriptFunction::callAsConstructor(const FunctionObject *fo, const const ScriptFunction *f = static_cast<const ScriptFunction *>(fo); Scope scope(v4); - InternalClass *ic = f->classForConstructor(); - ScopedValue thisObject(scope, v4->memoryManager->allocObject<Object>(ic)); + ScopedValue thisObject(scope, v4->memoryManager->allocObject<Object>(f->classForConstructor())); ReturnedValue result = Moth::VME::exec(fo, thisObject, argv, argc); @@ -415,38 +465,29 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function setFunction(function); Q_ASSERT(function); - Q_ASSERT(function->code); Scope s(scope); ScopedFunctionObject f(s, this); ScopedString name(s, function->name()); f->init(name, true); - Q_ASSERT(internalClass && internalClass->find(s.engine->id_length()) == Index_Length); - setProperty(s.engine, Index_Length, Primitive::fromInt32(f->formalParameterCount())); - - if (function->isStrict()) { - ScopedProperty pd(s); - pd->value = s.engine->thrower(); - pd->set = s.engine->thrower(); - f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - f->insertMember(s.engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); - } + Q_ASSERT(internalClass && internalClass->find(s.engine->id_length()->identifier()) == Index_Length); + setProperty(s.engine, Index_Length, Primitive::fromInt32(int(function->compiledFunction->length))); } -InternalClass *ScriptFunction::classForConstructor() const +Heap::InternalClass *ScriptFunction::classForConstructor() const { const Object *o = d()->protoProperty(); - InternalClass *ic = d()->cachedClassForConstructor; - if (ic && ic->prototype == o->d()) - return ic; + if (d()->cachedClassForConstructor && d()->cachedClassForConstructor->prototype == o->d()) + return d()->cachedClassForConstructor; - ic = engine()->internalClasses[EngineBase::Class_Object]; + Scope scope(engine()); + Scoped<InternalClass> ic(scope, engine()->internalClasses(EngineBase::Class_Object)); if (o) ic = ic->changePrototype(o->d()); - d()->cachedClassForConstructor = ic; + d()->cachedClassForConstructor.set(scope.engine, ic->d()); - return ic; + return ic->d(); } DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 32e71a175b..e8bd574161 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -64,9 +64,6 @@ namespace QV4 { struct IndexedBuiltinFunction; struct JSCallData; -typedef ReturnedValue (*jsCallFunction)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); -typedef ReturnedValue (*jsConstructFunction)(const FunctionObject *, const Value *argv, int argc); - namespace Heap { @@ -111,14 +108,16 @@ struct IndexedBuiltinFunction : FunctionObject { uint index; }; -struct ScriptFunction : FunctionObject { +#define ScriptFunctionMembers(class, Member) \ + Member(class, Pointer, InternalClass *, cachedClassForConstructor) + +DECLARE_HEAP_OBJECT(ScriptFunction, FunctionObject) { + DECLARE_MARKOBJECTS(ScriptFunction) enum { Index_Name = FunctionObject::Index_Prototype + 1, Index_Length }; void init(QV4::ExecutionContext *scope, Function *function); - - QV4::InternalClass *cachedClassForConstructor; }; #define BoundFunctionMembers(class, Member) \ @@ -166,11 +165,7 @@ struct Q_QML_EXPORT FunctionObject: Object { static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function); - static Heap::FunctionObject *createBuiltinFunction(ExecutionContext *scope, String *name, - ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)) - { - return scope->engine()->memoryManager->allocObject<FunctionObject>(scope, name, code); - } + static Heap::FunctionObject *createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, jsCallFunction code, int argumentCount); bool strictMode() const { return d()->function ? d()->function->isStrict() : false; } bool isBinding() const; @@ -181,7 +176,7 @@ struct Q_QML_EXPORT FunctionObject: Object { template<> inline const FunctionObject *Value::as() const { - return isManaged() && m()->vtable()->isFunctionObject ? reinterpret_cast<const FunctionObject *>(this) : nullptr; + return isManaged() && m()->internalClass->vtable->isFunctionObject ? reinterpret_cast<const FunctionObject *>(this) : nullptr; } @@ -191,6 +186,12 @@ struct FunctionCtor: FunctionObject static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +protected: + enum Type { + Type_Function, + Type_Generator + }; + static QQmlRefPointer<CompiledData::CompilationUnit> parse(ExecutionEngine *engine, const Value *argv, int argc, Type t = Type_Function); }; struct FunctionPrototype: FunctionObject @@ -203,6 +204,7 @@ struct FunctionPrototype: FunctionObject static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_bind(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; struct IndexedBuiltinFunction : FunctionObject @@ -227,7 +229,7 @@ struct ScriptFunction : FunctionObject { static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); - InternalClass *classForConstructor() const; + Heap::InternalClass *classForConstructor() const; }; @@ -236,7 +238,7 @@ struct BoundFunction: FunctionObject { static Heap::BoundFunction *create(ExecutionContext *scope, FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs) { - return scope->engine()->memoryManager->allocObject<BoundFunction>(scope, target, boundThis, boundArgs); + return scope->engine()->memoryManager->allocate<BoundFunction>(scope, target, boundThis, boundArgs); } Heap::FunctionObject *target() const { return d()->target; } diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp new file mode 100644 index 0000000000..a29eef513c --- /dev/null +++ b/src/qml/jsruntime/qv4generatorobject.cpp @@ -0,0 +1,248 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qv4generatorobject_p.h> +#include <qv4symbol_p.h> +#include <qv4iterator_p.h> +#include <qv4jscall_p.h> +#include <qv4vme_moth_p.h> + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(GeneratorFunctionCtor); +DEFINE_OBJECT_VTABLE(GeneratorFunction); +DEFINE_OBJECT_VTABLE(GeneratorObject); + +void Heap::GeneratorFunctionCtor::init(QV4::ExecutionContext *scope) +{ + Heap::FunctionObject::init(scope, QStringLiteral("GeneratorFunction")); +} + +ReturnedValue GeneratorFunctionCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +{ + ExecutionEngine *engine = f->engine(); + + QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Generator); + if (engine->hasException) + return Encode::undefined(); + + Function *vmf = compilationUnit->linkToEngine(engine); + ExecutionContext *global = engine->scriptContext(); + return Encode(GeneratorFunction::create(global, vmf)); +} + +// 15.3.1: This is equivalent to new Function(...) +ReturnedValue GeneratorFunctionCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + return callAsConstructor(f, argv, argc); +} + +Heap::FunctionObject *GeneratorFunction::create(ExecutionContext *context, Function *function) +{ + Scope scope(context); + Scoped<GeneratorFunction> g(scope, context->engine()->memoryManager->allocate<GeneratorFunction>(context, function)); + ScopedObject proto(scope, scope.engine->newObject()); + proto->setPrototype(scope.engine->generatorPrototype()); + g->defineDefaultProperty(scope.engine->id_prototype(), proto, Attr_NotConfigurable|Attr_NotEnumerable); + g->setPrototype(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype()))); + return g->d(); +} + +ReturnedValue GeneratorFunction::callAsConstructor(const FunctionObject *f, const Value *, int) +{ + return f->engine()->throwTypeError(); +} + +ReturnedValue GeneratorFunction::call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + const GeneratorFunction *gf = static_cast<const GeneratorFunction *>(f); + Function *function = gf->function(); + ExecutionEngine *engine = gf->engine(); + + // We need to set up a separate stack for the generator, as it's being re-entered + uint stackSize = argc; // space for the original arguments + int jsStackFrameSize = offsetof(CallData, args)/sizeof(Value) + function->compiledFunction->nRegisters; + stackSize += jsStackFrameSize; + + size_t requiredMemory = sizeof(GeneratorObject::Data) - sizeof(Value) + sizeof(Value) * stackSize; + + Scope scope(gf); + Scoped<GeneratorObject> g(scope, scope.engine->memoryManager->allocManaged<GeneratorObject>(requiredMemory, scope.engine->classes[EngineBase::Class_GeneratorObject])); + g->setPrototype(ScopedObject(scope, gf->get(scope.engine->id_prototype()))); + + Heap::GeneratorObject *gp = g->d(); + gp->stack.size = stackSize; + gp->stack.alloc = stackSize; + + // copy original arguments + memcpy(gp->stack.values, argv, argc*sizeof(Value)); + gp->cppFrame.originalArguments = gp->stack.values; + gp->cppFrame.originalArgumentsCount = argc; + + // setup JS stack frame + CallData *callData = reinterpret_cast<CallData *>(&gp->stack.values[argc]); + callData->function = *gf; + callData->context = gf->scope(); + callData->accumulator = Encode::undefined(); + callData->thisObject = thisObject ? *thisObject : Primitive::undefinedValue(); + if (argc > int(function->nFormals)) + argc = int(function->nFormals); + callData->setArgc(argc); + memcpy(callData->args, argv, argc*sizeof(Value)); + + gp->cppFrame.v4Function = function; + gp->cppFrame.instructionPointer = 0; + gp->cppFrame.jsFrame = callData; + gp->cppFrame.parent = engine->currentStackFrame; + engine->currentStackFrame = &gp->cppFrame; + + Moth::VME::interpret(gp->cppFrame, function->codeData); + gp->state = GeneratorState::SuspendedStart; + + engine->currentStackFrame = gp->cppFrame.parent; + return g->asReturnedValue(); +} + + +void Heap::GeneratorPrototype::init() +{ + Heap::FunctionObject::init(); +} + + +void GeneratorPrototype::init(ExecutionEngine *engine, Object *ctor) +{ + Scope scope(engine); + ScopedValue v(scope); + + ScopedObject ctorProto(scope, engine->newObject(engine->newInternalClass(Object::staticVTable(), engine->functionPrototype()))); + + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyProperty(engine->id_prototype(), ctorProto); + + ctorProto->defineDefaultProperty(QStringLiteral("constructor"), (v = ctor), Attr_ReadOnly_ButConfigurable); + ctorProto->defineDefaultProperty(engine->symbol_toStringTag(), (v = engine->newIdentifier(QStringLiteral("GeneratorFunction"))), Attr_ReadOnly_ButConfigurable); + ctorProto->defineDefaultProperty(engine->id_prototype(), (v = this), Attr_ReadOnly_ButConfigurable); + + setPrototype(engine->iteratorPrototype()); + defineDefaultProperty(QStringLiteral("constructor"), ctorProto, Attr_ReadOnly_ButConfigurable); + defineDefaultProperty(QStringLiteral("next"), method_next, 1); + defineDefaultProperty(QStringLiteral("return"), method_return, 1); + defineDefaultProperty(QStringLiteral("throw"), method_throw, 1); + defineDefaultProperty(engine->symbol_toStringTag(), (v = engine->newString(QStringLiteral("Generator"))), Attr_ReadOnly_ButConfigurable); +} + +ReturnedValue GeneratorPrototype::method_next(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + ExecutionEngine *engine = f->engine(); + const GeneratorObject *g = thisObject->as<GeneratorObject>(); + if (!g || g->d()->state == GeneratorState::Executing) + return engine->throwTypeError(); + Heap::GeneratorObject *gp = g->d(); + + if (gp->state == GeneratorState::Completed) + return IteratorPrototype::createIterResultObject(engine, Primitive::undefinedValue(), true); + + return g->resume(engine, argc ? argv[0] : Primitive::undefinedValue()); +} + +ReturnedValue GeneratorPrototype::method_return(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + ExecutionEngine *engine = f->engine(); + const GeneratorObject *g = thisObject->as<GeneratorObject>(); + if (!g || g->d()->state == GeneratorState::Executing) + return engine->throwTypeError(); + + Heap::GeneratorObject *gp = g->d(); + + if (gp->state == GeneratorState::SuspendedStart) + gp->state = GeneratorState::Completed; + + if (gp->state == GeneratorState::Completed) + return IteratorPrototype::createIterResultObject(engine, argc ? argv[0] : Primitive::undefinedValue(), true); + + // the bytecode interpreter interprets an exception with empty value as + // a yield called with return() + engine->throwError(Primitive::emptyValue()); + + return g->resume(engine, argc ? argv[0]: Primitive::undefinedValue()); +} + +ReturnedValue GeneratorPrototype::method_throw(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + ExecutionEngine *engine = f->engine(); + const GeneratorObject *g = thisObject->as<GeneratorObject>(); + if (!g || g->d()->state == GeneratorState::Executing) + return engine->throwTypeError(); + + Heap::GeneratorObject *gp = g->d(); + + engine->throwError(argc ? argv[0]: Primitive::undefinedValue()); + + if (gp->state == GeneratorState::SuspendedStart || gp->state == GeneratorState::Completed) { + gp->state = GeneratorState::Completed; + return Encode::undefined(); + } + + return g->resume(engine, Primitive::undefinedValue()); +} + +ReturnedValue GeneratorObject::resume(ExecutionEngine *engine, const Value &arg) const +{ + Heap::GeneratorObject *gp = d(); + gp->state = GeneratorState::Executing; + gp->cppFrame.parent = engine->currentStackFrame; + engine->currentStackFrame = &gp->cppFrame; + + Q_ASSERT(gp->cppFrame.yield != nullptr); + const char *code = gp->cppFrame.yield; + gp->cppFrame.yield = nullptr; + gp->cppFrame.jsFrame->accumulator = arg; + + Scope scope(engine); + ScopedValue result(scope, Moth::VME::interpret(gp->cppFrame, code)); + + engine->currentStackFrame = gp->cppFrame.parent; + + bool done = (gp->cppFrame.yield == nullptr); + gp->state = done ? GeneratorState::Completed : GeneratorState::SuspendedYield; + if (engine->hasException) + return Encode::undefined(); + return IteratorPrototype::createIterResultObject(engine, result, done); +} diff --git a/src/qml/jsruntime/qv4generatorobject_p.h b/src/qml/jsruntime/qv4generatorobject_p.h new file mode 100644 index 0000000000..62ffcbbad1 --- /dev/null +++ b/src/qml/jsruntime/qv4generatorobject_p.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4GENERATOROBJECT_P_H +#define QV4GENERATOROBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4functionobject_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +enum class GeneratorState { + Undefined, + SuspendedStart, + SuspendedYield, + Executing, + Completed +}; + +namespace Heap { + +struct GeneratorFunctionCtor : FunctionObject { + void init(QV4::ExecutionContext *scope); +}; + +struct GeneratorFunction : ScriptFunction { +}; + +struct GeneratorPrototype : FunctionObject { + void init(); +}; + +#define GeneratorObjectMembers(class, Member) \ + Member(class, Pointer, ExecutionContext *, context) \ + Member(class, Pointer, GeneratorFunction *, function) \ + Member(class, NoMark, GeneratorState, state) \ + Member(class, NoMark, CppStackFrame, cppFrame) \ + Member(class, ValueArray, ValueArray, stack) + +DECLARE_HEAP_OBJECT(GeneratorObject, Object) { + DECLARE_MARKOBJECTS(GeneratorObject); +}; + +} + +struct GeneratorFunctionCtor : FunctionCtor +{ + V4_OBJECT2(GeneratorFunctionCtor, FunctionCtor) + + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct GeneratorFunction : ScriptFunction +{ + V4_OBJECT2(GeneratorFunction, ScriptFunction) + V4_INTERNALCLASS(GeneratorFunction) + + static Heap::FunctionObject *create(ExecutionContext *scope, Function *function); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct GeneratorPrototype : Object +{ + void init(ExecutionEngine *engine, Object *ctor); + + static ReturnedValue method_next(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_return(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_throw(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +}; + + +struct GeneratorObject : Object { + V4_OBJECT2(GeneratorObject, Object) + Q_MANAGED_TYPE(GeneratorObject) + V4_INTERNALCLASS(GeneratorObject) + V4_PROTOTYPE(generatorPrototype) + + ReturnedValue resume(ExecutionEngine *engine, const Value &arg) const; +}; + +} + +QT_END_NAMESPACE + +#endif // QV4GENERATORFUNCTION_P_H + diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 1fa4bae049..607f8b4d28 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -153,6 +153,11 @@ namespace Compiler { struct Module; struct Context; struct JSUnitGenerator; + class Codegen; +} + +namespace Moth { + class BytecodeGenerator; } namespace Heap { @@ -160,13 +165,17 @@ namespace Heap { struct MemberData; struct ArrayData; + struct StringOrSymbol; struct String; + struct Symbol; struct Object; struct ObjectPrototype; struct ExecutionContext; struct CallContext; + struct QmlContext; struct ScriptFunction; + struct InternalClass; struct BooleanObject; struct NumberObject; @@ -188,14 +197,18 @@ namespace Heap { template <typename T, size_t> struct Pointer; } +struct CppStackFrame; class MemoryManager; class ExecutableAllocator; +struct StringOrSymbol; struct String; +struct Symbol; struct Object; struct ObjectPrototype; struct ObjectIterator; struct ExecutionContext; struct CallContext; +struct QmlContext; struct ScriptFunction; struct InternalClass; struct Property; @@ -236,6 +249,7 @@ struct Scope; struct ScopedValue; template<typename T> struct Scoped; typedef Scoped<String> ScopedString; +typedef Scoped<StringOrSymbol> ScopedStringOrSymbol; typedef Scoped<Object> ScopedObject; typedef Scoped<ArrayObject> ScopedArrayObject; typedef Scoped<FunctionObject> ScopedFunctionObject; @@ -244,6 +258,7 @@ typedef Scoped<ExecutionContext> ScopedContext; struct PersistentValueStorage; class PersistentValue; class WeakValue; +struct MarkStack; struct IdentifierTable; class RegExpCache; @@ -357,6 +372,12 @@ struct Q_QML_EXPORT StackFrame { }; typedef QVector<StackFrame> StackTrace; +enum class ObjectLiteralArgument { + Value, + Getter, + Setter +}; + } Q_DECLARE_TYPEINFO(QV4::PropertyAttributes, Q_PRIMITIVE_TYPE); diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index f419ab53fe..47a6734eda 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -351,7 +351,7 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc, if (!directCall) { // the context for eval should be the global scope - ctx = v4->rootContext(); + ctx = v4->scriptContext(); } String *scode = argv[0].stringValue(); @@ -361,7 +361,7 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc, const QString code = scode->toQString(); bool inheritContext = !isStrict; - Script script(ctx, QV4::Compiler::EvalCode, code, QStringLiteral("eval code")); + Script script(ctx, QV4::Compiler::ContextType::Eval, code, QStringLiteral("eval code")); script.strictMode = (directCall && isStrict); script.inheritContext = inheritContext; script.parse(); diff --git a/src/qml/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp index c122bcb51a..4bfae14aec 100644 --- a/src/qml/jsruntime/qv4identifier.cpp +++ b/src/qml/jsruntime/qv4identifier.cpp @@ -38,11 +38,22 @@ ****************************************************************************/ #include "qv4identifier_p.h" #include "qv4identifiertable_p.h" +#include "qv4string_p.h" QT_BEGIN_NAMESPACE namespace QV4 { +QString Identifier::toQString() const +{ + if (isArrayIndex()) + return QString::number(asArrayIndex()); + Heap::Base *b = asHeapObject(); + Q_ASSERT(b->internalClass->vtable->isStringOrSymbol); + Heap::StringOrSymbol *s = static_cast<Heap::StringOrSymbol *>(b); + return s->toQString(); +} + static const uchar prime_deltas[] = { 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 @@ -54,14 +65,16 @@ static inline int primeForNumBits(int numBits) } -IdentifierHashData::IdentifierHashData(int numBits) +IdentifierHashData::IdentifierHashData(IdentifierTable *table, int numBits) : size(0) , numBits(numBits) + , identifierTable(table) { refCount.store(1); alloc = primeForNumBits(numBits); entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); memset(entries, 0, alloc*sizeof(IdentifierHashEntry)); + identifierTable->addIdentifierHash(this); } IdentifierHashData::IdentifierHashData(IdentifierHashData *other) @@ -73,12 +86,18 @@ IdentifierHashData::IdentifierHashData(IdentifierHashData *other) alloc = other->alloc; entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); memcpy(entries, other->entries, alloc*sizeof(IdentifierHashEntry)); + identifierTable->addIdentifierHash(this); +} + +IdentifierHashData::~IdentifierHashData() { + free(entries); + if (identifierTable) + identifierTable->removeIdentifierHash(this); } IdentifierHash::IdentifierHash(ExecutionEngine *engine) { - d = new IdentifierHashData(3); - d->identifierTable = engine->identifierTable; + d = new IdentifierHashData(engine->identifierTable, 3); } void IdentifierHash::detach() @@ -92,8 +111,10 @@ void IdentifierHash::detach() } -IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier) +IdentifierHashEntry *IdentifierHash::addEntry(Identifier identifier) { + Q_ASSERT(identifier.isValid()); + // fill up to max 50% bool grow = (d->alloc <= d->size*2); @@ -104,10 +125,10 @@ IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier) memset(newEntries, 0, newAlloc*sizeof(IdentifierHashEntry)); for (int i = 0; i < d->alloc; ++i) { const IdentifierHashEntry &e = d->entries[i]; - if (!e.identifier) + if (!e.identifier.isValid()) continue; - uint idx = e.identifier->hashValue % newAlloc; - while (newEntries[idx].identifier) { + uint idx = e.identifier.id % newAlloc; + while (newEntries[idx].identifier.isValid()) { ++idx; idx %= newAlloc; } @@ -118,8 +139,8 @@ IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier) d->alloc = newAlloc; } - uint idx = identifier->hashValue % d->alloc; - while (d->entries[idx].identifier) { + uint idx = identifier.id % d->alloc; + while (d->entries[idx].identifier.isValid()) { Q_ASSERT(d->entries[idx].identifier != identifier); ++idx; idx %= d->alloc; @@ -129,15 +150,15 @@ IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier) return d->entries + idx; } -const IdentifierHashEntry *IdentifierHash::lookup(const Identifier *identifier) const +const IdentifierHashEntry *IdentifierHash::lookup(Identifier identifier) const { - if (!d) + if (!d || !identifier.isValid()) return nullptr; Q_ASSERT(d->entries); - uint idx = identifier->hashValue % d->alloc; + uint idx = identifier.id % d->alloc; while (1) { - if (!d->entries[idx].identifier) + if (!d->entries[idx].identifier.isValid()) return nullptr; if (d->entries[idx].identifier == identifier) return d->entries + idx; @@ -150,41 +171,56 @@ const IdentifierHashEntry *IdentifierHash::lookup(const QString &str) const { if (!d) return nullptr; - Q_ASSERT(d->entries); - uint hash = String::createHashValue(str.constData(), str.length(), nullptr); - uint idx = hash % d->alloc; - while (1) { - if (!d->entries[idx].identifier) - return nullptr; - if (d->entries[idx].identifier->string == str) - return d->entries + idx; - ++idx; - idx %= d->alloc; - } + Identifier id = d->identifierTable->identifier(str); + return lookup(id); } const IdentifierHashEntry *IdentifierHash::lookup(String *str) const { if (!d) return nullptr; - if (str->d()->identifier) - return lookup(str->d()->identifier); + Identifier id = d->identifierTable->identifier(str); + if (id.isValid()) + return lookup(id); return lookup(str->toQString()); } -const Identifier *IdentifierHash::toIdentifier(const QString &str) const +const Identifier IdentifierHash::toIdentifier(const QString &str) const { Q_ASSERT(d); return d->identifierTable->identifier(str); } -const Identifier *IdentifierHash::toIdentifier(Heap::String *str) const +const Identifier IdentifierHash::toIdentifier(Heap::String *str) const { Q_ASSERT(d); return d->identifierTable->identifier(str); } +QString QV4::IdentifierHash::findId(int value) const +{ + IdentifierHashEntry *e = d->entries; + IdentifierHashEntry *end = e + d->alloc; + while (e < end) { + if (e->identifier.isValid() && e->value == value) + return e->identifier.toQString(); + ++e; + } + return QString(); +} + +void IdentifierHashData::markObjects(MarkStack *markStack) const +{ + IdentifierHashEntry *e = entries; + IdentifierHashEntry *end = e + alloc; + while (e < end) { + if (Heap::Base *o = e->identifier.asHeapObject()) + o->mark(markStack); + ++e; + } +} + } diff --git a/src/qml/jsruntime/qv4identifier_p.h b/src/qml/jsruntime/qv4identifier_p.h index 82346d5f68..b167a149a2 100644 --- a/src/qml/jsruntime/qv4identifier_p.h +++ b/src/qml/jsruntime/qv4identifier_p.h @@ -51,38 +51,47 @@ // #include <qstring.h> +#include <private/qv4global_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -namespace Heap { - struct String; -} - -struct String; -struct IdentifierTable; -struct ExecutionEngine; - struct Identifier { - QString string; - uint hashValue; + // id's are either pointers to Heap::String or Heap::Symbol from the Identifier table. + // For Symbol is can simply point to itself. + // This gives us automative GC'ing of identifiers + // In addition, an identifier can have the lowest bit set, and then indicates an array index + quint64 id; + + static Identifier invalid() { return Identifier{ 0 }; } + static Identifier fromArrayIndex(uint idx) { return Identifier{ (quint64(idx) << 1) | 1 }; } + bool isValid() const { return id && !(id & 1); } + uint asArrayIndex() const { return (id & 1) ? (id >> 1) : std::numeric_limits<uint>::max(); } + uint isArrayIndex() const { return (id & 1); } + static Identifier fromHeapObject(Heap::Base *b) { return Identifier{ reinterpret_cast<quintptr>(b) }; } + Heap::Base *asHeapObject() const { return (id & 1) ? nullptr : reinterpret_cast<Heap::Base *>(id); } + + Q_QML_EXPORT QString toQString() const; + + bool operator ==(const Identifier &other) const { return id == other.id; } + bool operator !=(const Identifier &other) const { return id != other.id; } + bool operator <(const Identifier &other) const { return id < other.id; } }; struct IdentifierHashEntry { - const Identifier *identifier; + Identifier identifier; int value; }; struct IdentifierHashData { - IdentifierHashData(int numBits); + IdentifierHashData(IdentifierTable *table, int numBits); explicit IdentifierHashData(IdentifierHashData *other); - ~IdentifierHashData() { - free(entries); - } + ~IdentifierHashData(); + void markObjects(MarkStack *markStack) const; QBasicAtomicInt refCount; int alloc; @@ -117,12 +126,12 @@ struct IdentifierHash QString findId(int value) const; protected: - IdentifierHashEntry *addEntry(const Identifier *i); - const IdentifierHashEntry *lookup(const Identifier *identifier) const; + IdentifierHashEntry *addEntry(Identifier i); + const IdentifierHashEntry *lookup(Identifier identifier) const; const IdentifierHashEntry *lookup(const QString &str) const; const IdentifierHashEntry *lookup(String *str) const; - const Identifier *toIdentifier(const QString &str) const; - const Identifier *toIdentifier(Heap::String *str) const; + const Identifier toIdentifier(const QString &str) const; + const Identifier toIdentifier(Heap::String *str) const; }; @@ -180,20 +189,6 @@ inline int IdentifierHash::value(String *str) const return e ? e->value : -1; } - -inline -QString IdentifierHash::findId(int value) const -{ - IdentifierHashEntry *e = d->entries; - IdentifierHashEntry *end = e + d->alloc; - while (e < end) { - if (e->identifier && e->value == value) - return e->identifier->string; - ++e; - } - return QString(); -} - } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp index b77f9478d3..c045ac3008 100644 --- a/src/qml/jsruntime/qv4identifiertable.cpp +++ b/src/qml/jsruntime/qv4identifiertable.cpp @@ -37,6 +37,7 @@ ** ****************************************************************************/ #include "qv4identifiertable_p.h" +#include "qv4symbol_p.h" QT_BEGIN_NAMESPACE @@ -59,38 +60,38 @@ IdentifierTable::IdentifierTable(ExecutionEngine *engine) , numBits(8) { alloc = primeForNumBits(numBits); - entries = (Heap::String **)malloc(alloc*sizeof(Heap::String *)); - memset(entries, 0, alloc*sizeof(Heap::String *)); + entriesByHash = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *)); + entriesById = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *)); + memset(entriesByHash, 0, alloc*sizeof(Heap::String *)); + memset(entriesById, 0, alloc*sizeof(Heap::String *)); } IdentifierTable::~IdentifierTable() { - for (int i = 0; i < alloc; ++i) - if (entries[i]) - delete entries[i]->identifier; - free(entries); + free(entriesByHash); + free(entriesById); + for (auto &h : idHashes) + h->identifierTable = nullptr; } -void IdentifierTable::addEntry(Heap::String *str) +void IdentifierTable::addEntry(Heap::StringOrSymbol *str) { uint hash = str->hashValue(); if (str->subtype == Heap::String::StringType_ArrayIndex) return; - str->identifier = new Identifier; - str->identifier->string = str->toQString(); - str->identifier->hashValue = hash; + str->identifier = Identifier::fromHeapObject(str); bool grow = (alloc <= size*2); if (grow) { ++numBits; int newAlloc = primeForNumBits(numBits); - Heap::String **newEntries = (Heap::String **)malloc(newAlloc*sizeof(Heap::String *)); - memset(newEntries, 0, newAlloc*sizeof(Heap::String *)); + Heap::StringOrSymbol **newEntries = (Heap::StringOrSymbol **)malloc(newAlloc*sizeof(Heap::String *)); + memset(newEntries, 0, newAlloc*sizeof(Heap::StringOrSymbol *)); for (int i = 0; i < alloc; ++i) { - Heap::String *e = entries[i]; + Heap::StringOrSymbol *e = entriesByHash[i]; if (!e) continue; uint idx = e->stringHash % newAlloc; @@ -100,17 +101,42 @@ void IdentifierTable::addEntry(Heap::String *str) } newEntries[idx] = e; } - free(entries); - entries = newEntries; + free(entriesByHash); + entriesByHash = newEntries; + + newEntries = (Heap::StringOrSymbol **)malloc(newAlloc*sizeof(Heap::String *)); + memset(newEntries, 0, newAlloc*sizeof(Heap::StringOrSymbol *)); + for (int i = 0; i < alloc; ++i) { + Heap::StringOrSymbol *e = entriesById[i]; + if (!e) + continue; + uint idx = e->identifier.id % newAlloc; + while (newEntries[idx]) { + ++idx; + idx %= newAlloc; + } + newEntries[idx] = e; + } + free(entriesById); + entriesById = newEntries; + alloc = newAlloc; } uint idx = hash % alloc; - while (entries[idx]) { + while (entriesByHash[idx]) { + ++idx; + idx %= alloc; + } + entriesByHash[idx] = str; + + idx = str->identifier.id % alloc; + while (entriesById[idx]) { ++idx; idx %= alloc; } - entries[idx] = str; + entriesById[idx] = str; + ++size; } @@ -121,9 +147,9 @@ Heap::String *IdentifierTable::insertString(const QString &s) uint subtype; uint hash = String::createHashValue(s.constData(), s.length(), &subtype); uint idx = hash % alloc; - while (Heap::String *e = entries[idx]) { + while (Heap::StringOrSymbol *e = entriesByHash[idx]) { if (e->stringHash == hash && e->toQString() == s) - return e; + return static_cast<Heap::String *>(e); ++idx; idx %= alloc; } @@ -135,18 +161,42 @@ Heap::String *IdentifierTable::insertString(const QString &s) return str; } +Heap::Symbol *IdentifierTable::insertSymbol(const QString &s) +{ + Q_ASSERT(s.at(0) == QLatin1Char('@')); + + uint subtype; + uint hash = String::createHashValue(s.constData(), s.length(), &subtype); + uint idx = hash % alloc; + while (Heap::StringOrSymbol *e = entriesByHash[idx]) { + if (e->stringHash == hash && e->toQString() == s) + return static_cast<Heap::Symbol *>(e); + ++idx; + idx %= alloc; + } + + Heap::Symbol *str = Symbol::create(engine, s); + str->stringHash = hash; + str->subtype = subtype; + addEntry(str); + return str; + +} + -Identifier *IdentifierTable::identifierImpl(const Heap::String *str) +Identifier IdentifierTable::identifierImpl(const Heap::String *str) { - if (str->identifier) + if (str->identifier.isValid()) return str->identifier; uint hash = str->hashValue(); - if (str->subtype == Heap::String::StringType_ArrayIndex) - return nullptr; + if (str->subtype == Heap::String::StringType_ArrayIndex) { + str->identifier = Identifier::fromArrayIndex(hash); + return str->identifier; + } uint idx = hash % alloc; - while (Heap::String *e = entries[idx]) { - if (e->stringHash == hash && e->isEqualTo(str)) { + while (Heap::StringOrSymbol *e = entriesByHash[idx]) { + if (e->stringHash == hash && e->toQString() == str->toQString()) { str->identifier = e->identifier; return e->identifier; } @@ -158,28 +208,104 @@ Identifier *IdentifierTable::identifierImpl(const Heap::String *str) return str->identifier; } -Heap::String *IdentifierTable::stringFromIdentifier(Identifier *i) +Heap::StringOrSymbol *IdentifierTable::resolveId(Identifier i) const { - if (!i) + uint arrayIdx = i.asArrayIndex(); + if (arrayIdx < UINT_MAX) + return engine->newString(QString::number(arrayIdx)); + if (!i.isValid()) return nullptr; - uint idx = i->hashValue % alloc; + uint idx = i.id % alloc; while (1) { - Heap::String *e = entries[idx]; - Q_ASSERT(e); - if (e->identifier == i) + Heap::StringOrSymbol *e = entriesById[idx]; + if (!e || e->identifier == i) return e; ++idx; idx %= alloc; } } -Identifier *IdentifierTable::identifier(const QString &s) +Heap::String *IdentifierTable::stringForId(Identifier i) const +{ + Heap::StringOrSymbol *s = resolveId(i); + Q_ASSERT(s && s->internalClass->vtable->isString); + return static_cast<Heap::String *>(s); +} + +Heap::Symbol *IdentifierTable::symbolForId(Identifier i) const +{ + Heap::StringOrSymbol *s = resolveId(i); + Q_ASSERT(!s || !s->internalClass->vtable->isString); + return static_cast<Heap::Symbol *>(s); +} + +void IdentifierTable::markObjects(MarkStack *markStack) +{ + for (const auto &h : idHashes) + h->markObjects(markStack); +} + +template <typename Key> +int sweepTable(Heap::StringOrSymbol **table, int alloc, std::function<Key(Heap::StringOrSymbol *)> f) { + int freed = 0; + Key lastKey = 0; + int lastEntry = -1; + int start = 0; + // start at an empty entry so we compress properly + for (; start < alloc; ++start) { + if (!table[start]) + break; + } + + for (int i = 0; i < alloc; ++i) { + int idx = (i + start) % alloc; + Heap::StringOrSymbol *entry = table[idx]; + if (!entry) { + lastEntry = -1; + continue; + } + if (entry->isMarked()) { + if (lastEntry >= 0 && lastKey == f(entry)) { + Q_ASSERT(table[lastEntry] == nullptr); + table[lastEntry] = entry; + table[idx] = nullptr; + lastEntry = (lastEntry + 1) % alloc; + Q_ASSERT(table[lastEntry] == nullptr); + } + continue; + } + if (lastEntry == -1) { + lastEntry = idx; + lastKey = f(entry); + } + table[idx] = nullptr; + ++freed; + } + for (int i = 0; i < alloc; ++i) { + Heap::StringOrSymbol *entry = table[i]; + if (!entry) + continue; + Q_ASSERT(entry->isMarked()); + } + return freed; +} + +void IdentifierTable::sweep() +{ + int f = sweepTable<int>(entriesByHash, alloc, [](Heap::StringOrSymbol *entry) {return entry->hashValue(); }); + int freed = sweepTable<quint64>(entriesById, alloc, [](Heap::StringOrSymbol *entry) {return entry->identifier.id; }); + Q_UNUSED(f); + Q_ASSERT(f == freed); + size -= freed; +} + +Identifier IdentifierTable::identifier(const QString &s) { return insertString(s)->identifier; } -Identifier *IdentifierTable::identifier(const char *s, int len) +Identifier IdentifierTable::identifier(const char *s, int len) { uint subtype; uint hash = String::createHashValue(s, len, &subtype); @@ -188,7 +314,7 @@ Identifier *IdentifierTable::identifier(const char *s, int len) QLatin1String latin(s, len); uint idx = hash % alloc; - while (Heap::String *e = entries[idx]) { + while (Heap::StringOrSymbol *e = entriesByHash[idx]) { if (e->stringHash == hash && e->toQString() == latin) return e->identifier; ++idx; diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h index b0b08f1e54..6e6600d055 100644 --- a/src/qml/jsruntime/qv4identifiertable_p.h +++ b/src/qml/jsruntime/qv4identifiertable_p.h @@ -53,6 +53,7 @@ #include "qv4identifier_p.h" #include "qv4string_p.h" #include "qv4engine_p.h" +#include <qset.h> #include <limits.h> QT_BEGIN_NAMESPACE @@ -66,9 +67,12 @@ struct IdentifierTable int alloc; int size; int numBits; - Heap::String **entries; + Heap::StringOrSymbol **entriesByHash; + Heap::StringOrSymbol **entriesById; - void addEntry(Heap::String *str); + QSet<IdentifierHashData *> idHashes; + + void addEntry(Heap::StringOrSymbol *str); public: @@ -76,32 +80,34 @@ public: ~IdentifierTable(); Heap::String *insertString(const QString &s); + Heap::Symbol *insertSymbol(const QString &s); - Identifier *identifier(const Heap::String *str) { - if (str->identifier) + Identifier identifier(const Heap::String *str) { + if (str->identifier.isValid()) return str->identifier; return identifierImpl(str); } - Identifier *identifier(const QV4::String *str) { + Identifier identifier(const QV4::String *str) { return identifier(str->d()); } - Identifier *identifier(const QString &s); - Identifier *identifier(const char *s, int len); + Identifier identifier(const QString &s); + Identifier identifier(const char *s, int len); + + Identifier identifierImpl(const Heap::String *str); - Identifier *identifierImpl(const Heap::String *str); + Heap::StringOrSymbol *resolveId(Identifier i) const; + Q_QML_PRIVATE_EXPORT Heap::String *stringForId(Identifier i) const; + Q_QML_PRIVATE_EXPORT Heap::Symbol *symbolForId(Identifier i) const; - Heap::String *stringFromIdentifier(Identifier *i); + void markObjects(MarkStack *markStack); + void sweep(); - void mark(MarkStack *markStack) { - for (int i = 0; i < alloc; ++i) { - Heap::String *entry = entries[i]; - if (!entry || entry->isMarked()) - continue; - entry->setMarkBit(); - Q_ASSERT(entry->vtable()->markObjects); - entry->vtable()->markObjects(entry, markStack); - } + void addIdentifierHash(IdentifierHashData *h) { + idHashes.insert(h); + } + void removeIdentifierHash(IdentifierHashData *h) { + idHashes.remove(h); } }; diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 3bfcf358bf..a913d5ca75 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -47,7 +47,7 @@ QT_BEGIN_NAMESPACE -using namespace QV4; +namespace QV4 { static const uchar prime_deltas[] = { 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, @@ -74,27 +74,11 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize) // fill up to max 50% bool grow = (d->alloc <= d->size*2); - if (classSize < d->size || grow) { - PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits); - for (int i = 0; i < d->alloc; ++i) { - const Entry &e = d->entries[i]; - if (!e.identifier || e.index >= static_cast<unsigned>(classSize)) - continue; - uint idx = e.identifier->hashValue % dd->alloc; - while (dd->entries[idx].identifier) { - ++idx; - idx %= dd->alloc; - } - dd->entries[idx] = e; - } - dd->size = classSize; - Q_ASSERT(d->refCount > 1); - --d->refCount; - d = dd; - } + if (classSize < d->size || grow) + detach(grow, classSize); - uint idx = entry.identifier->hashValue % d->alloc; - while (d->entries[idx].identifier) { + uint idx = entry.identifier.id % d->alloc; + while (d->entries[idx].identifier.isValid()) { ++idx; idx %= d->alloc; } @@ -102,38 +86,125 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize) ++d->size; } +int PropertyHash::removeIdentifier(Identifier identifier, int classSize) +{ + int val = -1; + PropertyHashData *dd = new PropertyHashData(d->numBits); + for (int i = 0; i < d->alloc; ++i) { + const Entry &e = d->entries[i]; + if (!e.identifier.isValid() || e.index >= static_cast<unsigned>(classSize)) + continue; + if (e.identifier == identifier) { + val = e.index; + continue; + } + uint idx = e.identifier.id % dd->alloc; + while (dd->entries[idx].identifier.isValid()) { + ++idx; + idx %= dd->alloc; + } + dd->entries[idx] = e; + } + dd->size = classSize; + if (!--d->refCount) + delete d; + d = dd; + + Q_ASSERT(val != -1); + return val; +} + +void PropertyHash::detach(bool grow, int classSize) +{ + if (d->refCount == 1 && !grow) + return; -InternalClass::InternalClass(ExecutionEngine *engine) - : engine(engine) - , vtable(nullptr) - , prototype(nullptr) - , m_sealed(nullptr) - , m_frozen(nullptr) - , size(0) - , extensible(true) + PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits); + for (int i = 0; i < d->alloc; ++i) { + const Entry &e = d->entries[i]; + if (!e.identifier.isValid() || e.index >= static_cast<unsigned>(classSize)) + continue; + uint idx = e.identifier.id % dd->alloc; + while (dd->entries[idx].identifier.isValid()) { + ++idx; + idx %= dd->alloc; + } + dd->entries[idx] = e; + } + dd->size = classSize; + if (!--d->refCount) + delete d; + d = dd; +} + +namespace Heap { + +void InternalClass::init(ExecutionEngine *engine) { - id = engine->newInternalClassId(); + Base::init(); + new (&propertyTable) PropertyHash(); + new (&nameMap) SharedInternalClassData<Identifier>(); + new (&propertyData) SharedInternalClassData<PropertyAttributes>(); + new (&transitions) std::vector<Transition>(); + + this->engine = engine; + vtable = QV4::InternalClass::staticVTable(); +// prototype = nullptr; +// parent = nullptr; +// size = 0; + extensible = true; + isFrozen = false; + isSealed = false; + isUsedAsProto = false; + protoId = engine->newProtoId(); + + // Also internal classes need an internal class pointer. Simply make it point to itself + internalClass.set(engine, this); } -InternalClass::InternalClass(const QV4::InternalClass &other) - : QQmlJS::Managed() - , engine(other.engine) - , vtable(other.vtable) - , prototype(other.prototype) - , propertyTable(other.propertyTable) - , nameMap(other.nameMap) - , propertyData(other.propertyData) - , m_sealed(nullptr) - , m_frozen(nullptr) - , size(other.size) - , extensible(other.extensible) - , isUsedAsProto(other.isUsedAsProto) +void InternalClass::init(Heap::InternalClass *other) +{ + Base::init(); + Q_ASSERT(!other->isFrozen); + new (&propertyTable) PropertyHash(other->propertyTable); + new (&nameMap) SharedInternalClassData<Identifier>(other->nameMap); + new (&propertyData) SharedInternalClassData<PropertyAttributes>(other->propertyData); + new (&transitions) std::vector<Transition>(); + + engine = other->engine; + vtable = other->vtable; + prototype = other->prototype; + parent = other; + size = other->size; + extensible = other->extensible; + isSealed = other->isSealed; + isFrozen = other->isFrozen; + isUsedAsProto = other->isUsedAsProto; + protoId = engine->newProtoId(); + + internalClass.set(engine, other->internalClass); +} + +void InternalClass::destroy() { - id = engine->newInternalClassId(); +#ifndef QT_NO_DEBUG + for (const auto &t : transitions) { + Q_ASSERT(!t.lookup || !t.lookup->isMarked()); + } +#endif + if (parent && parent->engine && parent->isMarked()) + parent->removeChildEntry(this); + + propertyTable.~PropertyHash(); + nameMap.~SharedInternalClassData<Identifier>(); + propertyData.~SharedInternalClassData<PropertyAttributes>(); + transitions.~vector<Transition>(); + engine = nullptr; + Base::destroy(); } -static void insertHoleIntoPropertyData(Object *object, int idx) +static void insertHoleIntoPropertyData(QV4::Object *object, int idx) { Heap::Object *o = object->d(); ExecutionEngine *v4 = o->internalClass->engine; @@ -142,7 +213,7 @@ static void insertHoleIntoPropertyData(Object *object, int idx) o->setProperty(v4, i, *o->propertyData(i - 1)); } -static void removeFromPropertyData(Object *object, int idx, bool accessor = false) +static void removeFromPropertyData(QV4::Object *object, int idx, bool accessor = false) { Heap::Object *o = object->d(); ExecutionEngine *v4 = o->internalClass->engine; @@ -154,20 +225,23 @@ static void removeFromPropertyData(Object *object, int idx, bool accessor = fals o->setProperty(v4, size + 1, Primitive::undefinedValue()); } -void InternalClass::changeMember(Object *object, String *string, PropertyAttributes data, uint *index) +void InternalClass::changeMember(QV4::Object *object, Identifier id, PropertyAttributes data, uint *index) { + Q_ASSERT(id.isValid()); uint idx; - InternalClass *oldClass = object->internalClass(); - InternalClass *newClass = oldClass->changeMember(string->identifier(), data, &idx); + Heap::InternalClass *oldClass = object->internalClass(); + Heap::InternalClass *newClass = oldClass->changeMember(id, data, &idx); if (index) *index = idx; + uint oldSize = oldClass->size; object->setInternalClass(newClass); - if (newClass->size > oldClass->size) { - Q_ASSERT(newClass->size == oldClass->size + 1); + // don't use oldClass anymore, it could be GC'ed + if (newClass->size > oldSize) { + Q_ASSERT(newClass->size == oldSize + 1); insertHoleIntoPropertyData(object, idx); - } else if (newClass->size < oldClass->size) { - Q_ASSERT(newClass->size == oldClass->size - 1); + } else if (newClass->size < oldSize) { + Q_ASSERT(newClass->size == oldSize - 1); removeFromPropertyData(object, idx + 1); } } @@ -183,7 +257,16 @@ InternalClassTransition &InternalClass::lookupOrInsertTransition(const InternalC } } -InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttributes data, uint *index) +static void addDummyEntry(InternalClass *newClass, PropertyHash::Entry e) +{ + // add a dummy entry, since we need two entries for accessors + newClass->propertyTable.addEntry(e, newClass->size); + newClass->nameMap.add(newClass->size, Identifier::invalid()); + newClass->propertyData.add(newClass->size, PropertyAttributes()); + ++newClass->size; +} + +Heap::InternalClass *InternalClass::changeMember(Identifier identifier, PropertyAttributes data, uint *index) { data.resolve(); uint idx = find(identifier); @@ -193,7 +276,7 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri *index = idx; if (data == propertyData.at(idx)) - return this; + return static_cast<Heap::InternalClass *>(this); Transition temp = { { identifier }, nullptr, (int)data.flags() }; Transition &t = lookupOrInsertTransition(temp); @@ -201,14 +284,34 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri return t.lookup; // create a new class and add it to the tree - InternalClass *newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); - newClass = newClass->changePrototype(prototype); - for (uint i = 0; i < size; ++i) { - if (i == idx) { - newClass = newClass->addMember(nameMap.at(i), data); - } else if (!propertyData.at(i).isEmpty()) { - newClass = newClass->addMember(nameMap.at(i), propertyData.at(i)); + Heap::InternalClass *newClass = engine->newClass(this); + if (data.isAccessor() != propertyData.at(idx).isAccessor()) { + // this changes the layout of the class, so we need to rebuild the data + newClass->propertyTable = PropertyHash(); + newClass->nameMap = SharedInternalClassData<Identifier>(); + newClass->propertyData = SharedInternalClassData<PropertyAttributes>(); + newClass->size = 0; + for (uint i = 0; i < size; ++i) { + Identifier identifier = nameMap.at(i); + PropertyHash::Entry e = { identifier, newClass->size }; + if (i && !identifier.isValid()) + e.identifier = nameMap.at(i - 1); + newClass->propertyTable.addEntry(e, newClass->size); + newClass->nameMap.add(newClass->size, identifier); + if (i == idx) { + newClass->propertyData.add(newClass->size, data); + ++newClass->size; + if (data.isAccessor()) + addDummyEntry(newClass, e); + else + ++i; + } else { + newClass->propertyData.add(newClass->size, propertyData.at(i)); + ++newClass->size; + } } + } else { + newClass->propertyData.set(idx, data); } t.lookup = newClass; @@ -216,14 +319,16 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri return newClass; } -InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) +Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) { + Scope scope(engine); + ScopedValue protectThis(scope, this); if (proto) proto->setUsedAsProto(); Q_ASSERT(prototype != proto); Q_ASSERT(!proto || proto->internalClass->isUsedAsProto); - Transition temp = { { nullptr }, nullptr, Transition::PrototypeChange }; + Transition temp = { { Identifier::invalid() }, nullptr, Transition::PrototypeChange }; temp.prototype = proto; Transition &t = lookupOrInsertTransition(temp); @@ -231,29 +336,19 @@ InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) return t.lookup; // create a new class and add it to the tree - InternalClass *newClass; - if (!size && !prototype) { - newClass = engine->newClass(*this); - newClass->prototype = proto; - } else { - newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); - newClass = newClass->changePrototype(proto); - for (uint i = 0; i < size; ++i) { - if (!propertyData.at(i).isEmpty()) - newClass = newClass->addMember(nameMap.at(i), propertyData.at(i)); - } - } + Heap::InternalClass *newClass = engine->newClass(this); + newClass->prototype = proto; t.lookup = newClass; return newClass; } -InternalClass *InternalClass::changeVTableImpl(const VTable *vt) +Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt) { Q_ASSERT(vtable != vt); - Transition temp = { { nullptr }, nullptr, Transition::VTableChange }; + Transition temp = { { Identifier::invalid() }, nullptr, Transition::VTableChange }; temp.vtable = vt; Transition &t = lookupOrInsertTransition(temp); @@ -261,18 +356,8 @@ InternalClass *InternalClass::changeVTableImpl(const VTable *vt) return t.lookup; // create a new class and add it to the tree - InternalClass *newClass; - if (this == engine->internalClasses[EngineBase::Class_Empty]) { - newClass = engine->newClass(*this); - newClass->vtable = vt; - } else { - newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vt); - newClass = newClass->changePrototype(prototype); - for (uint i = 0; i < size; ++i) { - if (!propertyData.at(i).isEmpty()) - newClass = newClass->addMember(nameMap.at(i), propertyData.at(i)); - } - } + Heap::InternalClass *newClass = engine->newClass(this); + newClass->vtable = vt; t.lookup = newClass; Q_ASSERT(t.lookup); @@ -280,17 +365,17 @@ InternalClass *InternalClass::changeVTableImpl(const VTable *vt) return newClass; } -InternalClass *InternalClass::nonExtensible() +Heap::InternalClass *InternalClass::nonExtensible() { if (!extensible) return this; - Transition temp = { { nullptr }, nullptr, Transition::NotExtensible}; + Transition temp = { { Identifier::invalid() }, nullptr, Transition::NotExtensible}; Transition &t = lookupOrInsertTransition(temp); if (t.lookup) return t.lookup; - InternalClass *newClass = engine->newClass(*this); + Heap::InternalClass *newClass = engine->newClass(this); newClass->extensible = false; t.lookup = newClass; @@ -298,31 +383,26 @@ InternalClass *InternalClass::nonExtensible() return newClass; } -void InternalClass::addMember(Object *object, String *string, PropertyAttributes data, uint *index) +void InternalClass::addMember(QV4::Object *object, Identifier id, PropertyAttributes data, uint *index) { + Q_ASSERT(id.isValid()); data.resolve(); - object->internalClass()->engine->identifierTable->identifier(string); - if (object->internalClass()->propertyTable.lookup(string->d()->identifier) < object->internalClass()->size) { - changeMember(object, string, data, index); + if (object->internalClass()->propertyTable.lookup(id) < object->internalClass()->size) { + changeMember(object, id, data, index); return; } uint idx; - InternalClass *newClass = object->internalClass()->addMemberImpl(string->identifier(), data, &idx); + Heap::InternalClass *newClass = object->internalClass()->addMemberImpl(id, data, &idx); if (index) *index = idx; object->setInternalClass(newClass); } -InternalClass *InternalClass::addMember(String *string, PropertyAttributes data, uint *index) -{ - engine->identifierTable->identifier(string); - return addMember(string->identifier(), data, index); -} - -InternalClass *InternalClass::addMember(Identifier *identifier, PropertyAttributes data, uint *index) +Heap::InternalClass *InternalClass::addMember(Identifier identifier, PropertyAttributes data, uint *index) { + Q_ASSERT(identifier.isValid()); data.resolve(); if (propertyTable.lookup(identifier) < size) @@ -331,7 +411,7 @@ InternalClass *InternalClass::addMember(Identifier *identifier, PropertyAttribut return addMemberImpl(identifier, data, index); } -InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index) +Heap::InternalClass *InternalClass::addMemberImpl(Identifier identifier, PropertyAttributes data, uint *index) { Transition temp = { { identifier }, nullptr, (int)data.flags() }; Transition &t = lookupOrInsertTransition(temp); @@ -343,109 +423,156 @@ InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttr return t.lookup; // create a new class and add it to the tree - InternalClass *newClass = engine->newClass(*this); + Heap::InternalClass *newClass = engine->newClass(this); PropertyHash::Entry e = { identifier, newClass->size }; newClass->propertyTable.addEntry(e, newClass->size); newClass->nameMap.add(newClass->size, identifier); newClass->propertyData.add(newClass->size, data); ++newClass->size; - if (data.isAccessor()) { - // add a dummy entry, since we need two entries for accessors - newClass->propertyTable.addEntry(e, newClass->size); - newClass->nameMap.add(newClass->size, 0); - newClass->propertyData.add(newClass->size, PropertyAttributes()); - ++newClass->size; - } + if (data.isAccessor()) + addDummyEntry(newClass, e); t.lookup = newClass; Q_ASSERT(t.lookup); return newClass; } -void InternalClass::removeMember(Object *object, Identifier *id) +void InternalClass::removeChildEntry(InternalClass *child) { - InternalClass *oldClass = object->internalClass(); - uint propIdx = oldClass->propertyTable.lookup(id); - Q_ASSERT(propIdx < oldClass->size); + Q_ASSERT(engine); + for (auto &t : transitions) { + if (t.lookup == child) { + t.lookup = nullptr; + return; + } + } + Q_UNREACHABLE(); - Transition temp = { { id }, nullptr, -1 }; - Transition &t = object->internalClass()->lookupOrInsertTransition(temp); +} - bool accessor = oldClass->propertyData.at(propIdx).isAccessor(); +void InternalClass::removeMember(QV4::Object *object, Identifier identifier) +{ + Heap::InternalClass *oldClass = object->internalClass(); + Q_ASSERT(oldClass->propertyTable.lookup(identifier) < oldClass->size); - if (t.lookup) { - object->setInternalClass(t.lookup); - } else { + Transition temp = { { identifier }, nullptr, Transition::RemoveMember }; + Transition &t = object->internalClass()->lookupOrInsertTransition(temp); + + if (!t.lookup) { // create a new class and add it to the tree - InternalClass *newClass = oldClass->engine->internalClasses[EngineBase::Class_Empty]->changeVTable(oldClass->vtable); - newClass = newClass->changePrototype(oldClass->prototype); - for (uint i = 0; i < oldClass->size; ++i) { - if (i == propIdx) - continue; - if (!oldClass->propertyData.at(i).isEmpty()) - newClass = newClass->addMember(oldClass->nameMap.at(i), oldClass->propertyData.at(i)); - } - object->setInternalClass(newClass); + Heap::InternalClass *newClass = oldClass->engine->newClass(oldClass); + // simply make the entry inaccessible + int idx = newClass->propertyTable.removeIdentifier(identifier, oldClass->size); + newClass->nameMap.set(idx, Identifier::invalid()); + newClass->propertyData.set(idx, PropertyAttributes()); + t.lookup = newClass; + Q_ASSERT(t.lookup); } + object->setInternalClass(t.lookup); - Q_ASSERT(object->internalClass()->size == oldClass->size - (accessor ? 2 : 1)); - - // remove the entry in the property data - removeFromPropertyData(object, propIdx, accessor); - - t.lookup = object->internalClass(); - Q_ASSERT(t.lookup); + // we didn't remove the data slot, just made it inaccessible + Q_ASSERT(object->internalClass()->size == oldClass->size); } -uint InternalClass::find(const String *string) +Heap::InternalClass *InternalClass::sealed() { - engine->identifierTable->identifier(string); - const Identifier *id = string->d()->identifier; + if (isSealed) + return this; - uint index = propertyTable.lookup(id); - if (index < size) - return index; + bool alreadySealed = !extensible; + for (uint i = 0; i < size; ++i) { + PropertyAttributes attrs = propertyData.at(i); + if (attrs.isEmpty()) + continue; + if (attrs.isConfigurable()) { + alreadySealed = false; + break; + } + } - return UINT_MAX; -} + if (alreadySealed) { + isSealed = true; + return this; + } -InternalClass *InternalClass::sealed() -{ - if (m_sealed) - return m_sealed; + Transition temp = { { Identifier::invalid() }, nullptr, InternalClassTransition::Sealed }; + Transition &t = lookupOrInsertTransition(temp); + + if (t.lookup) { + Q_ASSERT(t.lookup && t.lookup->isSealed); + return t.lookup; + } + + Heap::InternalClass *s = engine->newClass(this); - m_sealed = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); - m_sealed = m_sealed->changePrototype(prototype); for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); if (attrs.isEmpty()) continue; attrs.setConfigurable(false); - m_sealed = m_sealed->addMember(nameMap.at(i), attrs); + s->propertyData.set(i, attrs); } - m_sealed = m_sealed->nonExtensible(); + s->extensible = false; + s->isSealed = true; - m_sealed->m_sealed = m_sealed; - return m_sealed; + t.lookup = s; + return s; } -InternalClass *InternalClass::frozen() +Heap::InternalClass *InternalClass::frozen() { - if (m_frozen) - return m_frozen; + if (isFrozen) + return this; + + bool alreadyFrozen = !extensible; + for (uint i = 0; i < size; ++i) { + PropertyAttributes attrs = propertyData.at(i); + if (attrs.isEmpty()) + continue; + if ((attrs.isData() && attrs.isWritable()) || attrs.isConfigurable()) { + alreadyFrozen = false; + break; + } + } + + if (alreadyFrozen) { + isSealed = true; + isFrozen = true; + return this; + } + + Transition temp = { { Identifier::invalid() }, nullptr, InternalClassTransition::Frozen }; + Transition &t = lookupOrInsertTransition(temp); + + if (t.lookup) { + Q_ASSERT(t.lookup && t.lookup->isSealed && t.lookup->isFrozen); + return t.lookup; + } + + Heap::InternalClass *f = engine->newClass(this); - m_frozen = propertiesFrozen(); - m_frozen = m_frozen->nonExtensible(); + for (uint i = 0; i < size; ++i) { + PropertyAttributes attrs = propertyData.at(i); + if (attrs.isEmpty()) + continue; + if (attrs.isData()) + attrs.setWritable(false); + attrs.setConfigurable(false); + f->propertyData.set(i, attrs); + } + f->extensible = false; + f->isSealed = true; + f->isFrozen = true; - m_frozen->m_frozen = m_frozen; - m_frozen->m_sealed = m_frozen; - return m_frozen; + t.lookup = f; + return f; } -InternalClass *InternalClass::propertiesFrozen() const +Heap::InternalClass *InternalClass::propertiesFrozen() const { - InternalClass *frozen = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); + Scope scope(engine); + Scoped<QV4::InternalClass> frozen(scope, engine->internalClasses(EngineBase::Class_Empty)->changeVTable(vtable)); frozen = frozen->changePrototype(prototype); for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); @@ -455,20 +582,20 @@ InternalClass *InternalClass::propertiesFrozen() const attrs.setConfigurable(false); frozen = frozen->addMember(nameMap.at(i), attrs); } - return frozen; + return frozen->d(); } -InternalClass *InternalClass::asProtoClass() +Heap::InternalClass *InternalClass::asProtoClass() { if (isUsedAsProto) return this; - Transition temp = { { nullptr }, nullptr, Transition::ProtoClass }; + Transition temp = { { Identifier::invalid() }, nullptr, Transition::ProtoClass }; Transition &t = lookupOrInsertTransition(temp); if (t.lookup) return t.lookup; - InternalClass *newClass = engine->newClass(*this); + Heap::InternalClass *newClass = engine->newClass(this); newClass->isUsedAsProto = true; t.lookup = newClass; @@ -476,90 +603,43 @@ InternalClass *InternalClass::asProtoClass() return newClass; } -void InternalClass::destroy() +static void updateProtoUsage(Heap::Object *o, Heap::InternalClass *ic) { - std::vector<InternalClass *> destroyStack; - destroyStack.reserve(64); - destroyStack.push_back(this); - - while (!destroyStack.empty()) { - InternalClass *next = destroyStack.back(); - destroyStack.pop_back(); - if (!next->engine) - continue; - next->engine = nullptr; - next->propertyTable.~PropertyHash(); - next->nameMap.~SharedInternalClassData<Identifier *>(); - next->propertyData.~SharedInternalClassData<PropertyAttributes>(); - if (next->m_sealed) - destroyStack.push_back(next->m_sealed); - if (next->m_frozen) - destroyStack.push_back(next->m_frozen); - - for (size_t i = 0; i < next->transitions.size(); ++i) { - Q_ASSERT(next->transitions.at(i).lookup); - destroyStack.push_back(next->transitions.at(i).lookup); - } - - next->transitions.~vector<Transition>(); + if (ic->prototype == o) + ic->protoId = ic->engine->newProtoId(); + for (auto &t : ic->transitions) { + if (t.lookup) + updateProtoUsage(o, t.lookup); } } + void InternalClass::updateProtoUsage(Heap::Object *o) { Q_ASSERT(isUsedAsProto); - InternalClass *ic = engine->internalClasses[EngineBase::Class_Empty]; + Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_Empty); Q_ASSERT(!ic->prototype); - // only need to go two levels into the IC hierarchy, as prototype changes - // can only happen there - for (auto &t : ic->transitions) { - Q_ASSERT(t.lookup); - if (t.flags == InternalClassTransition::VTableChange) { - InternalClass *ic2 = t.lookup; - for (auto &t2 : ic2->transitions) { - if (t2.flags == InternalClassTransition::PrototypeChange && - t2.lookup->prototype == o) - ic2->updateInternalClassIdRecursive(); - } - } else if (t.flags == InternalClassTransition::PrototypeChange && t.lookup->prototype == o) { - ic->updateInternalClassIdRecursive(); - } - } + Heap::updateProtoUsage(o, ic); } -void InternalClass::updateInternalClassIdRecursive() +void InternalClass::markObjects(Heap::Base *b, MarkStack *stack) { - id = engine->newInternalClassId(); - for (auto &t : transitions) { - Q_ASSERT(t.lookup); - if (t.flags == InternalClassTransition::VTableChange || t.flags == InternalClassTransition::PrototypeChange) - continue; - t.lookup->updateInternalClassIdRecursive(); + Heap::InternalClass *ic = static_cast<Heap::InternalClass *>(b); + if (ic->prototype) + ic->prototype->mark(stack); + if (ic->parent) + ic->parent->mark(stack); + + for (uint i = 0; i < ic->size; ++i) { + Identifier id = ic->nameMap.at(i); + if (Heap::Base *b = id.asHeapObject()) + b->mark(stack); } } +} - -void InternalClassPool::markObjects(MarkStack *markStack) -{ - InternalClass *ic = markStack->engine->internalClasses[EngineBase::Class_Empty]; - Q_ASSERT(!ic->prototype); - - // only need to go two levels into the IC hierarchy, as prototype changes - // can only happen there - for (auto &t : ic->transitions) { - Q_ASSERT(t.lookup); - if (t.flags == InternalClassTransition::VTableChange) { - InternalClass *ic2 = t.lookup; - for (auto &t2 : ic2->transitions) { - if (t2.flags == InternalClassTransition::PrototypeChange) - t2.lookup->prototype->mark(markStack); - } - } else if (t.flags == InternalClassTransition::PrototypeChange) { - t.lookup->prototype->mark(markStack); - } - } } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index b689272006..290251f4ba 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -53,16 +53,13 @@ #include "qv4global_p.h" #include <QHash> -#include <private/qqmljsmemorypool_p.h> #include <private/qv4identifier_p.h> +#include <private/qv4heap_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -struct String; -struct Object; -struct Identifier; struct VTable; struct MarkStack; @@ -70,7 +67,7 @@ struct PropertyHashData; struct PropertyHash { struct Entry { - const Identifier *identifier; + Identifier identifier; uint index; }; @@ -79,12 +76,12 @@ struct PropertyHash inline PropertyHash(); inline PropertyHash(const PropertyHash &other); inline ~PropertyHash(); + PropertyHash &operator=(const PropertyHash &other); void addEntry(const Entry &entry, int classSize); - uint lookup(const Identifier *identifier) const; - -private: - PropertyHash &operator=(const PropertyHash &other); + uint lookup(Identifier identifier) const; + int removeIdentifier(Identifier identifier, int classSize); + void detach(bool grow, int classSize); }; struct PropertyHashData @@ -118,15 +115,26 @@ inline PropertyHash::~PropertyHash() delete d; } -inline uint PropertyHash::lookup(const Identifier *identifier) const +inline PropertyHash &PropertyHash::operator=(const PropertyHash &other) +{ + ++other.d->refCount; + if (!--d->refCount) + delete d; + d = other.d; + return *this; +} + + + +inline uint PropertyHash::lookup(Identifier identifier) const { Q_ASSERT(d->entries); - uint idx = identifier->hashValue % d->alloc; + uint idx = identifier.id % d->alloc; while (1) { if (d->entries[idx].identifier == identifier) return d->entries[idx].index; - if (!d->entries[idx].identifier) + if (!d->entries[idx].identifier.isValid()) return UINT_MAX; ++idx; idx %= d->alloc; @@ -163,6 +171,13 @@ struct SharedInternalClassData { if (!--d->refcount) delete d; } + SharedInternalClassData &operator=(const SharedInternalClassData &other) { + ++other.d->refcount; + if (!--d->refcount) + delete d; + d = other.d; + return *this; + } void add(uint pos, T value) { if (pos < d->size) { @@ -214,26 +229,26 @@ struct SharedInternalClassData { Q_ASSERT(i < d->size); return d->data[i]; } - -private: - SharedInternalClassData &operator=(const SharedInternalClassData &other); }; struct InternalClassTransition { union { - Identifier *id; + Identifier id; const VTable *vtable; Heap::Object *prototype; }; - InternalClass *lookup; + Heap::InternalClass *lookup; int flags; enum { // range 0-0xff is reserved for attribute changes NotExtensible = 0x100, VTableChange = 0x200, PrototypeChange = 0x201, - ProtoClass = 0x202 + ProtoClass = 0x202, + Sealed = 0x203, + Frozen = 0x204, + RemoveMember = -1 }; bool operator==(const InternalClassTransition &other) const @@ -243,48 +258,44 @@ struct InternalClassTransition { return id < other.id || (id == other.id && flags < other.flags); } }; -struct InternalClass : public QQmlJS::Managed { - int id = 0; // unique across the engine, gets changed also when proto chain changes +namespace Heap { + +struct InternalClass : Base { ExecutionEngine *engine; const VTable *vtable; + quintptr protoId; // unique across the engine, gets changed whenever the proto chain changes Heap::Object *prototype; + InternalClass *parent; PropertyHash propertyTable; // id to valueIndex - SharedInternalClassData<Identifier *> nameMap; + SharedInternalClassData<Identifier> nameMap; SharedInternalClassData<PropertyAttributes> propertyData; typedef InternalClassTransition Transition; std::vector<Transition> transitions; InternalClassTransition &lookupOrInsertTransition(const InternalClassTransition &t); - InternalClass *m_sealed; - InternalClass *m_frozen; - uint size; bool extensible; - bool isUsedAsProto = false; + bool isSealed; + bool isFrozen; + bool isUsedAsProto; + + void init(ExecutionEngine *engine); + void init(InternalClass *other); + void destroy(); Q_REQUIRED_RESULT InternalClass *nonExtensible(); - Q_REQUIRED_RESULT InternalClass *changeVTable(const VTable *vt) { - if (vtable == vt) - return this; - return changeVTableImpl(vt); - } - Q_REQUIRED_RESULT InternalClass *changePrototype(Heap::Object *proto) { - if (prototype == proto) - return this; - return changePrototypeImpl(proto); - } - static void addMember(Object *object, String *string, PropertyAttributes data, uint *index); - Q_REQUIRED_RESULT InternalClass *addMember(String *string, PropertyAttributes data, uint *index = nullptr); - Q_REQUIRED_RESULT InternalClass *addMember(Identifier *identifier, PropertyAttributes data, uint *index = nullptr); - Q_REQUIRED_RESULT InternalClass *changeMember(Identifier *identifier, PropertyAttributes data, uint *index = nullptr); - static void changeMember(Object *object, String *string, PropertyAttributes data, uint *index = nullptr); - static void removeMember(Object *object, Identifier *id); - uint find(const String *string); - uint find(const Identifier *id) + static void addMember(QV4::Object *object, Identifier id, PropertyAttributes data, uint *index); + Q_REQUIRED_RESULT InternalClass *addMember(Identifier identifier, PropertyAttributes data, uint *index = nullptr); + Q_REQUIRED_RESULT InternalClass *changeMember(Identifier identifier, PropertyAttributes data, uint *index = nullptr); + static void changeMember(QV4::Object *object, Identifier id, PropertyAttributes data, uint *index = nullptr); + static void removeMember(QV4::Object *object, Identifier identifier); + uint find(const Identifier id) { + Q_ASSERT(id.isValid()); + uint index = propertyTable.lookup(id); if (index < size) return index; @@ -298,24 +309,37 @@ struct InternalClass : public QQmlJS::Managed { Q_REQUIRED_RESULT InternalClass *asProtoClass(); - void destroy(); + Q_REQUIRED_RESULT InternalClass *changeVTable(const VTable *vt) { + if (vtable == vt) + return this; + return changeVTableImpl(vt); + } + Q_REQUIRED_RESULT InternalClass *changePrototype(Heap::Object *proto) { + if (prototype == proto) + return this; + return changePrototypeImpl(proto); + } void updateProtoUsage(Heap::Object *o); + static void markObjects(Heap::Base *ic, MarkStack *stack); + private: Q_QML_EXPORT InternalClass *changeVTableImpl(const VTable *vt); Q_QML_EXPORT InternalClass *changePrototypeImpl(Heap::Object *proto); - InternalClass *addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index); - void updateInternalClassIdRecursive(); + InternalClass *addMemberImpl(Identifier identifier, PropertyAttributes data, uint *index); + + void removeChildEntry(InternalClass *child); friend struct ExecutionEngine; - InternalClass(ExecutionEngine *engine); - InternalClass(const InternalClass &other); }; -struct InternalClassPool : public QQmlJS::MemoryPool +inline +void Base::markObjects(Base *b, MarkStack *stack) { - void markObjects(MarkStack *markStack); -}; + b->internalClass->mark(stack); +} + +} } diff --git a/src/qml/jsruntime/qv4iterator.cpp b/src/qml/jsruntime/qv4iterator.cpp new file mode 100644 index 0000000000..df8000a8f7 --- /dev/null +++ b/src/qml/jsruntime/qv4iterator.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qv4iterator_p.h> +#include <qv4symbol_p.h> +#include <qv4engine_p.h> + +using namespace QV4; + +void IteratorPrototype::init(ExecutionEngine *engine) +{ + defineDefaultProperty(engine->symbol_iterator(), method_iterator, 0); +} + +ReturnedValue IteratorPrototype::method_iterator(const FunctionObject *, const Value *thisObject, const Value *, int) +{ + return thisObject->asReturnedValue(); +} + + +ReturnedValue IteratorPrototype::createIterResultObject(ExecutionEngine *engine, const Value &value, bool done) +{ + Scope scope(engine); + ScopedObject obj(scope, engine->newObject()); + obj->set(ScopedString(scope, engine->newString(QStringLiteral("value"))), value, Object::DoNotThrow); + obj->set(ScopedString(scope, engine->newString(QStringLiteral("done"))), Primitive::fromBoolean(done), Object::DoNotThrow); + return obj->asReturnedValue(); +} + diff --git a/src/qml/jsruntime/qv4iterator_p.h b/src/qml/jsruntime/qv4iterator_p.h new file mode 100644 index 0000000000..28e337d21b --- /dev/null +++ b/src/qml/jsruntime/qv4iterator_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4ITERATOR_P_H +#define QV4ITERATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" +#include "qv4arraydata_p.h" + +QT_BEGIN_NAMESPACE + + +namespace QV4 { + +enum IteratorKind { + KeyIteratorKind, + ValueIteratorKind, + KeyValueIteratorKind +}; + +struct IteratorPrototype : Object +{ + void init(ExecutionEngine *engine); + + static ReturnedValue method_iterator(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue createIterResultObject(ExecutionEngine *engine, const Value &value, bool done); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4ARRAYITERATOR_P_H + diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h index c676b57c51..e186285025 100644 --- a/src/qml/jsruntime/qv4jscall_p.h +++ b/src/qml/jsruntime/qv4jscall_p.h @@ -67,7 +67,7 @@ struct JSCallData { if (thisObject) this->thisObject = const_cast<Value *>(thisObject); else - this->thisObject = scope.alloc(1); + this->thisObject = scope.alloc(); if (argv) this->args = const_cast<Value *>(argv); else @@ -80,8 +80,7 @@ struct JSCallData { CallData *callData(const FunctionObject *f = nullptr) const { int size = int(offsetof(QV4::CallData, args)/sizeof(QV4::Value)) + argc; - CallData *ptr = reinterpret_cast<CallData *>(scope.engine->jsStackTop); - scope.engine->jsStackTop += size; + CallData *ptr = reinterpret_cast<CallData *>(scope.alloc<Scope::Uninitialized>(size)); ptr->function = Encode::undefined(); ptr->context = Encode::undefined(); ptr->accumulator = Encode::undefined(); diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index c3569c29d2..75a9bf4111 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -47,6 +47,7 @@ #include <qv4variantobject_p.h> #include "qv4string_p.h" #include "qv4jscall_p.h" +#include <qv4symbol_p.h> #include <qstack.h> #include <qstringlist.h> @@ -743,7 +744,7 @@ QString Stringify::Str(const QString &key, const Value &v) o = value->asReturnedValue(); if (o) { if (!o->as<FunctionObject>()) { - if (o->as<ArrayObject>() || o->isListType()) { + if (o->isArrayLike()) { return JA(o.getPointer()); } else { return JO(o); @@ -881,6 +882,8 @@ void Heap::JsonObject::init() o->defineDefaultProperty(QStringLiteral("parse"), QV4::JsonObject::method_parse, 2); o->defineDefaultProperty(QStringLiteral("stringify"), QV4::JsonObject::method_stringify, 3); + ScopedString json(scope, scope.engine->newString(QStringLiteral("JSON"))); + o->defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), json); } diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 52ab03cd94..67b4b9dc38 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -47,7 +47,7 @@ QT_BEGIN_NAMESPACE using namespace QV4; -void Lookup::resolveProtoGetter(Identifier *name, const Heap::Object *proto) +void Lookup::resolveProtoGetter(Identifier name, const Heap::Object *proto) { while (proto) { uint index = proto->internalClass->find(name); @@ -70,7 +70,12 @@ void Lookup::resolveProtoGetter(Identifier *name, const Heap::Object *proto) ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *object) { Heap::Object *obj = object->d(); - Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + Identifier name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + if (name.isArrayIndex()) { + indexedLookup.index = name.asArrayIndex(); + getter = getterIndexed; + return getter(this, engine, *object); + } uint index = obj->internalClass->find(name); if (index != UINT_MAX) { @@ -92,7 +97,7 @@ ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *objec return getter(this, engine, *object); } - protoLookup.icIdentifier = obj->internalClass->id; + protoLookup.protoId = obj->internalClass->protoId; resolveProtoGetter(name, obj->prototype()); return getter(this, engine, *object); } @@ -109,11 +114,12 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu break; case Value::Managed_Type: { // ### Should move this over to the Object path, as strings also have an internalClass - Q_ASSERT(object.isString()); - primitiveLookup.proto = engine->stringPrototype()->d(); + Q_ASSERT(object.isStringOrSymbol()); + primitiveLookup.proto = static_cast<const Managed &>(object).internalClass()->prototype; + Q_ASSERT(primitiveLookup.proto); Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - if (name->equals(engine->id_length())) { + if (object.isString() && name->equals(engine->id_length())) { // special case, as the property is on the object itself getter = stringLengthGetter; return stringLengthGetter(this, engine, object); @@ -125,8 +131,8 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu primitiveLookup.proto = engine->numberPrototype()->d(); } - Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - protoLookup.icIdentifier = primitiveLookup.proto->internalClass->id; + Identifier name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + protoLookup.protoId = primitiveLookup.proto->internalClass->protoId; resolveProtoGetter(name, primitiveLookup.proto); if (getter == getterProto) @@ -139,8 +145,8 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu ReturnedValue Lookup::resolveGlobalGetter(ExecutionEngine *engine) { Object *o = engine->globalObject; - Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - protoLookup.icIdentifier = o->internalClass()->id; + Identifier name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + protoLookup.protoId = o->internalClass()->protoId; resolveProtoGetter(name, o->d()); if (getter == getterProto) @@ -188,16 +194,16 @@ ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const return result; } if (first.getter == getterProto && second.getter == getterProto) { - l->protoLookupTwoClasses.icIdentifier = first.protoLookup.icIdentifier; - l->protoLookupTwoClasses.icIdentifier2 = second.protoLookup.icIdentifier; + l->protoLookupTwoClasses.protoId = first.protoLookup.protoId; + l->protoLookupTwoClasses.protoId2 = second.protoLookup.protoId; l->protoLookupTwoClasses.data = first.protoLookup.data; l->protoLookupTwoClasses.data2 = second.protoLookup.data; l->getter = getterProtoTwoClasses; return result; } if (first.getter == getterProtoAccessor && second.getter == getterProtoAccessor) { - l->protoLookupTwoClasses.icIdentifier = first.protoLookup.icIdentifier; - l->protoLookupTwoClasses.icIdentifier2 = second.protoLookup.icIdentifier; + l->protoLookupTwoClasses.protoId = first.protoLookup.protoId; + l->protoLookupTwoClasses.protoId2 = second.protoLookup.protoId; l->protoLookupTwoClasses.data = first.protoLookup.data; l->protoLookupTwoClasses.data2 = second.protoLookup.data; l->getter = getterProtoAccessorTwoClasses; @@ -250,7 +256,7 @@ ReturnedValue Lookup::getterProto(Lookup *l, ExecutionEngine *engine, const Valu // the internal class won't match Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { - if (l->protoLookup.icIdentifier == o->internalClass->id) + if (l->protoLookup.protoId == o->internalClass->protoId) return l->protoLookup.data->asReturnedValue(); } return getterTwoClasses(l, engine, object); @@ -307,9 +313,9 @@ ReturnedValue Lookup::getterProtoTwoClasses(Lookup *l, ExecutionEngine *engine, // the internal class won't match Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { - if (l->protoLookupTwoClasses.icIdentifier == o->internalClass->id) + if (l->protoLookupTwoClasses.protoId == o->internalClass->protoId) return l->protoLookupTwoClasses.data->asReturnedValue(); - if (l->protoLookupTwoClasses.icIdentifier2 == o->internalClass->id) + if (l->protoLookupTwoClasses.protoId2 == o->internalClass->protoId) return l->protoLookupTwoClasses.data2->asReturnedValue(); return getterFallback(l, engine, object); } @@ -340,14 +346,13 @@ ReturnedValue Lookup::getterProtoAccessor(Lookup *l, ExecutionEngine *engine, co // we can safely cast to a QV4::Object here. If object is actually a string, // the internal class won't match Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); - if (o && l->protoLookup.icIdentifier == o->internalClass->id) { + if (o && l->protoLookup.protoId == o->internalClass->protoId) { const Value *getter = l->protoLookup.data; if (!getter->isFunctionObject()) // ### catch at resolve time return Encode::undefined(); return static_cast<const FunctionObject *>(getter)->call(&object, nullptr, 0); } - l->getter = getterTwoClasses; return getterTwoClasses(l, engine, object); } @@ -358,9 +363,9 @@ ReturnedValue Lookup::getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine * Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { const Value *getter = nullptr; - if (l->protoLookupTwoClasses.icIdentifier == o->internalClass->id) + if (l->protoLookupTwoClasses.protoId == o->internalClass->protoId) getter = l->protoLookupTwoClasses.data; - else if (l->protoLookupTwoClasses.icIdentifier2 == o->internalClass->id) + else if (l->protoLookupTwoClasses.protoId2 == o->internalClass->protoId) getter = l->protoLookupTwoClasses.data2; if (getter) { if (!getter->isFunctionObject()) // ### catch at resolve time @@ -373,11 +378,29 @@ ReturnedValue Lookup::getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine * return getterFallback(l, engine, object); } +ReturnedValue Lookup::getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object) +{ + Object *o = object.objectValue(); + if (o) { + Heap::Object *ho = o->d(); + if (ho->arrayData && ho->arrayData->type == Heap::ArrayData::Simple) { + Heap::SimpleArrayData *s = ho->arrayData.cast<Heap::SimpleArrayData>(); + if (l->indexedLookup.index < s->values.size) + if (!s->data(l->indexedLookup.index).isEmpty()) + return s->data(l->indexedLookup.index).asReturnedValue(); + } + return o->getIndexed(l->indexedLookup.index); + } + l->getter = getterFallback; + return getterFallback(l, engine, object); + +} + ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object) { if (object.type() == l->primitiveLookup.type) { Heap::Object *o = l->primitiveLookup.proto; - if (l->primitiveLookup.icIdentifier == o->internalClass->id) + if (l->primitiveLookup.protoId == o->internalClass->protoId) return l->primitiveLookup.data->asReturnedValue(); } l->getter = getterGeneric; @@ -388,7 +411,7 @@ ReturnedValue Lookup::primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine { if (object.type() == l->primitiveLookup.type) { Heap::Object *o = l->primitiveLookup.proto; - if (l->primitiveLookup.icIdentifier == o->internalClass->id) { + if (l->primitiveLookup.protoId == o->internalClass->protoId) { const Value *getter = l->primitiveLookup.data; if (!getter->isFunctionObject()) // ### catch at resolve time return Encode::undefined(); @@ -417,7 +440,7 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetterProto(Lookup *l, ExecutionEngine *engine) { Heap::Object *o = engine->globalObject->d(); - if (l->protoLookup.icIdentifier == o->internalClass->id) + if (l->protoLookup.protoId == o->internalClass->protoId) return l->protoLookup.data->asReturnedValue(); l->globalGetter = globalGetterGeneric; return globalGetterGeneric(l, engine); @@ -426,7 +449,7 @@ ReturnedValue Lookup::globalGetterProto(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetterProtoAccessor(Lookup *l, ExecutionEngine *engine) { Heap::Object *o = engine->globalObject->d(); - if (l->protoLookup.icIdentifier == o->internalClass->id) { + if (l->protoLookup.protoId == o->internalClass->protoId) { const Value *getter = l->protoLookup.data; if (!getter->isFunctionObject()) // ### catch at resolve time return Encode::undefined(); @@ -442,8 +465,9 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value Scope scope(engine); ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - InternalClass *c = object->internalClass(); - uint idx = c->find(name); + Heap::InternalClass *c = object->internalClass(); + name->makeIdentifier(); + uint idx = c->find(name->identifier()); if (idx != UINT_MAX) { if (object->isArrayObject() && idx == Heap::ArrayObject::LengthPropertyIndex) { setter = arrayLengthSetter; @@ -460,7 +484,7 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value return setter(this, engine, *object, value); } - insertionLookup.icIdentifier = c->id; + insertionLookup.protoId = c->protoId; if (!object->put(name, value)) { setter = Lookup::setterFallback; return false; @@ -471,7 +495,8 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value setter = setterFallback; return true; } - idx = object->internalClass()->find(name); + name->makeIdentifier(); + idx = object->internalClass()->find(name->identifier()); if (idx == UINT_MAX) { // ### can this even happen? setter = setterFallback; return false; @@ -574,7 +599,7 @@ bool Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, c bool Lookup::setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Object *o = static_cast<Object *>(object.managed()); - if (o && o->internalClass()->id == l->insertionLookup.icIdentifier) { + if (o && o->internalClass()->protoId == l->insertionLookup.protoId) { o->setInternalClass(l->insertionLookup.newClass); o->d()->setProperty(engine, l->insertionLookup.offset, value); return true; diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 5f507733fd..7fb9976135 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -65,7 +65,6 @@ QT_BEGIN_NAMESPACE namespace QV4 { struct Lookup { - enum { Size = 4 }; union { ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object); ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine); @@ -73,44 +72,57 @@ struct Lookup { }; union { struct { - InternalClass *ic; + Heap::Base *h1; + Heap::Base *h2; + quintptr unused; + quintptr unused2; + } markDef; + struct { + Heap::InternalClass *ic; + quintptr _unused; int offset; } objectLookup; struct { + quintptr protoId; + quintptr _unused; const Value *data; - int icIdentifier; } protoLookup; struct { - InternalClass *ic; - InternalClass *ic2; + Heap::InternalClass *ic; + Heap::InternalClass *ic2; int offset; int offset2; } objectLookupTwoClasses; struct { + quintptr protoId; + quintptr protoId2; const Value *data; const Value *data2; - int icIdentifier; - int icIdentifier2; } protoLookupTwoClasses; struct { // Make sure the next two values are in sync with protoLookup - const Value *data; - int icIdentifier; - unsigned type; + quintptr protoId; Heap::Object *proto; + const Value *data; + quintptr type; } primitiveLookup; struct { - InternalClass *newClass; - int icIdentifier; + Heap::InternalClass *newClass; + quintptr protoId; int offset; } insertionLookup; + struct { + quintptr _unused; + quintptr _unused2; + uint index; + } indexedLookup; }; uint nameIndex; ReturnedValue resolveGetter(ExecutionEngine *engine, const Object *object); ReturnedValue resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object); ReturnedValue resolveGlobalGetter(ExecutionEngine *engine); - void resolveProtoGetter(Identifier *name, const Heap::Object *proto); + void resolveProtoGetter(Identifier name, const Heap::Object *proto); static ReturnedValue getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object); @@ -126,6 +138,7 @@ struct Lookup { static ReturnedValue getterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterProtoAccessor(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object); + static ReturnedValue getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object); @@ -144,6 +157,17 @@ struct Lookup { static bool setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool arrayLengthSetter(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + + void markObjects(MarkStack *stack) { + if (markDef.h1 && !(reinterpret_cast<quintptr>(markDef.h1) & 1)) + markDef.h1->mark(stack); + if (markDef.h2 && !(reinterpret_cast<quintptr>(markDef.h2) & 1)) + markDef.h2->mark(stack); + } + + void clear() { + memset(&markDef, 0, sizeof(markDef)); + } }; Q_STATIC_ASSERT(std::is_standard_layout<Lookup>::value); diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp index b50e5f0355..58f5b0051f 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -55,22 +55,30 @@ const VTable Managed::static_vtbl = Managed::IsFunctionObject, Managed::IsErrorObject, Managed::IsArrayData, - 0, + Managed::IsStringOrSymbol, Managed::MyType, + { 0, 0, 0, 0 }, "Managed", nullptr, nullptr /*markObjects*/, isEqualTo }; +DEFINE_MANAGED_VTABLE(InternalClass); + QString Managed::className() const { const char *s = nullptr; - switch (Type(d()->vtable()->type)) { + switch (Type(vtable()->type)) { case Type_Invalid: - case Type_String: return QString(); + case Type_String: + s = "String"; + break; + case Type_Symbol: + s = "Symbol"; + break; case Type_Object: s = "Object"; break; @@ -80,6 +88,9 @@ QString Managed::className() const case Type_FunctionObject: s = "Function"; break; + case Type_GeneratorObject: + s = "Generator"; + break; case Type_BooleanObject: s = "Boolean"; break; @@ -89,6 +100,9 @@ QString Managed::className() const case Type_StringObject: s = "String"; break; + case Type_SymbolObject: + s = "Symbol"; + break; case Type_DateObject: s = "Date"; break; @@ -96,7 +110,7 @@ QString Managed::className() const s = "RegExp"; break; case Type_ErrorObject: - s = ErrorObject::className(static_cast<Heap::ErrorObject *>(d())->errorType); + s = "Error"; break; case Type_ArgumentsObject: s = "Arguments"; @@ -111,8 +125,23 @@ QString Managed::className() const case Type_ExecutionContext: s = "__ExecutionContext"; break; - case Type_ForeachIteratorObject: - s = "__ForeachIterator"; + case Type_MapIteratorObject: + s = "Map Iterator"; + break; + case Type_SetIteratorObject: + s = "Set Iterator"; + break; + case Type_ArrayIteratorObject: + s = "Array Iterator"; + break; + case Type_StringIteratorObject: + s = "String Iterator"; + break; + case Type_ForInIterator: + s = "__ForIn Iterator"; + break; + case Type_InternalClass: + s = "__InternalClass"; break; case Type_RegExp: s = "__RegExp"; diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index 092c61b81c..f16bf3dc58 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -92,14 +92,14 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} QV4::Heap::DataClass *dptr = d_unchecked(); \ dptr->_checkIsInitialized(); \ return dptr; \ - } \ - Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value); + } #define V4_MANAGED(DataClass, superClass) \ private: \ DataClass() Q_DECL_EQ_DELETE; \ Q_DISABLE_COPY(DataClass) \ - V4_MANAGED_ITSELF(DataClass, superClass) + V4_MANAGED_ITSELF(DataClass, superClass) \ + Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value); #define Q_MANAGED_TYPE(type) \ public: \ @@ -140,8 +140,9 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} classname::IsFunctionObject, \ classname::IsErrorObject, \ classname::IsArrayData, \ - 0, \ + classname::IsStringOrSymbol, \ classname::MyType, \ + { 0, 0, 0, 0 }, \ #classname, \ Q_VTABLE_FUNCTION(classname, destroy), \ classname::Data::markObjects, \ @@ -154,8 +155,8 @@ const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF #define V4_INTERNALCLASS(c) \ - static QV4::InternalClass *defaultInternalClass(QV4::EngineBase *e) \ - { return e->internalClasses[QV4::EngineBase::Class_##c]; } + static Heap::InternalClass *defaultInternalClass(QV4::EngineBase *e) \ + { return e->internalClasses(QV4::EngineBase::Class_##c); } struct Q_QML_PRIVATE_EXPORT Managed : Value { @@ -163,6 +164,7 @@ struct Q_QML_PRIVATE_EXPORT Managed : Value enum { IsExecutionContext = false, IsString = false, + IsStringOrSymbol = false, IsObject = false, IsFunctionObject = false, IsErrorObject = false, @@ -180,11 +182,14 @@ public: Type_Invalid, Type_String, Type_Object, + Type_Symbol, Type_ArrayObject, Type_FunctionObject, + Type_GeneratorObject, Type_BooleanObject, Type_NumberObject, Type_StringObject, + Type_SymbolObject, Type_DateObject, Type_RegExpObject, Type_ErrorObject, @@ -193,25 +198,33 @@ public: Type_MathObject, Type_ExecutionContext, - Type_ForeachIteratorObject, + Type_InternalClass, + Type_SetIteratorObject, + Type_MapIteratorObject, + Type_ArrayIteratorObject, + Type_StringIteratorObject, + Type_ForInIterator, Type_RegExp, Type_QmlSequence }; Q_MANAGED_TYPE(Invalid) - InternalClass *internalClass() const { return d()->internalClass; } + Heap::InternalClass *internalClass() const { return d()->internalClass; } + const VTable *vtable() const { return d()->internalClass->vtable; } inline ExecutionEngine *engine() const { return internalClass()->engine; } - bool isListType() const { return d()->vtable()->type == Type_QmlSequence; } + bool isListType() const { return d()->internalClass->vtable->type == Type_QmlSequence; } + bool isArrayLike() const { return isArrayObject() || isListType(); } - bool isArrayObject() const { return d()->vtable()->type == Type_ArrayObject; } - bool isStringObject() const { return d()->vtable()->type == Type_StringObject; } + bool isArrayObject() const { return d()->internalClass->vtable->type == Type_ArrayObject; } + bool isStringObject() const { return d()->internalClass->vtable->type == Type_StringObject; } + bool isSymbolObject() const { return d()->internalClass->vtable->type == Type_SymbolObject; } QString className() const; bool isEqualTo(const Managed *other) const - { return d()->vtable()->isEqualTo(const_cast<Managed *>(this), const_cast<Managed *>(other)); } + { return d()->internalClass->vtable->isEqualTo(const_cast<Managed *>(this), const_cast<Managed *>(other)); } static bool isEqualTo(Managed *m, Managed *other); @@ -254,6 +267,29 @@ inline const Object *Value::as() const { return objectValue(); } + +struct InternalClass : Managed +{ + V4_MANAGED_ITSELF(InternalClass, Managed) + Q_MANAGED_TYPE(InternalClass) + V4_INTERNALCLASS(Empty) + V4_NEEDS_DESTROY + + Q_REQUIRED_RESULT Heap::InternalClass *changeVTable(const VTable *vt) { + return d()->changeVTable(vt); + } + Q_REQUIRED_RESULT Heap::InternalClass *changePrototype(Heap::Object *proto) { + return d()->changePrototype(proto); + } + Q_REQUIRED_RESULT Heap::InternalClass *addMember(Identifier identifier, PropertyAttributes data, uint *index = 0) { + return d()->addMember(identifier, data, index); + } + + void operator =(Heap::InternalClass *ic) { + Value::operator=(ic); + } +}; + } diff --git a/src/qml/jsruntime/qv4mapiterator.cpp b/src/qml/jsruntime/qv4mapiterator.cpp new file mode 100644 index 0000000000..74b0dda791 --- /dev/null +++ b/src/qml/jsruntime/qv4mapiterator.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qv4iterator_p.h> +#include <private/qv4mapiterator_p.h> +#include <private/qv4mapobject_p.h> +#include <private/qv4symbol_p.h> + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(MapIteratorObject); + +void MapIteratorPrototype::init(ExecutionEngine *e) +{ + defineDefaultProperty(QStringLiteral("next"), method_next, 0); + + Scope scope(e); + ScopedString val(scope, e->newString(QLatin1String("Map Iterator"))); + defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val); +} + +ReturnedValue MapIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int) +{ + Scope scope(b); + const MapIteratorObject *thisObject = that->as<MapIteratorObject>(); + if (!thisObject) + return scope.engine->throwTypeError(QLatin1String("Not a Map Iterator instance")); + + Scoped<MapObject> s(scope, thisObject->d()->iteratedMap); + quint32 index = thisObject->d()->mapNextIndex; + IteratorKind itemKind = thisObject->d()->iterationKind; + + if (!s) { + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); + } + + Scoped<ArrayObject> keys(scope, s->d()->mapKeys); + Scoped<ArrayObject> values(scope, s->d()->mapValues); + + while (index < keys->getLength()) { + ScopedValue sk(scope, keys->getIndexed(index)); + ScopedValue sv(scope, values->getIndexed(index)); + index += 1; + thisObject->d()->mapNextIndex = index; + + ScopedValue result(scope); + + if (itemKind == KeyIteratorKind) { + result = sk; + } else if (itemKind == ValueIteratorKind) { + result = sv; + } else { + Q_ASSERT(itemKind == KeyValueIteratorKind); + + result = scope.engine->newArrayObject(); + + Scoped<ArrayObject> resultArray(scope, result); + resultArray->arrayReserve(2); + resultArray->arrayPut(0, sk); + resultArray->arrayPut(1, sv); + resultArray->setArrayLengthUnchecked(2); + } + + return IteratorPrototype::createIterResultObject(scope.engine, result, false); + } + + thisObject->d()->iteratedMap.set(scope.engine, nullptr); + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); +} + + diff --git a/src/qml/jsruntime/qv4mapiterator_p.h b/src/qml/jsruntime/qv4mapiterator_p.h new file mode 100644 index 0000000000..836ba14663 --- /dev/null +++ b/src/qml/jsruntime/qv4mapiterator_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4MAPITERATOR_P_H +#define QV4MAPITERATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" +#include "qv4iterator_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Heap { + +#define MapIteratorObjectMembers(class, Member) \ + Member(class, Pointer, Object *, iteratedMap) \ + Member(class, NoMark, IteratorKind, iterationKind) \ + Member(class, NoMark, quint32, mapNextIndex) + +DECLARE_HEAP_OBJECT(MapIteratorObject, Object) { + DECLARE_MARKOBJECTS(MapIteratorObject); + void init(Object *obj, QV4::ExecutionEngine *engine) + { + Object::init(); + this->iteratedMap.set(engine, obj); + this->mapNextIndex = 0; + } +}; + +} + +struct MapIteratorPrototype : Object +{ + V4_PROTOTYPE(iteratorPrototype) + void init(ExecutionEngine *engine); + + static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); +}; + +struct MapIteratorObject : Object +{ + V4_OBJECT2(MapIteratorObject, Object) + Q_MANAGED_TYPE(MapIteratorObject) + V4_PROTOTYPE(mapIteratorPrototype) + + void init(ExecutionEngine *engine); +}; + + +} + +QT_END_NAMESPACE + +#endif // QV4MAPITERATOR_P_H + + diff --git a/src/qml/jsruntime/qv4mapobject.cpp b/src/qml/jsruntime/qv4mapobject.cpp new file mode 100644 index 0000000000..e3ca75b951 --- /dev/null +++ b/src/qml/jsruntime/qv4mapobject.cpp @@ -0,0 +1,354 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4setobject_p.h" // ### temporary +#include "qv4mapobject_p.h" +#include "qv4mapiterator_p.h" +#include "qv4symbol_p.h" + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(MapCtor); +DEFINE_OBJECT_VTABLE(MapObject); + +void Heap::MapCtor::init(QV4::ExecutionContext *scope) +{ + Heap::FunctionObject::init(scope, QStringLiteral("Map")); +} + +ReturnedValue MapCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +{ + Scope scope(f); + Scoped<MapObject> a(scope, scope.engine->memoryManager->allocate<MapObject>()); + a->d()->mapKeys.set(scope.engine, scope.engine->newArrayObject()); + a->d()->mapValues.set(scope.engine, scope.engine->newArrayObject()); + + if (argc > 0) { + ScopedValue iterable(scope, argv[0]); + + // ### beware, hack alert! + // Object iteration seems broken right now. if we allow any object to + // iterate, it endlessly loops in the Map/prototype tests in test262... + // disable these for now until Object iteration is fixed, just so we can + // test this. + Scoped<MapObject> mapObjectCheck(scope, argv[0]); + Scoped<SetObject> setObjectCheck(scope, argv[0]); + + if (!iterable->isUndefined() && !iterable->isNull() && (mapObjectCheck || setObjectCheck)) { + + + ScopedFunctionObject adder(scope, a->get(ScopedString(scope, scope.engine->newString(QString::fromLatin1("set"))))); + if (!adder) { + return scope.engine->throwTypeError(); + } + ScopedObject iter(scope, Runtime::method_getIterator(scope.engine, iterable, true)); + + CHECK_EXCEPTION(); + if (!iter) { + return a.asReturnedValue(); + } + + Value *nextValue = scope.alloc(1); + ScopedValue done(scope); + forever { + done = Runtime::method_iteratorNext(scope.engine, iter, nextValue); + CHECK_EXCEPTION(); + if (done->toBoolean()) { + return a.asReturnedValue(); + } + + adder->call(a, nextValue, 1); + if (scope.engine->hasException) { + ScopedValue falsey(scope, Encode(false)); + return Runtime::method_iteratorClose(scope.engine, iter, falsey); + } + } + } + } + return a.asReturnedValue(); +} + +ReturnedValue MapCtor::call(const FunctionObject *f, const Value *, const Value *, int) +{ + Scope scope(f); + return scope.engine->throwTypeError(QString::fromLatin1("Map requires new")); +} + +void MapPrototype::init(ExecutionEngine *engine, Object *ctor) +{ + Scope scope(engine); + ScopedObject o(scope); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + ctor->addSymbolSpecies(); + defineDefaultProperty(engine->id_constructor(), (o = ctor)); + + defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); + defineDefaultProperty(QStringLiteral("delete"), method_delete, 1); + defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1); + defineDefaultProperty(QStringLiteral("get"), method_get, 1); + defineDefaultProperty(QStringLiteral("has"), method_has, 1); + defineDefaultProperty(QStringLiteral("keys"), method_keys, 0); + defineDefaultProperty(QStringLiteral("set"), method_set, 0); + defineAccessorProperty(QStringLiteral("size"), method_get_size, nullptr); + defineDefaultProperty(QStringLiteral("values"), method_values, 0); + + // Per the spec, the value for entries/@@iterator is the same + ScopedString valString(scope, scope.engine->newIdentifier(QStringLiteral("entries"))); + ScopedFunctionObject entriesFn(scope, FunctionObject::createBuiltinFunction(engine, valString, MapPrototype::method_entries, 0)); + defineDefaultProperty(QStringLiteral("entries"), entriesFn); + defineDefaultProperty(engine->symbol_iterator(), entriesFn); + + ScopedString val(scope, engine->newString(QLatin1String("Map"))); + defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), val); +} + +ReturnedValue MapPrototype::method_clear(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + that->d()->mapKeys.set(scope.engine, scope.engine->newArrayObject()); + that->d()->mapValues.set(scope.engine, scope.engine->newArrayObject()); + return Encode::undefined(); +} + +// delete value +ReturnedValue MapPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<ArrayObject> keys(scope, that->d()->mapKeys); + qint64 len = keys->getLength(); + + bool found = false; + int idx = 0; + ScopedValue sk(scope); + + for (; idx < len; ++idx) { + sk = keys->getIndexed(idx); + if (sk->sameValueZero(argv[0])) { + found = true; + break; + } + } + + if (found == true) { + Scoped<ArrayObject> values(scope, that->d()->mapValues); + Scoped<ArrayObject> newKeys(scope, scope.engine->newArrayObject()); + Scoped<ArrayObject> newValues(scope, scope.engine->newArrayObject()); + for (int j = 0, newIdx = 0; j < len; ++j, newIdx++) { + if (j == idx) { + newIdx--; // skip the entry + continue; + } + newKeys->putIndexed(newIdx, ScopedValue(scope, keys->getIndexed(j))); + newValues->putIndexed(newIdx, ScopedValue(scope, values->getIndexed(j))); + } + + that->d()->mapKeys.set(scope.engine, newKeys->d()); + that->d()->mapValues.set(scope.engine, newValues->d()); + return Encode(true); + } else { + return Encode(false); + } +} + +ReturnedValue MapPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(that)); + ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind; + return ao->asReturnedValue(); +} + +ReturnedValue MapPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + ScopedFunctionObject callbackfn(scope, argv[0]); + if (!callbackfn) + return scope.engine->throwTypeError(); + + ScopedValue thisArg(scope, Primitive::undefinedValue()); + if (argc > 1) + thisArg = ScopedValue(scope, argv[1]); + + Scoped<ArrayObject> keys(scope, that->d()->mapKeys); + Scoped<ArrayObject> values(scope, that->d()->mapKeys); + qint64 len = keys->getLength(); + + Value *arguments = scope.alloc(3); + ScopedValue sk(scope); + ScopedValue sv(scope); + for (int i = 0; i < len; ++i) { + sk = keys->getIndexed(i); + sv = values->getIndexed(i); + + arguments[0] = sv; + arguments[1] = sk; + arguments[2] = that; + callbackfn->call(thisArg, arguments, 3); + CHECK_EXCEPTION(); + } + return Encode::undefined(); +} + +ReturnedValue MapPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<ArrayObject> keys(scope, that->d()->mapKeys); + ScopedValue sk(scope); + qint64 len = keys->getLength(); + + for (int i = 0; i < len; ++i) { + sk = keys->getIndexed(i); + if (sk->sameValueZero(argv[0])) { + Scoped<ArrayObject> values(scope, that->d()->mapValues); + return values->getIndexed(i); + } + } + + return Encode::undefined(); +} + +ReturnedValue MapPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<ArrayObject> keys(scope, that->d()->mapKeys); + ScopedValue sk(scope); + qint64 len = keys->getLength(); + + for (int i = 0; i < len; ++i) { + sk = keys->getIndexed(i); + if (sk->sameValueZero(argv[0])) + return Encode(true); + } + + return Encode(false); +} + +ReturnedValue MapPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(that)); + ao->d()->iterationKind = IteratorKind::KeyIteratorKind; + return ao->asReturnedValue(); +} + +ReturnedValue MapPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<ArrayObject> keys(scope, that->d()->mapKeys); + Scoped<ArrayObject> values(scope, that->d()->mapValues); + ScopedValue sk(scope, argv[1]); + qint64 len = keys->getLength(); + + for (int i = 0; i < len; ++i) { + sk = keys->getIndexed(i); + if (sk->sameValueZero(argv[0])) { + values->putIndexed(len, argv[1]); + return that.asReturnedValue(); + } + } + + sk = argv[0]; + if (sk->isDouble()) { + if (sk->doubleValue() == 0 && std::signbit(sk->doubleValue())) + sk = Primitive::fromDouble(+0); + } + + keys->putIndexed(len, sk); + values->putIndexed(len, argv[1]); + return that.asReturnedValue(); +} + +ReturnedValue MapPrototype::method_get_size(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<ArrayObject> keys(scope, that->d()->mapKeys); + qint64 len = keys->getLength(); + return Encode((uint)len); +} + +ReturnedValue MapPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<MapObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(that)); + ao->d()->iterationKind = IteratorKind::ValueIteratorKind; + return ao->asReturnedValue(); +} + + diff --git a/src/qml/jsruntime/qv4mapobject_p.h b/src/qml/jsruntime/qv4mapobject_p.h new file mode 100644 index 0000000000..9c64e25c3d --- /dev/null +++ b/src/qml/jsruntime/qv4mapobject_p.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4MAPOBJECT_P_H +#define QV4MAPOBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" +#include "qv4objectproto_p.h" +#include "qv4functionobject_p.h" +#include "qv4string_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Heap { + +struct MapCtor : FunctionObject { + void init(QV4::ExecutionContext *scope); +}; + +#define MapObjectMembers(class, Member) \ + Member(class, Pointer, ArrayObject *, mapKeys) \ + Member(class, Pointer, ArrayObject *, mapValues) + +DECLARE_HEAP_OBJECT(MapObject, Object) { + DECLARE_MARKOBJECTS(MapObject); + void init() { Object::init(); } +}; + +} + +struct MapCtor: FunctionObject +{ + V4_OBJECT2(MapCtor, FunctionObject) + + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct MapObject : Object +{ + V4_OBJECT2(MapObject, Object) + V4_PROTOTYPE(mapPrototype) +}; + +struct MapPrototype : Object +{ + void init(ExecutionEngine *engine, Object *ctor); + + static ReturnedValue method_clear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_delete(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_size(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +}; + + +} // namespace QV4 + + +QT_END_NAMESPACE + +#endif // QV4MAPOBJECT_P_H + diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp index 0c18d908de..652f6c603e 100644 --- a/src/qml/jsruntime/qv4mathobject.cpp +++ b/src/qml/jsruntime/qv4mathobject.cpp @@ -39,6 +39,7 @@ #include "qv4mathobject_p.h" #include "qv4objectproto_p.h" +#include "qv4symbol_p.h" #include <QtCore/qdatetime.h> #include <QtCore/qmath.h> @@ -70,14 +71,27 @@ void Heap::MathObject::init() m->defineDefaultProperty(QStringLiteral("abs"), QV4::MathObject::method_abs, 1); m->defineDefaultProperty(QStringLiteral("acos"), QV4::MathObject::method_acos, 1); - m->defineDefaultProperty(QStringLiteral("asin"), QV4::MathObject::method_asin, 0); + m->defineDefaultProperty(QStringLiteral("acosh"), QV4::MathObject::method_acosh, 1); + m->defineDefaultProperty(QStringLiteral("asin"), QV4::MathObject::method_asin, 1); + m->defineDefaultProperty(QStringLiteral("asinh"), QV4::MathObject::method_asinh, 1); m->defineDefaultProperty(QStringLiteral("atan"), QV4::MathObject::method_atan, 1); + m->defineDefaultProperty(QStringLiteral("atanh"), QV4::MathObject::method_atanh, 1); m->defineDefaultProperty(QStringLiteral("atan2"), QV4::MathObject::method_atan2, 2); + m->defineDefaultProperty(QStringLiteral("cbrt"), QV4::MathObject::method_cbrt, 1); m->defineDefaultProperty(QStringLiteral("ceil"), QV4::MathObject::method_ceil, 1); + m->defineDefaultProperty(QStringLiteral("clz32"), QV4::MathObject::method_clz32, 1); m->defineDefaultProperty(QStringLiteral("cos"), QV4::MathObject::method_cos, 1); + m->defineDefaultProperty(QStringLiteral("cosh"), QV4::MathObject::method_cosh, 1); m->defineDefaultProperty(QStringLiteral("exp"), QV4::MathObject::method_exp, 1); + m->defineDefaultProperty(QStringLiteral("expm1"), QV4::MathObject::method_expm1, 1); m->defineDefaultProperty(QStringLiteral("floor"), QV4::MathObject::method_floor, 1); + m->defineDefaultProperty(QStringLiteral("fround"), QV4::MathObject::method_fround, 1); + m->defineDefaultProperty(QStringLiteral("hypot"), QV4::MathObject::method_hypot, 2); + m->defineDefaultProperty(QStringLiteral("imul"), QV4::MathObject::method_imul, 2); m->defineDefaultProperty(QStringLiteral("log"), QV4::MathObject::method_log, 1); + m->defineDefaultProperty(QStringLiteral("log10"), QV4::MathObject::method_log10, 1); + m->defineDefaultProperty(QStringLiteral("log1p"), QV4::MathObject::method_log1p, 1); + m->defineDefaultProperty(QStringLiteral("log2"), QV4::MathObject::method_log2, 1); m->defineDefaultProperty(QStringLiteral("max"), QV4::MathObject::method_max, 2); m->defineDefaultProperty(QStringLiteral("min"), QV4::MathObject::method_min, 2); m->defineDefaultProperty(QStringLiteral("pow"), QV4::MathObject::method_pow, 2); @@ -85,8 +99,14 @@ void Heap::MathObject::init() m->defineDefaultProperty(QStringLiteral("round"), QV4::MathObject::method_round, 1); m->defineDefaultProperty(QStringLiteral("sign"), QV4::MathObject::method_sign, 1); m->defineDefaultProperty(QStringLiteral("sin"), QV4::MathObject::method_sin, 1); + m->defineDefaultProperty(QStringLiteral("sinh"), QV4::MathObject::method_sinh, 1); m->defineDefaultProperty(QStringLiteral("sqrt"), QV4::MathObject::method_sqrt, 1); m->defineDefaultProperty(QStringLiteral("tan"), QV4::MathObject::method_tan, 1); + m->defineDefaultProperty(QStringLiteral("tanh"), QV4::MathObject::method_tanh, 1); + m->defineDefaultProperty(QStringLiteral("trunc"), QV4::MathObject::method_trunc, 1); + + ScopedString name(scope, scope.engine->newString(QStringLiteral("Math"))); + m->defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name); } static Q_ALWAYS_INLINE double copySign(double x, double y) @@ -120,6 +140,19 @@ ReturnedValue MathObject::method_acos(const FunctionObject *, const Value *, con RETURN_RESULT(Encode(std::acos(v))); } +ReturnedValue MathObject::method_acosh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : 2; + if (v < 1) + RETURN_RESULT(Encode(qt_qnan())); + +#ifdef Q_OS_ANDROID // incomplete std :-( + RETURN_RESULT(Encode(std::log(v +std::sqrt(v + 1) * std::sqrt(v - 1)))); +#else + RETURN_RESULT(Encode(std::acosh(v))); +#endif +} + ReturnedValue MathObject::method_asin(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : 2; @@ -129,6 +162,19 @@ ReturnedValue MathObject::method_asin(const FunctionObject *, const Value *, con RETURN_RESULT(Encode(std::asin(v))); } +ReturnedValue MathObject::method_asinh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : 2; + if (v == 0.0) + RETURN_RESULT(Encode(v)); + +#ifdef Q_OS_ANDROID // incomplete std :-( + RETURN_RESULT(Encode(std::log(v +std::sqrt(1 + v * v)))); +#else + RETURN_RESULT(Encode(std::asinh(v))); +#endif +} + ReturnedValue MathObject::method_atan(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); @@ -138,6 +184,25 @@ ReturnedValue MathObject::method_atan(const FunctionObject *, const Value *, con RETURN_RESULT(Encode(std::atan(v))); } +ReturnedValue MathObject::method_atanh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (v == 0.0) + RETURN_RESULT(Encode(v)); + +#ifdef Q_OS_ANDROID // incomplete std :-( + if (-1 < v && v < 1) + RETURN_RESULT(Encode(0.5 * (std::log(v + 1) - std::log(v - 1)))); + + if (v > 1 || v < -1) + RETURN_RESULT(Encode(qt_qnan())); + + RETURN_RESULT(Encode(copySign(qt_inf(), v))); +#else + RETURN_RESULT(Encode(std::atanh(v))); +#endif +} + ReturnedValue MathObject::method_atan2(const FunctionObject *, const Value *, const Value *argv, int argc) { double v1 = argc ? argv[0].toNumber() : qt_qnan(); @@ -156,6 +221,16 @@ ReturnedValue MathObject::method_atan2(const FunctionObject *, const Value *, co RETURN_RESULT(Encode(std::atan2(v1, v2))); } +ReturnedValue MathObject::method_cbrt(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); +#ifdef Q_OS_ANDROID // incomplete std :-( + RETURN_RESULT(Encode(copySign(std::exp(std::log(std::abs(v)) / 3), v))); +#else + RETURN_RESULT(Encode(std::cbrt(v))); // cube root +#endif +} + ReturnedValue MathObject::method_ceil(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); @@ -165,12 +240,24 @@ ReturnedValue MathObject::method_ceil(const FunctionObject *, const Value *, con RETURN_RESULT(Encode(std::ceil(v))); } +ReturnedValue MathObject::method_clz32(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + quint32 v = argc ? argv[0].toUInt32() : 0; + RETURN_RESULT(Encode(qint32(qCountLeadingZeroBits(v)))); +} + ReturnedValue MathObject::method_cos(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); RETURN_RESULT(Encode(std::cos(v))); } +ReturnedValue MathObject::method_cosh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + RETURN_RESULT(Encode(std::cosh(v))); +} + ReturnedValue MathObject::method_exp(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); @@ -184,6 +271,25 @@ ReturnedValue MathObject::method_exp(const FunctionObject *, const Value *, cons } } +ReturnedValue MathObject::method_expm1(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (std::isnan(v) || qIsNull(v)) { + RETURN_RESULT(Encode(v)); + } else if (qt_is_inf(v)) { + if (copySign(1.0, v) == -1.0) + RETURN_RESULT(Encode(-1.0)); + else + RETURN_RESULT(Encode(qt_inf())); + } else { +#ifdef Q_OS_ANDROID // incomplete std :-( + RETURN_RESULT(Encode(std::exp(v) - 1)); +#else + RETURN_RESULT(Encode(std::expm1(v))); +#endif + } +} + ReturnedValue MathObject::method_floor(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); @@ -192,6 +298,53 @@ ReturnedValue MathObject::method_floor(const FunctionObject *, const Value *, co RETURN_RESULT(result); } +ReturnedValue MathObject::method_fround(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (std::isnan(v) || qt_is_inf(v) || qIsNull(v)) + RETURN_RESULT(Encode(v)); + else // convert to 32-bit float using roundTiesToEven, then convert back to 64-bit double + RETURN_RESULT(Encode(double(float(v)))); +} + +ReturnedValue MathObject::method_hypot(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + // ES6 Math.hypot(v1, ..., vn) -> sqrt(sum(vi**2)) but "should take care to + // avoid the loss of precision from overflows and underflows" (as std::hypot does). + double v = argc ? argv[0].toNumber() : 0; + // Spec mandates +0 on no args; and says nothing about what to do if toNumber() signals ... +#ifdef Q_OS_ANDROID // incomplete std :-( + bool big = qt_is_inf(v), bad = std::isnan(v); + v *= v; + for (int i = 1; !big && i < argc; i++) { + double u = argv[i].toNumber(); + if (qt_is_inf(u)) + big = true; + if (std::isnan(u)) + bad = true; + v += u * u; + } + if (big) + RETURN_RESULT(Encode(qt_inf())); + if (bad) + RETURN_RESULT(Encode(qt_qnan())); + // Should actually check for {und,ov}erflow, but too fiddly ! + RETURN_RESULT(Primitive::fromDouble(sqrt(v))); +#else + for (int i = 1; i < argc; i++) + v = std::hypot(v, argv[i].toNumber()); +#endif + RETURN_RESULT(Primitive::fromDouble(v)); +} + +ReturnedValue MathObject::method_imul(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + quint32 a = argc ? argv[0].toUInt32() : 0; + quint32 b = argc > 0 ? argv[1].toUInt32() : 0; + qint32 product = a * b; + RETURN_RESULT(Encode(product)); +} + ReturnedValue MathObject::method_log(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); @@ -201,6 +354,43 @@ ReturnedValue MathObject::method_log(const FunctionObject *, const Value *, cons RETURN_RESULT(Encode(std::log(v))); } +ReturnedValue MathObject::method_log10(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (v < 0) + RETURN_RESULT(Encode(qt_qnan())); + else + RETURN_RESULT(Encode(std::log10(v))); +} + +ReturnedValue MathObject::method_log1p(const FunctionObject *, const Value *, const Value *argv, int argc) +{ +#if !defined(__ANDROID__) + using std::log1p; +#endif + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (v < -1) + RETURN_RESULT(Encode(qt_qnan())); + else + RETURN_RESULT(Encode(log1p(v))); +} + +ReturnedValue MathObject::method_log2(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (v < 0) { + RETURN_RESULT(Encode(qt_qnan())); + } else { +#ifdef Q_OS_ANDROID // incomplete std :-( + // Android ndk r10e doesn't have std::log2, so fall back. + const double ln2 = std::log(2.0); + RETURN_RESULT(Encode(std::log(v) / ln2)); +#else + RETURN_RESULT(Encode(std::log2(v))); +#endif + } +} + ReturnedValue MathObject::method_max(const FunctionObject *, const Value *, const Value *argv, int argc) { double mx = -qt_inf(); @@ -283,8 +473,11 @@ ReturnedValue MathObject::method_random(const FunctionObject *, const Value *, c ReturnedValue MathObject::method_round(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); - v = copySign(std::floor(v + 0.5), v); - RETURN_RESULT(Encode(v)); + if (std::isnan(v) || qt_is_inf(v) || qIsNull(v)) + RETURN_RESULT(Encode(v)); + + v = copySign(std::floor(v + 0.5), v); + RETURN_RESULT(Encode(v)); } ReturnedValue MathObject::method_sign(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -303,7 +496,19 @@ ReturnedValue MathObject::method_sign(const FunctionObject *, const Value *, con ReturnedValue MathObject::method_sin(const FunctionObject *, const Value *, const Value *argv, int argc) { double v = argc ? argv[0].toNumber() : qt_qnan(); - RETURN_RESULT(Encode(std::sin(v))); + if (v == 0.0) + RETURN_RESULT(Encode(v)); + else + RETURN_RESULT(Encode(std::sin(v))); +} + +ReturnedValue MathObject::method_sinh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (v == 0.0) + RETURN_RESULT(Encode(v)); + else + RETURN_RESULT(Encode(std::sinh(v))); } ReturnedValue MathObject::method_sqrt(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -321,3 +526,25 @@ ReturnedValue MathObject::method_tan(const FunctionObject *, const Value *, cons RETURN_RESULT(Encode(std::tan(v))); } +ReturnedValue MathObject::method_tanh(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); + if (v == 0.0) + RETURN_RESULT(Encode(v)); + else + RETURN_RESULT(Encode(std::tanh(v))); +} + +ReturnedValue MathObject::method_trunc(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + double v = argc ? argv[0].toNumber() : qt_qnan(); +#ifdef Q_OS_ANDROID // incomplete std :-( + if (std::isnan(v) || qt_is_inf(v) || qIsNull(v)) + RETURN_RESULT(Encode(v)); + // Nearest integer not greater in magnitude: + quint64 whole = std::abs(v); + RETURN_RESULT(Encode(copySign(whole, v))); +#else + RETURN_RESULT(Encode(std::trunc(v))); +#endif +} diff --git a/src/qml/jsruntime/qv4mathobject_p.h b/src/qml/jsruntime/qv4mathobject_p.h index 0bf5da9404..2658e25438 100644 --- a/src/qml/jsruntime/qv4mathobject_p.h +++ b/src/qml/jsruntime/qv4mathobject_p.h @@ -71,14 +71,27 @@ struct MathObject: Object static ReturnedValue method_abs(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_acos(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_acosh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_asin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_asinh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_atan(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_atanh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_atan2(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_cbrt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_ceil(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_clz32(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_cos(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_cosh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_exp(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_expm1(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_floor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_fround(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_hypot(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_imul(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_log(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_log10(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_log1p(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_log2(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_max(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_min(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_pow(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); @@ -86,8 +99,11 @@ struct MathObject: Object static ReturnedValue method_round(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_sign(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_sin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_sinh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_sqrt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_tan(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_tanh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_trunc(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; } diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h index ac9671254d..186083b83a 100644 --- a/src/qml/jsruntime/qv4memberdata_p.h +++ b/src/qml/jsruntime/qv4memberdata_p.h @@ -74,18 +74,6 @@ struct MemberData : Managed V4_MANAGED(MemberData, Managed) V4_INTERNALCLASS(MemberData) - struct Index { - Heap::Base *base; - Value *slot; - - void set(EngineBase *e, Value newVal) { - WriteBarrier::write(e, base, slot->data_ptr(), newVal.asReturnedValue()); - } - const Value *operator->() const { return slot; } - const Value &operator*() const { return *slot; } - bool isNull() const { return !slot; } - }; - const Value &operator[] (uint idx) const { return d()->values[idx]; } const Value *data() const { return d()->values.data(); } void set(EngineBase *e, uint index, Value v) { d()->values.set(e, index, v); } diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 0c6cde84ad..79a63d1ee6 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -50,6 +50,7 @@ #include "qv4string_p.h" #include "qv4identifiertable_p.h" #include "qv4jscall_p.h" +#include "qv4symbol_p.h" #include <stdint.h> @@ -57,9 +58,9 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(Object); -void Object::setInternalClass(InternalClass *ic) +void Object::setInternalClass(Heap::InternalClass *ic) { - d()->internalClass = ic; + d()->internalClass.set(engine(), ic); if (ic->isUsedAsProto) ic->updateProtoUsage(d()); Q_ASSERT(ic && ic->vtable); @@ -89,7 +90,7 @@ void Object::setProperty(uint index, const Property *p) void Heap::Object::setUsedAsProto() { - internalClass = internalClass->asProtoClass(); + internalClass.set(internalClass->engine, internalClass->asProtoClass()); } bool Object::setPrototype(Object *proto) @@ -121,7 +122,7 @@ ReturnedValue Object::getValue(const Value &thisObject, const Value &v, Property bool Object::putValue(uint memberIndex, const Value &value) { - QV4::InternalClass *ic = internalClass(); + Heap::InternalClass *ic = internalClass(); if (ic->engine->hasException) return false; @@ -148,37 +149,34 @@ bool Object::putValue(uint memberIndex, const Value &value) return true; } -void Object::defineDefaultProperty(const QString &name, const Value &value) +void Object::defineDefaultProperty(const QString &name, const Value &value, PropertyAttributes attributes) { ExecutionEngine *e = engine(); Scope scope(e); ScopedString s(scope, e->newIdentifier(name)); - defineDefaultProperty(s, value); + defineDefaultProperty(s, value, attributes); } -void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount) +void Object::defineDefaultProperty(const QString &name, jsCallFunction code, + int argumentCount, PropertyAttributes attributes) { ExecutionEngine *e = engine(); Scope scope(e); ScopedString s(scope, e->newIdentifier(name)); - ExecutionContext *global = e->rootContext(); - ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(global, s, code)); - function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); - defineDefaultProperty(s, function); + ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(e, s, code, argumentCount)); + defineDefaultProperty(s, function, attributes); } -void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount) +void Object::defineDefaultProperty(StringOrSymbol *nameOrSymbol, jsCallFunction code, + int argumentCount, PropertyAttributes attributes) { ExecutionEngine *e = engine(); Scope scope(e); - ExecutionContext *global = e->rootContext(); - ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(global, name, code)); - function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); - defineDefaultProperty(name, function); + ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(e, nameOrSymbol, code, argumentCount)); + defineDefaultProperty(nameOrSymbol, function, attributes); } -void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int), - ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int)) +void Object::defineAccessorProperty(const QString &name, jsCallFunction getter, jsCallFunction setter) { ExecutionEngine *e = engine(); Scope scope(e); @@ -186,16 +184,27 @@ void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter) defineAccessorProperty(s, getter, setter); } -void Object::defineAccessorProperty(String *name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int), - ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int)) +void Object::defineAccessorProperty(StringOrSymbol *name, jsCallFunction getter, jsCallFunction setter) { ExecutionEngine *v4 = engine(); QV4::Scope scope(v4); ScopedProperty p(scope); - ExecutionContext *global = v4->rootContext(); - p->setGetter(ScopedFunctionObject(scope, (getter ? FunctionObject::createBuiltinFunction(global, name, getter) : nullptr))); - p->setSetter(ScopedFunctionObject(scope, (setter ? FunctionObject::createBuiltinFunction(global, name, setter) : nullptr))); - insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); + QString n = name->toQString(); + if (n.at(0) == QLatin1Char('@')) + n = QChar::fromLatin1('[') + n.midRef(1) + QChar::fromLatin1(']'); + if (getter) { + ScopedString getName(scope, v4->newString(QString::fromLatin1("get ") + n)); + p->setGetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(v4, getName, getter, 0))); + } else { + p->setGetter(nullptr); + } + if (setter) { + ScopedString setName(scope, v4->newString(QString::fromLatin1("set ") + n)); + p->setSetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(v4, setName, setter, 0))); + } else { + p->setSetter(nullptr); + } + insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable); } @@ -221,13 +230,23 @@ void Object::defineReadonlyConfigurableProperty(const QString &name, const Value defineReadonlyConfigurableProperty(s, value); } -void Object::defineReadonlyConfigurableProperty(String *name, const Value &value) +void Object::defineReadonlyConfigurableProperty(StringOrSymbol *name, const Value &value) { insertMember(name, value, Attr_ReadOnly_ButConfigurable); } +void Object::addSymbolSpecies() +{ + Scope scope(engine()); + ScopedProperty p(scope); + p->setGetter(scope.engine->getSymbolSpecies()); + p->setSetter(nullptr); + insertMember(scope.engine->symbol_species(), p, QV4::Attr_Accessor|QV4::Attr_NotWritable|QV4::Attr_NotEnumerable); +} + void Heap::Object::markObjects(Heap::Base *b, MarkStack *stack) { + Base::markObjects(b, stack); Object *o = static_cast<Object *>(b); if (o->memberData) o->memberData->mark(stack); @@ -242,10 +261,11 @@ void Heap::Object::markObjects(Heap::Base *b, MarkStack *stack) } } -void Object::insertMember(String *s, const Property *p, PropertyAttributes attributes) +void Object::insertMember(StringOrSymbol *s, const Property *p, PropertyAttributes attributes) { uint idx; - InternalClass::addMember(this, s, attributes, &idx); + s->makeIdentifier(); + Heap::InternalClass::addMember(this, s->identifier(), attributes, &idx); if (attributes.isAccessor()) { setProperty(idx + GetterOffset, p->value); @@ -256,14 +276,14 @@ void Object::insertMember(String *s, const Property *p, PropertyAttributes attri } // Section 8.12.1 -void Object::getOwnProperty(String *name, PropertyAttributes *attrs, Property *p) +void Object::getOwnProperty(StringOrSymbol *name, PropertyAttributes *attrs, Property *p) { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) return getOwnProperty(idx, attrs, p); name->makeIdentifier(); - Identifier *id = name->identifier(); + Identifier id = name->identifier(); uint member = internalClass()->find(id); if (member < UINT_MAX) { @@ -300,12 +320,12 @@ void Object::getOwnProperty(uint index, PropertyAttributes *attrs, Property *p) } // Section 8.12.2 -MemberData::Index Object::getValueOrSetter(String *name, PropertyAttributes *attrs) +PropertyIndex Object::getValueOrSetter(StringOrSymbol *name, PropertyAttributes *attrs) { Q_ASSERT(name->asArrayIndex() == UINT_MAX); name->makeIdentifier(); - Identifier *id = name->identifier(); + Identifier id = name->identifier(); Heap::Object *o = d(); while (o) { @@ -321,7 +341,7 @@ MemberData::Index Object::getValueOrSetter(String *name, PropertyAttributes *att return { nullptr, nullptr }; } -ArrayData::Index Object::getValueOrSetter(uint index, PropertyAttributes *attrs) +PropertyIndex Object::getValueOrSetter(uint index, PropertyAttributes *attrs) { Heap::Object *o = d(); while (o) { @@ -329,7 +349,7 @@ ArrayData::Index Object::getValueOrSetter(uint index, PropertyAttributes *attrs) uint idx = o->arrayData->mappedIndex(index); if (idx != UINT_MAX) { *attrs = o->arrayData->attributes(index); - return { o->arrayData , attrs->isAccessor() ? idx + SetterOffset : idx }; + return { o->arrayData , o->arrayData->values.values + (attrs->isAccessor() ? idx + SetterOffset : idx) }; } } if (o->vtable()->type == Type_StringObject) { @@ -346,7 +366,7 @@ ArrayData::Index Object::getValueOrSetter(uint index, PropertyAttributes *attrs) return { nullptr, 0 }; } -bool Object::hasProperty(String *name) const +bool Object::hasProperty(StringOrSymbol *name) const { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) @@ -378,14 +398,14 @@ bool Object::hasProperty(uint index) const return false; } -bool Object::hasOwnProperty(String *name) const +bool Object::hasOwnProperty(StringOrSymbol *name) const { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) return hasOwnProperty(idx); name->makeIdentifier(); - Identifier *id = name->identifier(); + Identifier id = name->identifier(); if (internalClass()->find(id) < UINT_MAX) return true; @@ -418,7 +438,7 @@ ReturnedValue Object::call(const FunctionObject *f, const Value *, const Value * return f->engine()->throwTypeError(); } -ReturnedValue Object::get(const Managed *m, String *name, bool *hasProperty) +ReturnedValue Object::get(const Managed *m, StringOrSymbol *name, bool *hasProperty) { return static_cast<const Object *>(m)->internalGet(name, hasProperty); } @@ -428,7 +448,7 @@ ReturnedValue Object::getIndexed(const Managed *m, uint index, bool *hasProperty return static_cast<const Object *>(m)->internalGetIndexed(index, hasProperty); } -bool Object::put(Managed *m, String *name, const Value &value) +bool Object::put(Managed *m, StringOrSymbol *name, const Value &value) { return static_cast<Object *>(m)->internalPut(name, value); } @@ -438,14 +458,14 @@ bool Object::putIndexed(Managed *m, uint index, const Value &value) return static_cast<Object *>(m)->internalPutIndexed(index, value); } -PropertyAttributes Object::query(const Managed *m, String *name) +PropertyAttributes Object::query(const Managed *m, StringOrSymbol *name) { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) return queryIndexed(m, idx); name->makeIdentifier(); - Identifier *id = name->identifier(); + Identifier id = name->identifier(); const Object *o = static_cast<const Object *>(m); idx = o->internalClass()->find(id); @@ -468,7 +488,7 @@ PropertyAttributes Object::queryIndexed(const Managed *m, uint index) return Attr_Invalid; } -bool Object::deleteProperty(Managed *m, String *name) +bool Object::deleteProperty(Managed *m, StringOrSymbol *name) { return static_cast<Object *>(m)->internalDeleteProperty(name); } @@ -525,9 +545,10 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint * } while (it->memberIndex < o->internalClass()->size) { - Identifier *n = o->internalClass()->nameMap.at(it->memberIndex); - if (!n) { + Identifier n = o->internalClass()->nameMap.at(it->memberIndex); + if (!n.isValid() || !n.asHeapObject()->internalClass->vtable->isString) { // accessor properties have a dummy entry with n == 0 + // symbol entries are supposed to be skipped ++it->memberIndex; continue; } @@ -536,7 +557,7 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint * PropertyAttributes a = o->internalClass()->propertyData[it->memberIndex]; ++it->memberIndex; if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { - name->setM(o->engine()->identifierTable->stringFromIdentifier(n)); + name->setM(n.asHeapObject()); *attrs = a; pd->value = *o->propertyData(idx); if (a.isAccessor()) @@ -549,14 +570,14 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint * } // Section 8.12.3 -ReturnedValue Object::internalGet(String *name, bool *hasProperty) const +ReturnedValue Object::internalGet(StringOrSymbol *name, bool *hasProperty) const { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) return getIndexed(idx, hasProperty); name->makeIdentifier(); - Identifier *id = name->identifier(); + Identifier id = name->identifier(); Heap::Object *o = d(); while (o) { @@ -612,7 +633,7 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const // Section 8.12.5 -bool Object::internalPut(String *name, const Value &value) +bool Object::internalPut(StringOrSymbol *name, const Value &value) { ExecutionEngine *engine = this->engine(); if (engine->hasException) @@ -623,9 +644,9 @@ bool Object::internalPut(String *name, const Value &value) return putIndexed(idx, value); name->makeIdentifier(); - Identifier *id = name->identifier(); + Identifier id = name->identifier(); - MemberData::Index memberIndex{nullptr, nullptr}; + PropertyIndex memberIndex{nullptr, nullptr}; uint member = internalClass()->find(id); PropertyAttributes attrs; if (member < UINT_MAX) { @@ -641,7 +662,7 @@ bool Object::internalPut(String *name, const Value &value) return false; } else if (!attrs.isWritable()) return false; - else if (isArrayObject() && name->equals(engine->id_length())) { + else if (isArrayObject() && id == engine->id_length()->identifier()) { bool ok; uint l = value.asArrayLength(&ok); if (!ok) { @@ -701,7 +722,7 @@ bool Object::internalPutIndexed(uint index, const Value &value) PropertyAttributes attrs; - ArrayData::Index arrayIndex = arrayData() ? arrayData()->getValueOrSetter(index, &attrs) : ArrayData::Index{ nullptr, 0 }; + PropertyIndex arrayIndex = arrayData() ? arrayData()->getValueOrSetter(index, &attrs) : PropertyIndex{ nullptr, 0 }; if (arrayIndex.isNull() && isStringObject()) { if (index < static_cast<StringObject *>(this)->length()) @@ -759,7 +780,7 @@ bool Object::internalPutIndexed(uint index, const Value &value) } // Section 8.12.7 -bool Object::internalDeleteProperty(String *name) +bool Object::internalDeleteProperty(StringOrSymbol *name) { if (internalClass()->engine->hasException) return false; @@ -769,11 +790,12 @@ bool Object::internalDeleteProperty(String *name) return deleteIndexedProperty(idx); name->makeIdentifier(); + Identifier id = name->identifier(); - uint memberIdx = internalClass()->find(name->identifier()); + uint memberIdx = internalClass()->find(id); if (memberIdx != UINT_MAX) { if (internalClass()->propertyData[memberIdx].isConfigurable()) { - InternalClass::removeMember(this, name->identifier()); + Heap::InternalClass::removeMember(this, id); return true; } return false; @@ -796,7 +818,7 @@ bool Object::internalDeleteIndexedProperty(uint index) } // Section 8.12.9 -bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const Property *p, PropertyAttributes attrs) +bool Object::__defineOwnProperty__(ExecutionEngine *engine, StringOrSymbol *name, const Property *p, PropertyAttributes attrs) { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) @@ -807,8 +829,8 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const uint memberIndex; - if (isArrayObject() && name->equals(engine->id_length())) { - Q_ASSERT(Heap::ArrayObject::LengthPropertyIndex == internalClass()->find(engine->id_length())); + if (isArrayObject() && name->identifier() == engine->id_length()->identifier()) { + Q_ASSERT(Heap::ArrayObject::LengthPropertyIndex == internalClass()->find(engine->id_length()->identifier())); ScopedProperty lp(scope); PropertyAttributes cattrs; getProperty(Heap::ArrayObject::LengthPropertyIndex, lp, &cattrs); @@ -829,7 +851,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const } if (attrs.hasWritable() && !attrs.isWritable()) { cattrs.setWritable(false); - InternalClass::changeMember(this, engine->id_length(), cattrs); + Heap::InternalClass::changeMember(this, engine->id_length()->identifier(), cattrs); } if (!succeeded) return false; @@ -898,7 +920,7 @@ bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Prope return __defineOwnProperty__(engine, index, nullptr, p, attrs); } -bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *member, const Property *p, PropertyAttributes attrs) +bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, StringOrSymbol *member, const Property *p, PropertyAttributes attrs) { // clause 5 if (attrs.isEmpty()) @@ -977,7 +999,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * current->merge(cattrs, p, attrs); if (member) { - InternalClass::changeMember(this, member, cattrs); + Heap::InternalClass::changeMember(this, member->identifier(), cattrs); setProperty(index, current); } else { setArrayAttributes(index, cattrs); @@ -1030,11 +1052,11 @@ void Object::copyArrayData(Object *other) setArrayLengthUnchecked(other->getLength()); } -uint Object::getLength(const Managed *m) +qint64 Object::getLength(const Managed *m) { Scope scope(static_cast<const Object *>(m)->engine()); ScopedValue v(scope, static_cast<Object *>(const_cast<Managed *>(m))->get(scope.engine->id_length())); - return v->toUInt32(); + return v->toLength(); } // 'var' is 'V' in 15.3.5.3. @@ -1132,12 +1154,10 @@ void Heap::ArrayObject::init(const QStringList &list) a->setArrayLengthUnchecked(len); } -uint ArrayObject::getLength(const Managed *m) +qint64 ArrayObject::getLength(const Managed *m) { const ArrayObject *a = static_cast<const ArrayObject *>(m); - if (a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->isInteger()) - return a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->integerValue(); - return Primitive::toUInt32(a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->doubleValue()); + return a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->toLength(); } QStringList ArrayObject::toQStringList() const diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 1731ae3c76..5ad67635db 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -64,6 +64,9 @@ QT_BEGIN_NAMESPACE namespace QV4 { +typedef ReturnedValue (*jsCallFunction)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +typedef ReturnedValue (*jsConstructFunction)(const FunctionObject *, const Value *argv, int argc); + namespace Heap { #define ObjectMembers(class, Member) \ @@ -74,6 +77,10 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) { static void markObjects(Heap::Base *base, MarkStack *stack); void init() { Base::init(); } + const VTable *vtable() const { + return internalClass->vtable; + } + const Value *inlinePropertyDataWithOffset(uint indexWithOffset) const { Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < vtable()->inlinePropertyOffset + vtable()->nInlineProperties); return reinterpret_cast<const Value *>(this) + indexWithOffset; @@ -90,15 +97,15 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) { void setInlineProperty(ExecutionEngine *e, uint index, Heap::Base *b) { Q_ASSERT(index < vtable()->nInlineProperties); Value *prop = reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index; - WriteBarrier::write(e, this, prop->data_ptr(), b->asReturnedValue()); + WriteBarrier::write(e, this, prop->data_ptr(), Value::fromHeapObject(b).asReturnedValue()); } - QV4::MemberData::Index writablePropertyData(uint index) { + PropertyIndex writablePropertyData(uint index) { uint nInline = vtable()->nInlineProperties; if (index < nInline) - return { this, reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index}; + return PropertyIndex{ this, reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index}; index -= nInline; - return { memberData, memberData->values.values + index }; + return PropertyIndex{ memberData, memberData->values.values + index }; } const Value *propertyData(uint index) const { @@ -162,15 +169,15 @@ struct ObjectVTable VTable vTable; ReturnedValue (*call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); ReturnedValue (*callAsConstructor)(const FunctionObject *, const Value *argv, int argc); - ReturnedValue (*get)(const Managed *, String *name, bool *hasProperty); + ReturnedValue (*get)(const Managed *, StringOrSymbol *name, bool *hasProperty); ReturnedValue (*getIndexed)(const Managed *, uint index, bool *hasProperty); - bool (*put)(Managed *, String *name, const Value &value); + bool (*put)(Managed *, StringOrSymbol *name, const Value &value); bool (*putIndexed)(Managed *, uint index, const Value &value); - PropertyAttributes (*query)(const Managed *, String *name); + PropertyAttributes (*query)(const Managed *, StringOrSymbol *name); PropertyAttributes (*queryIndexed)(const Managed *, uint index); - bool (*deleteProperty)(Managed *m, String *name); + bool (*deleteProperty)(Managed *m, StringOrSymbol *name); bool (*deleteIndexedProperty)(Managed *m, uint index); - uint (*getLength)(const Managed *m); + qint64 (*getLength)(const Managed *m); void (*advanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); ReturnedValue (*instanceOf)(const Object *typeObject, const Value &var); }; @@ -218,7 +225,7 @@ struct Q_QML_EXPORT Object: Managed { SetterOffset = 1 }; - void setInternalClass(InternalClass *ic); + void setInternalClass(Heap::InternalClass *ic); const Value *propertyData(uint index) const { return d()->propertyData(index); } @@ -236,20 +243,20 @@ struct Q_QML_EXPORT Object: Managed { Heap::Object *prototype() const { return d()->prototype(); } bool setPrototype(Object *proto); - void getOwnProperty(String *name, PropertyAttributes *attrs, Property *p = nullptr); + void getOwnProperty(StringOrSymbol *name, PropertyAttributes *attrs, Property *p = nullptr); void getOwnProperty(uint index, PropertyAttributes *attrs, Property *p = nullptr); - MemberData::Index getValueOrSetter(String *name, PropertyAttributes *attrs); - ArrayData::Index getValueOrSetter(uint index, PropertyAttributes *attrs); + PropertyIndex getValueOrSetter(StringOrSymbol *name, PropertyAttributes *attrs); + PropertyIndex getValueOrSetter(uint index, PropertyAttributes *attrs); - bool hasProperty(String *name) const; + bool hasProperty(StringOrSymbol *name) const; bool hasProperty(uint index) const; - bool hasOwnProperty(String *name) const; + bool hasOwnProperty(StringOrSymbol *name) const; bool hasOwnProperty(uint index) const; - bool __defineOwnProperty__(ExecutionEngine *engine, uint index, String *member, const Property *p, PropertyAttributes attrs); - bool __defineOwnProperty__(ExecutionEngine *engine, String *name, const Property *p, PropertyAttributes attrs); + bool __defineOwnProperty__(ExecutionEngine *engine, uint index, StringOrSymbol *member, const Property *p, PropertyAttributes attrs); + bool __defineOwnProperty__(ExecutionEngine *engine, StringOrSymbol *name, const Property *p, PropertyAttributes attrs); bool __defineOwnProperty__(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs); bool __defineOwnProperty__(ExecutionEngine *engine, const QString &name, const Property *p, PropertyAttributes attrs); bool defineOwnProperty2(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs); @@ -267,31 +274,33 @@ struct Q_QML_EXPORT Object: Managed { bool putValue(uint memberIndex, const Value &value); /* The spec default: Writable: true, Enumerable: false, Configurable: true */ - void defineDefaultProperty(String *name, const Value &value) { - insertMember(name, value, Attr_Data|Attr_NotEnumerable); + void defineDefaultProperty(StringOrSymbol *name, const Value &value, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable) { + insertMember(name, value, attributes); } - void defineDefaultProperty(const QString &name, const Value &value); - void defineDefaultProperty(const QString &name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount = 0); - void defineDefaultProperty(String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount = 0); - void defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int), - ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int)); - void defineAccessorProperty(String *name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int), - ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int)); + void defineDefaultProperty(const QString &name, const Value &value, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable); + void defineDefaultProperty(const QString &name, jsCallFunction code, + int argumentCount = 0, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable); + void defineDefaultProperty(StringOrSymbol *name, jsCallFunction code, + int argumentCount = 0, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable); + void defineAccessorProperty(const QString &name, jsCallFunction getter, jsCallFunction setter); + void defineAccessorProperty(StringOrSymbol *name, jsCallFunction getter, jsCallFunction setter); /* Fixed: Writable: false, Enumerable: false, Configurable: false */ void defineReadonlyProperty(const QString &name, const Value &value); void defineReadonlyProperty(String *name, const Value &value); /* Fixed: Writable: false, Enumerable: false, Configurable: true */ void defineReadonlyConfigurableProperty(const QString &name, const Value &value); - void defineReadonlyConfigurableProperty(String *name, const Value &value); + void defineReadonlyConfigurableProperty(StringOrSymbol *name, const Value &value); + + void addSymbolSpecies(); - void insertMember(String *s, const Value &v, PropertyAttributes attributes = Attr_Data) { + void insertMember(StringOrSymbol *s, const Value &v, PropertyAttributes attributes = Attr_Data) { Scope scope(engine()); ScopedProperty p(scope); p->value = v; insertMember(s, p, attributes); } - void insertMember(String *s, const Property *p, PropertyAttributes attributes); + void insertMember(StringOrSymbol *s, const Property *p, PropertyAttributes attributes); bool isExtensible() const { return d()->internalClass->extensible; } @@ -360,13 +369,13 @@ public: return false; } - inline ReturnedValue get(String *name, bool *hasProperty = nullptr) const + inline ReturnedValue get(StringOrSymbol *name, bool *hasProperty = nullptr) const { return vtable()->get(this, name, hasProperty); } inline ReturnedValue getIndexed(uint idx, bool *hasProperty = nullptr) const { return vtable()->getIndexed(this, idx, hasProperty); } // use the set variants instead, to customize throw behavior - inline bool put(String *name, const Value &v) + inline bool put(StringOrSymbol *name, const Value &v) { return vtable()->put(this, name, v); } inline bool putIndexed(uint idx, const Value &v) { return vtable()->putIndexed(this, idx, v); } @@ -376,8 +385,25 @@ public: DoNotThrow }; + // This is the same as set(), but it doesn't require creating a string key, + // which is much more efficient for the array case. + inline bool setIndexed(uint idx, const Value &v, ThrowOnFailure shouldThrow) + { + bool ret = vtable()->putIndexed(this, idx, v); + // ES6: 7.3.3, 6: If success is false and Throw is true, throw a TypeError exception. + if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) { + ExecutionEngine *e = engine(); + if (!e->hasException) { // allow a custom set impl to throw itself + QString message = QLatin1String("Cannot assign to read-only property \"") + + QString::number(idx) + QLatin1Char('\"'); + e->throwTypeError(message); + } + } + return ret; + } + // ES6: 7.3.3 Set (O, P, V, Throw) - inline bool set(String *name, const Value &v, ThrowOnFailure shouldThrow) + inline bool set(StringOrSymbol *name, const Value &v, ThrowOnFailure shouldThrow) { bool ret = vtable()->put(this, name, v); // ES6: 7.3.3, 6: If success is false and Throw is true, throw a TypeError exception. @@ -392,41 +418,41 @@ public: return ret; } - PropertyAttributes query(String *name) const + PropertyAttributes query(StringOrSymbol *name) const { return vtable()->query(this, name); } PropertyAttributes queryIndexed(uint index) const { return vtable()->queryIndexed(this, index); } - bool deleteProperty(String *name) + bool deleteProperty(StringOrSymbol *name) { return vtable()->deleteProperty(this, name); } bool deleteIndexedProperty(uint index) { return vtable()->deleteIndexedProperty(this, index); } void advanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) { vtable()->advanceIterator(this, it, name, index, p, attributes); } - uint getLength() const { return vtable()->getLength(this); } + qint64 getLength() const { return vtable()->getLength(this); } ReturnedValue instanceOf(const Value &var) const { return vtable()->instanceOf(this, var); } protected: static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); + static ReturnedValue get(const Managed *m, StringOrSymbol *name, bool *hasProperty); static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - static bool put(Managed *m, String *name, const Value &value); + static bool put(Managed *m, StringOrSymbol *name, const Value &value); static bool putIndexed(Managed *m, uint index, const Value &value); - static PropertyAttributes query(const Managed *m, String *name); + static PropertyAttributes query(const Managed *m, StringOrSymbol *name); static PropertyAttributes queryIndexed(const Managed *m, uint index); - static bool deleteProperty(Managed *m, String *name); + static bool deleteProperty(Managed *m, StringOrSymbol *name); static bool deleteIndexedProperty(Managed *m, uint index); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); - static uint getLength(const Managed *m); + static qint64 getLength(const Managed *m); static ReturnedValue instanceOf(const Object *typeObject, const Value &var); private: - ReturnedValue internalGet(String *name, bool *hasProperty) const; + ReturnedValue internalGet(StringOrSymbol *name, bool *hasProperty) const; ReturnedValue internalGetIndexed(uint index, bool *hasProperty) const; - bool internalPut(String *name, const Value &value); + bool internalPut(StringOrSymbol *name, const Value &value); bool internalPutIndexed(uint index, const Value &value); - bool internalDeleteProperty(String *name); + bool internalDeleteProperty(StringOrSymbol *name); bool internalDeleteIndexedProperty(uint index); friend struct ObjectIterator; @@ -500,7 +526,7 @@ struct ArrayObject: Object { void init(ExecutionEngine *engine); using Object::getLength; - static uint getLength(const Managed *m); + static qint64 getLength(const Managed *m); QStringList toQStringList() const; }; @@ -551,7 +577,7 @@ inline void Object::arraySet(uint index, const Value &value) template<> inline const ArrayObject *Value::as() const { - return isManaged() && m()->vtable()->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : nullptr; + return isManaged() && m()->internalClass->vtable->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : nullptr; } #ifndef V4_BOOTSTRAP diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp index 7bf7e1aa04..1290a2c1b2 100644 --- a/src/qml/jsruntime/qv4objectiterator.cpp +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -42,9 +42,29 @@ #include "qv4identifier_p.h" #include "qv4argumentsobject_p.h" #include "qv4string_p.h" +#include "qv4iterator_p.h" using namespace QV4; +void ForInIteratorPrototype::init(ExecutionEngine *) +{ + defineDefaultProperty(QStringLiteral("next"), method_next, 0); +} + +ReturnedValue ForInIteratorPrototype::method_next(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + const ForInIteratorObject *forIn = thisObject->as<ForInIteratorObject>(); + Q_ASSERT(forIn); + Scope scope(b->engine()); + ScopedValue n(scope, forIn->nextPropertyName()); + bool done = false; + if (n->asReturnedValue() == Encode::null()) { + done = true; + n = Primitive::undefinedValue(); + } + return IteratorPrototype::createIterResultObject(scope.engine, n, done); +} + void ObjectIterator::init(const Object *o) { object->setM(o ? o->m() : nullptr); @@ -175,11 +195,11 @@ ReturnedValue ObjectIterator::nextPropertyNameAsString() } -DEFINE_OBJECT_VTABLE(ForEachIteratorObject); +DEFINE_OBJECT_VTABLE(ForInIteratorObject); -void Heap::ForEachIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack) +void Heap::ForInIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack) { - ForEachIteratorObject *o = static_cast<ForEachIteratorObject *>(that); + ForInIteratorObject *o = static_cast<ForInIteratorObject *>(that); o->workArea[0].mark(markStack); o->workArea[1].mark(markStack); Object::markObjects(that, markStack); diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h index 744d16301a..1e7000ad1f 100644 --- a/src/qml/jsruntime/qv4objectiterator_p.h +++ b/src/qml/jsruntime/qv4objectiterator_p.h @@ -92,8 +92,8 @@ struct Q_QML_EXPORT ObjectIterator: ObjectIteratorData ObjectIterator(Scope &scope, const Object *o, uint flags) { engine = scope.engine; - object = scope.alloc(1); - current = scope.alloc(1); + object = scope.alloc(); + current = scope.alloc(); arrayNode = nullptr; arrayIndex = 0; memberIndex = 0; @@ -111,7 +111,7 @@ private: }; namespace Heap { -struct ForEachIteratorObject : Object { +struct ForInIteratorObject : Object { void init(QV4::Object *o); ObjectIterator &it() { return *reinterpret_cast<ObjectIterator*>(&itData); } Value workArea[2]; @@ -123,15 +123,24 @@ private: } -struct ForEachIteratorObject: Object { - V4_OBJECT2(ForEachIteratorObject, Object) - Q_MANAGED_TYPE(ForeachIteratorObject) +struct ForInIteratorPrototype : Object +{ + V4_PROTOTYPE(iteratorPrototype) + void init(ExecutionEngine *engine); + + static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); +}; + +struct ForInIteratorObject: Object { + V4_OBJECT2(ForInIteratorObject, Object) + Q_MANAGED_TYPE(ForInIterator) + V4_PROTOTYPE(forInIteratorPrototype) - ReturnedValue nextPropertyName() { return d()->it().nextPropertyNameAsString(); } + ReturnedValue nextPropertyName() const { return d()->it().nextPropertyNameAsString(); } }; inline -void Heap::ForEachIteratorObject::init(QV4::Object *o) +void Heap::ForInIteratorObject::init(QV4::Object *o) { Object::init(); it() = ObjectIterator(internalClass->engine, workArea, workArea + 1, o, diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index b998b78520..a61b5d5136 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -47,6 +47,7 @@ #include "qv4objectiterator_p.h" #include "qv4string_p.h" #include "qv4jscall_p.h" +#include "qv4symbol_p.h" #include <QtCore/QDateTime> #include <QtCore/QStringList> @@ -97,6 +98,7 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) ctor->defineDefaultProperty(QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); + ctor->defineDefaultProperty(QStringLiteral("getOwnPropertySymbols"), method_getOwnPropertySymbols, 1); ctor->defineDefaultProperty(QStringLiteral("assign"), method_assign, 2); ctor->defineDefaultProperty(QStringLiteral("create"), method_create, 2); ctor->defineDefaultProperty(QStringLiteral("defineProperty"), method_defineProperty, 3); @@ -104,10 +106,12 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) ctor->defineDefaultProperty(QStringLiteral("seal"), method_seal, 1); ctor->defineDefaultProperty(QStringLiteral("freeze"), method_freeze, 1); ctor->defineDefaultProperty(QStringLiteral("preventExtensions"), method_preventExtensions, 1); + ctor->defineDefaultProperty(QStringLiteral("is"), method_is, 2); ctor->defineDefaultProperty(QStringLiteral("isSealed"), method_isSealed, 1); ctor->defineDefaultProperty(QStringLiteral("isFrozen"), method_isFrozen, 1); ctor->defineDefaultProperty(QStringLiteral("isExtensible"), method_isExtensible, 1); ctor->defineDefaultProperty(QStringLiteral("keys"), method_keys, 1); + ctor->defineDefaultProperty(QStringLiteral("setPrototypeOf"), method_setPrototypeOf, 2); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(v4->id_toString(), method_toString, 0); @@ -119,11 +123,7 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) defineDefaultProperty(QStringLiteral("__defineGetter__"), method_defineGetter, 2); defineDefaultProperty(QStringLiteral("__defineSetter__"), method_defineSetter, 2); - ExecutionContext *global = v4->rootContext(); - ScopedProperty p(scope); - p->value = FunctionObject::createBuiltinFunction(global, v4->id___proto__(), method_get_proto); - p->set = FunctionObject::createBuiltinFunction(global, v4->id___proto__(), method_set_proto); - insertMember(v4->id___proto__(), p, Attr_Accessor|Attr_NotEnumerable); + defineAccessorProperty(v4->id___proto__(), method_get_proto, method_set_proto); } ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, const Value *, const Value *argv, int argc) @@ -140,6 +140,15 @@ ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, co return (!!p ? p->asReturnedValue() : Encode::null()); } +ReturnedValue ObjectPrototype::method_is(const FunctionObject *, const Value *, const Value *argv, int argc) +{ + if (!argc) + return Encode(true); + if (argc == 1) + return Encode((argv[0].isUndefined() ? true : false)); + return Encode(argv[0].sameValue(argv[1])); +} + ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const FunctionObject *b, const Value *, const Value *argv, int argc) { Scope scope(b); @@ -154,7 +163,7 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const FunctionObj static_cast<ArgumentsObject *>(O.getPointer())->fullyCreate(); ScopedValue v(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); - ScopedString name(scope, v->toString(scope.engine)); + ScopedStringOrSymbol name(scope, v->toPropertyKey(scope.engine)); if (scope.engine->hasException) return QV4::Encode::undefined(); @@ -177,6 +186,28 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyNames(const FunctionObject * return Encode(getOwnPropertyNames(scope.engine, argv[0])); } +ReturnedValue ObjectPrototype::method_getOwnPropertySymbols(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (!argc) + return scope.engine->throwTypeError(); + + ScopedObject O(scope, argv[0].toObject(scope.engine)); + if (!O) + return Encode::undefined(); + Heap::InternalClass *ic = O->d()->internalClass; + ScopedValue n(scope); + ScopedArrayObject array(scope, scope.engine->newArrayObject()); + for (uint i = 0; i < ic->size; ++i) { + Identifier id = ic->nameMap.at(i); + n = id.asHeapObject(); + if (!n || !n->isSymbol()) + continue; + array->push_back(n); + } + return array->asReturnedValue(); +} + // 19.1.2.1 ReturnedValue ObjectPrototype::method_assign(const FunctionObject *b, const Value *, const Value *argv, int argc) { @@ -255,7 +286,7 @@ ReturnedValue ObjectPrototype::method_defineProperty(const FunctionObject *b, co return scope.engine->throwTypeError(); ScopedObject O(scope, argv[0]); - ScopedString name(scope, argc > 1 ? argv[1] : Primitive::undefinedValue(), ScopedString::Convert); + ScopedStringOrSymbol name(scope, (argc > 1 ? argv[1] : Primitive::undefinedValue()).toPropertyKey(scope.engine)); if (scope.engine->hasException) return QV4::Encode::undefined(); @@ -287,7 +318,7 @@ ReturnedValue ObjectPrototype::method_defineProperties(const FunctionObject *b, ScopedValue val(scope); ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly); - ScopedString name(scope); + ScopedStringOrSymbol name(scope); ScopedProperty pd(scope); ScopedProperty n(scope); while (1) { @@ -477,19 +508,60 @@ ReturnedValue ObjectPrototype::method_keys(const FunctionObject *b, const Value return a.asReturnedValue(); } +ReturnedValue ObjectPrototype::method_setPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f->engine()); + if (argc < 2 || argv[0].isNullOrUndefined() || !(argv[1].isObject() || argv[1].isNull())) + return scope.engine->throwTypeError(); + + if (!argv[0].isObject()) + return argv[0].asReturnedValue(); + + ScopedObject o(scope, argv[0]); + ScopedObject p(scope, argv[1]); + Q_ASSERT(!!o); + + if (o->prototype() != p->d()) { + bool ok = false; + if (o->isExtensible()) { + ok = o->setPrototype(p); + } + if (!ok) + return scope.engine->throwTypeError(QStringLiteral("Object.setPrototypeOf: Could not change prototype")); + } + return o->asReturnedValue(); +} + ReturnedValue ObjectPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); + QString string; if (thisObject->isUndefined()) { - return Encode(v4->newString(QStringLiteral("[object Undefined]"))); + string = QStringLiteral("[object Undefined]"); } else if (thisObject->isNull()) { - return Encode(v4->newString(QStringLiteral("[object Null]"))); + string = QStringLiteral("[object Null]"); } else { + const Object *o = thisObject->as<Object>(); + if (!o) { + // primitive, get the proper prototype + if (thisObject->isBoolean()) + o = v4->booleanPrototype(); + else if (thisObject->isNumber()) + o = v4->numberPrototype(); + else if (thisObject->isString()) + o = v4->stringPrototype(); + else if (thisObject->isSymbol()) + o = v4->symbolPrototype(); + Q_ASSERT(o); + } + QString name = o->className(); Scope scope(v4); - ScopedObject obj(scope, thisObject->toObject(scope.engine)); - QString className = obj->className(); - return Encode(v4->newString(QStringLiteral("[object %1]").arg(className))); + ScopedString toStringTag(scope, o->get(v4->symbol_toStringTag())); + if (toStringTag) + name = toStringTag->toQString(); + string = QStringLiteral("[object %1]").arg(name); } + return Encode(v4->newString(string)); } ReturnedValue ObjectPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) @@ -514,7 +586,7 @@ ReturnedValue ObjectPrototype::method_valueOf(const FunctionObject *b, const Val ReturnedValue ObjectPrototype::method_hasOwnProperty(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); - ScopedString P(scope, argc ? argv[0] : Primitive::undefinedValue(), ScopedString::Convert); + ScopedStringOrSymbol P(scope, (argc ? argv[0] : Primitive::undefinedValue()).toPropertyKey(scope.engine)); if (scope.engine->hasException) return QV4::Encode::undefined(); ScopedObject O(scope, thisObject->toObject(scope.engine)); @@ -548,7 +620,7 @@ ReturnedValue ObjectPrototype::method_isPrototypeOf(const FunctionObject *b, con ReturnedValue ObjectPrototype::method_propertyIsEnumerable(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); - ScopedString p(scope, argc ? argv[0] : Primitive::undefinedValue(), ScopedString::Convert); + ScopedStringOrSymbol p(scope, (argc ? argv[0] : Primitive::undefinedValue()).toPropertyKey(scope.engine)); if (scope.engine->hasException) return QV4::Encode::undefined(); @@ -734,29 +806,28 @@ ReturnedValue ObjectPrototype::fromPropertyDescriptor(ExecutionEngine *engine, c // is the standard built-in constructor with that name. ScopedObject o(scope, engine->newObject()); ScopedString s(scope); + ScopedValue v(scope); - ScopedProperty pd(scope); if (attrs.isData()) { - pd->value = desc->value; s = engine->newString(QStringLiteral("value")); - o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data); - pd->value = Primitive::fromBoolean(attrs.isWritable()); + o->put(s, desc->value); + v = Primitive::fromBoolean(attrs.isWritable()); s = engine->newString(QStringLiteral("writable")); - o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data); + o->put(s, v); } else { - pd->value = desc->getter() ? desc->getter()->asReturnedValue() : Encode::undefined(); + v = desc->getter() ? desc->getter()->asReturnedValue() : Encode::undefined(); s = engine->newString(QStringLiteral("get")); - o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data); - pd->value = desc->setter() ? desc->setter()->asReturnedValue() : Encode::undefined(); + o->put(s, v); + v = desc->setter() ? desc->setter()->asReturnedValue() : Encode::undefined(); s = engine->newString(QStringLiteral("set")); - o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data); + o->put(s, v); } - pd->value = Primitive::fromBoolean(attrs.isEnumerable()); + v = Primitive::fromBoolean(attrs.isEnumerable()); s = engine->newString(QStringLiteral("enumerable")); - o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data); - pd->value = Primitive::fromBoolean(attrs.isConfigurable()); + o->put(s, v); + v = Primitive::fromBoolean(attrs.isConfigurable()); s = engine->newString(QStringLiteral("configurable")); - o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data); + o->put(s, v); return o.asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h index 2b231d46ad..1a93685093 100644 --- a/src/qml/jsruntime/qv4objectproto_p.h +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -78,20 +78,23 @@ struct ObjectPrototype: Object { void init(ExecutionEngine *engine, Object *ctor); - static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_getOwnPropertyNames(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_assign(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_create(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_defineProperties(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_seal(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_freeze(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_preventExtensions(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_isSealed(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_isFrozen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getOwnPropertyNames(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getOwnPropertySymbols(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_is(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_isExtensible(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_isFrozen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_isSealed(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_preventExtensions(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_seal(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_setPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp index 7fc74173e3..f8bc28160e 100644 --- a/src/qml/jsruntime/qv4persistent.cpp +++ b/src/qml/jsruntime/qv4persistent.cpp @@ -98,9 +98,9 @@ Page *allocatePage(PersistentValueStorage *storage) p->header.freeList = 0; insertInFront(storage, p); for (int i = 0; i < kEntriesPerPage - 1; ++i) { - p->values[i].setEmpty(i + 1); + p->values[i] = Encode(i + 1); } - p->values[kEntriesPerPage - 1].setEmpty(-1); + p->values[kEntriesPerPage - 1] = Encode(-1); return p; } @@ -226,7 +226,7 @@ void PersistentValueStorage::free(Value *v) Page *p = getPage(v); - v->setEmpty(p->header.freeList); + *v = Encode(p->header.freeList); p->header.freeList = v - p->values; if (!--p->header.refCount) freePage(p); diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp index 5fd200efc1..b337243204 100644 --- a/src/qml/jsruntime/qv4profiling.cpp +++ b/src/qml/jsruntime/qv4profiling.cpp @@ -78,7 +78,7 @@ Profiler::Profiler(QV4::ExecutionEngine *engine) : featuresEnabled(0), m_engine( void Profiler::stopProfiling() { featuresEnabled = 0; - reportData(true); + reportData(); m_sentLocations.clear(); } @@ -89,7 +89,7 @@ bool operator<(const FunctionCall &call1, const FunctionCall &call2) (call1.m_end == call2.m_end && call1.m_function < call2.m_function))); } -void Profiler::reportData(bool trackLocations) +void Profiler::reportData() { std::sort(m_data.begin(), m_data.end()); QVector<FunctionCallProperties> properties; @@ -100,12 +100,11 @@ void Profiler::reportData(bool trackLocations) properties.append(call.properties()); Function *function = call.function(); SentMarker &marker = m_sentLocations[reinterpret_cast<quintptr>(function)]; - if (!trackLocations || !marker.isValid()) { + if (!marker.isValid()) { FunctionLocation &location = locations[properties.constLast().id]; if (!location.isValid()) location = call.resolveLocation(); - if (trackLocations) - marker.setFunction(function); + marker.setFunction(function); } } diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index e8c154e4e7..8461384e9a 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -251,7 +251,7 @@ public: void stopProfiling(); void startProfiling(quint64 features); - void reportData(bool trackLocations); + void reportData(); void setTimer(const QElapsedTimer &timer) { m_timer = timer; } signals: diff --git a/src/qml/jsruntime/qv4property_p.h b/src/qml/jsruntime/qv4property_p.h index 7cb106c424..26dc7a83c3 100644 --- a/src/qml/jsruntime/qv4property_p.h +++ b/src/qml/jsruntime/qv4property_p.h @@ -142,6 +142,19 @@ inline void Property::merge(PropertyAttributes &attrs, const Property *other, Pr } } +struct PropertyIndex { + Heap::Base *base; + Value *slot; + + void set(EngineBase *e, Value newVal) { + WriteBarrier::write(e, base, slot->data_ptr(), newVal.asReturnedValue()); + } + const Value *operator->() const { return slot; } + const Value &operator*() const { return *slot; } + bool isNull() const { return !slot; } +}; + + } Q_DECLARE_TYPEINFO(QV4::Property, Q_MOVABLE_TYPE); diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 040f060476..4226bf7972 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -78,30 +78,26 @@ void Heap::QQmlContextWrapper::destroy() Object::destroy(); } -ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasProperty) +ReturnedValue QQmlContextWrapper::get(const Managed *m, StringOrSymbol *n, bool *hasProperty) { Q_ASSERT(m->as<QQmlContextWrapper>()); + + if (n->isSymbol()) + return Object::get(m, n, hasProperty); + String *name = static_cast<String *>(n); + const QQmlContextWrapper *resource = static_cast<const QQmlContextWrapper *>(m); QV4::ExecutionEngine *v4 = resource->engine(); QV4::Scope scope(v4); - // In V8 the JS global object would come _before_ the QML global object, - // so simulate that here. - bool hasProp; - QV4::ScopedValue result(scope, v4->globalObject->get(name, &hasProp)); - if (hasProp) { - if (hasProperty) - *hasProperty = hasProp; - return result->asReturnedValue(); - } - if (resource->d()->isNullWrapper) return Object::get(m, name, hasProperty); if (v4->callingQmlContext() != *resource->d()->context) return Object::get(m, name, hasProperty); - result = Object::get(m, name, &hasProp); + bool hasProp = false; + ScopedValue result(scope, Object::get(m, name, &hasProp)); if (hasProp) { if (hasProperty) *hasProperty = hasProp; @@ -219,14 +215,28 @@ ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasP context = context->parent; } + // Do a lookup in the global object here to avoid expressionContext->unresolvedNames becoming + // true if we access properties of the global object. + result = v4->globalObject->get(name, &hasProp); + if (hasProp) { + if (hasProperty) + *hasProperty = hasProp; + return result->asReturnedValue(); + } + expressionContext->unresolvedNames = true; return Encode::undefined(); } -bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value) +bool QQmlContextWrapper::put(Managed *m, StringOrSymbol *n, const Value &value) { Q_ASSERT(m->as<QQmlContextWrapper>()); + + if (n->isSymbol()) + return Object::put(m, n, value); + String *name = static_cast<String *>(n); + QQmlContextWrapper *resource = static_cast<QQmlContextWrapper *>(m); ExecutionEngine *v4 = resource->engine(); QV4::Scope scope(v4); @@ -234,7 +244,8 @@ bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value) return false; QV4::Scoped<QQmlContextWrapper> wrapper(scope, resource); - uint member = wrapper->internalClass()->find(name); + name->makeIdentifier(); + uint member = wrapper->internalClass()->find(name->identifier()); if (member < UINT_MAX) return wrapper->putValue(member, value); @@ -312,7 +323,7 @@ Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, cons context->isInternal = true; context->isJSContext = true; - Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QQmlContextWrapper>(context, (QObject*)nullptr)); + Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocate<QQmlContextWrapper>(context, (QObject*)nullptr)); qml->d()->isNullWrapper = true; qml->setReadOnly(false); @@ -330,7 +341,7 @@ Heap::QmlContext *QmlContext::create(ExecutionContext *parent, QQmlContextData * { Scope scope(parent); - Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QQmlContextWrapper>(context, scopeObject)); + Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocate<QQmlContextWrapper>(context, scopeObject)); Heap::QmlContext *c = scope.engine->memoryManager->alloc<QmlContext>(parent, qml); Q_ASSERT(c->vtable() == staticVTable()); return c; diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index 647bef7fc1..86c5b62da2 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -98,8 +98,8 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object void setReadOnly(bool b) { d()->readOnly = b; } - static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static bool put(Managed *m, String *name, const Value &value); + static ReturnedValue get(const Managed *m, StringOrSymbol *name, bool *hasProperty); + static bool put(Managed *m, StringOrSymbol *name, const Value &value); }; struct Q_QML_EXPORT QmlContext : public ExecutionContext diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index d63d42478a..e17ce55f7b 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -56,7 +56,11 @@ #include <private/qv4functionobject_p.h> #include <private/qv4runtime_p.h> #include <private/qv4variantobject_p.h> + +#if QT_CONFIG(qml_sequence_object) #include <private/qv4sequenceobject_p.h> +#endif + #include <private/qv4objectproto_p.h> #include <private/qv4jsonobject_p.h> #include <private/qv4regexpobject_p.h> @@ -181,11 +185,13 @@ static QV4::ReturnedValue loadProperty(QV4::ExecutionEngine *v4, QObject *object if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property.propType())) return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, property.propType()); } else { +#if QT_CONFIG(qml_sequence_object) // see if it's a sequence type bool succeeded = false; - QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType(), object, property.coreIndex(), &succeeded)); + QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType(), object, property.coreIndex(), !property.isWritable(), &succeeded)); if (succeeded) return retn->asReturnedValue(); +#endif } if (property.propType() == QMetaType::UnknownType) { @@ -242,7 +248,7 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje return QV4::QObjectMethod::create(global, object, property->coreIndex()); } else if (property->isSignalHandler()) { QmlSignalHandler::initProto(engine); - return engine->memoryManager->allocObject<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue(); + return engine->memoryManager->allocate<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue(); } else { ExecutionContext *global = engine->rootContext(); return QV4::QObjectMethod::create(global, object, property->coreIndex()); @@ -684,18 +690,25 @@ ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QObject *object) return result; } } - return (engine->memoryManager->allocObject<QV4::QObjectWrapper>(object))->asReturnedValue(); + return (engine->memoryManager->allocate<QV4::QObjectWrapper>(object))->asReturnedValue(); } -QV4::ReturnedValue QObjectWrapper::get(const Managed *m, String *name, bool *hasProperty) +QV4::ReturnedValue QObjectWrapper::get(const Managed *m, StringOrSymbol *name, bool *hasProperty) { + if (name->isSymbol()) + return Object::get(m, name, hasProperty); + String *n = static_cast<String *>(name); const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m); QQmlContextData *qmlContext = that->engine()->callingQmlContext(); - return that->getQmlProperty(qmlContext, name, IgnoreRevision, hasProperty, /*includeImports*/ true); + return that->getQmlProperty(qmlContext, n, IgnoreRevision, hasProperty, /*includeImports*/ true); } -bool QObjectWrapper::put(Managed *m, String *name, const Value &value) +bool QObjectWrapper::put(Managed *m, StringOrSymbol *n, const Value &value) { + if (n->isSymbol()) + return Object::put(m, n, value); + String *name = static_cast<String *>(n); + QObjectWrapper *that = static_cast<QObjectWrapper*>(m); ExecutionEngine *v4 = that->engine(); @@ -720,8 +733,12 @@ bool QObjectWrapper::put(Managed *m, String *name, const Value &value) return true; } -PropertyAttributes QObjectWrapper::query(const Managed *m, String *name) +PropertyAttributes QObjectWrapper::query(const Managed *m, StringOrSymbol *name) { + if (name->isSymbol()) + return QV4::Object::query(m, name); + String *n = static_cast<String *>(name); + const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m); const QObject *thatObject = that->d()->object(); if (QQmlData::wasDeleted(thatObject)) @@ -730,8 +747,8 @@ PropertyAttributes QObjectWrapper::query(const Managed *m, String *name) ExecutionEngine *engine = that->engine(); QQmlContextData *qmlContext = engine->callingQmlContext(); QQmlPropertyData local; - if (that->findProperty(engine, qmlContext, name, IgnoreRevision, &local) - || name->equals(engine->id_destroy()) || name->equals(engine->id_toString())) + if (that->findProperty(engine, qmlContext, n, IgnoreRevision, &local) + || n->equals(engine->id_destroy()) || n->equals(engine->id_toString())) return QV4::Attr_Data; else return QV4::Object::query(m, name); @@ -1619,6 +1636,7 @@ void CallArgument::initAsType(int callType) } } +#if QT_CONFIG(qml_sequence_object) template <class T, class M> void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M CallArgument::*member, bool &queryEngine) { @@ -1631,6 +1649,7 @@ void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M } } } +#endif void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const QV4::Value &value) { @@ -1713,6 +1732,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q type = callType; } else if (callType == QMetaType::Void) { *qvariantPtr = QVariant(); +#if QT_CONFIG(qml_sequence_object) } else if (callType == qMetaTypeId<std::vector<int>>() || callType == qMetaTypeId<std::vector<qreal>>() || callType == qMetaTypeId<std::vector<bool>>() @@ -1740,6 +1760,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q stdVectorQModelIndexPtr = nullptr; fromContainerValue<std::vector<QModelIndex>>(object, callType, &CallArgument::stdVectorQModelIndexPtr, queryEngine); } +#endif } else { queryEngine = true; } @@ -1833,7 +1854,7 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine) ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index) { Scope valueScope(scope); - Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope)); + Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope)); method->d()->setObject(object); if (QQmlData *ddata = QQmlData::get(object)) @@ -1846,7 +1867,7 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, in ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index) { Scope valueScope(scope); - Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope)); + Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope)); method->d()->setPropertyCache(valueType->d()->propertyCache()); method->d()->index = index; method->d()->valueTypeWrapper.set(valueScope.engine, valueType->d()); @@ -2019,7 +2040,7 @@ void Heap::QMetaObjectWrapper::ensureConstructorsCache() { ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) { QV4::Scope scope(engine); - Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocObject<QV4::QMetaObjectWrapper>(metaObject)->asReturnedValue()); + Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocate<QV4::QMetaObjectWrapper>(metaObject)->asReturnedValue()); mo->init(engine); return mo->asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 1455acc1b3..56e20adbfa 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -193,9 +193,9 @@ protected: static QQmlPropertyData *findProperty(ExecutionEngine *engine, QObject *o, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local); QQmlPropertyData *findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const; - static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static bool put(Managed *m, String *name, const Value &value); - static PropertyAttributes query(const Managed *, String *name); + static ReturnedValue get(const Managed *m, StringOrSymbol *name, bool *hasProperty); + static bool put(Managed *m, StringOrSymbol *name, const Value &value); + static PropertyAttributes query(const Managed *, StringOrSymbol *name); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static ReturnedValue method_connect(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp new file mode 100644 index 0000000000..69baecd337 --- /dev/null +++ b/src/qml/jsruntime/qv4reflect.cpp @@ -0,0 +1,270 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4reflect_p.h" +#include "qv4symbol_p.h" +#include "qv4runtimeapi_p.h" +#include "qv4objectproto_p.h" + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(Reflect); + +void Heap::Reflect::init() +{ + Object::init(); + Scope scope(internalClass->engine); + ScopedObject r(scope, this); + + r->defineDefaultProperty(QStringLiteral("apply"), QV4::Reflect::method_apply, 3); + r->defineDefaultProperty(QStringLiteral("construct"), QV4::Reflect::method_construct, 2); + r->defineDefaultProperty(QStringLiteral("defineProperty"), QV4::Reflect::method_defineProperty, 3); + r->defineDefaultProperty(QStringLiteral("deleteProperty"), QV4::Reflect::method_deleteProperty, 2); + r->defineDefaultProperty(QStringLiteral("get"), QV4::Reflect::method_get, 2); + r->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), QV4::Reflect::method_getOwnPropertyDescriptor, 2); + r->defineDefaultProperty(QStringLiteral("getPrototypeOf"), QV4::Reflect::method_getPrototypeOf, 1); + r->defineDefaultProperty(QStringLiteral("has"), QV4::Reflect::method_has, 2); + r->defineDefaultProperty(QStringLiteral("isExtensible"), QV4::Reflect::method_isExtensible, 1); + r->defineDefaultProperty(QStringLiteral("ownKeys"), QV4::Reflect::method_ownKeys, 1); + r->defineDefaultProperty(QStringLiteral("preventExtensions"), QV4::Reflect::method_preventExtensions, 1); + r->defineDefaultProperty(QStringLiteral("set"), QV4::Reflect::method_set, 3); + r->defineDefaultProperty(QStringLiteral("setPrototypeOf"), QV4::Reflect::method_setPrototypeOf, 2); +} + +struct CallArgs { + Value *argv; + int argc; +}; + +static CallArgs createListFromArrayLike(Scope &scope, const Object *o) +{ + int len = o->getLength(); + Value *arguments = scope.alloc(len); + + for (int i = 0; i < len; ++i) { + arguments[i] = o->getIndexed(i); + if (scope.hasException()) + return { nullptr, 0 }; + } + return { arguments, len }; +} + +ReturnedValue Reflect::method_apply(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (argc < 3 || !argv[0].isFunctionObject() || !argv[2].isObject()) + return scope.engine->throwTypeError(); + + const Object *o = static_cast<const Object *>(argv + 2); + CallArgs arguments = createListFromArrayLike(scope, o); + if (scope.hasException()) + return Encode::undefined(); + + return static_cast<const FunctionObject &>(argv[0]).call(&argv[1], arguments.argv, arguments.argc); +} + +ReturnedValue Reflect::method_construct(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (argc < 2 || !argv[0].isFunctionObject() || !argv[1].isObject()) + return scope.engine->throwTypeError(); + + const Object *o = static_cast<const Object *>(argv + 1); + CallArgs arguments = createListFromArrayLike(scope, o); + if (scope.hasException()) + return Encode::undefined(); + + return static_cast<const FunctionObject &>(argv[0]).callAsConstructor(arguments.argv, arguments.argc); +} + +ReturnedValue Reflect::method_defineProperty(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (!argc || !argv[0].isObject()) + return scope.engine->throwTypeError(); + + ScopedObject O(scope, argv[0]); + ScopedStringOrSymbol name(scope, (argc > 1 ? argv[1] : Primitive::undefinedValue()).toPropertyKey(scope.engine)); + if (scope.engine->hasException) + return QV4::Encode::undefined(); + + ScopedValue attributes(scope, argc > 2 ? argv[2] : Primitive::undefinedValue()); + ScopedProperty pd(scope); + PropertyAttributes attrs; + ObjectPrototype::toPropertyDescriptor(scope.engine, attributes, pd, &attrs); + if (scope.engine->hasException) + return QV4::Encode::undefined(); + + bool result = O->__defineOwnProperty__(scope.engine, name, pd, attrs); + + return Encode(result); +} + +ReturnedValue Reflect::method_deleteProperty(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + ExecutionEngine *e = f->engine(); + if (!argc || !argv[0].isObject()) + return e->throwTypeError(); + + bool result = Runtime::method_deleteProperty(e, argv[0], argc > 1 ? argv[1] : Primitive::undefinedValue()); + return Encode(result); +} + +ReturnedValue Reflect::method_get(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + // ### Fix third receiver argument + Scope scope(f); + if (!argc || !argv[0].isObject()) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, static_cast<const Object *>(argv)); + Value undef = Primitive::undefinedValue(); + const Value *index = argc > 1 ? &argv[1] : &undef; + + uint n = index->asArrayIndex(); + if (n < UINT_MAX) + return Encode(o->getIndexed(n)); + + ScopedStringOrSymbol name(scope, index->toPropertyKey(scope.engine)); + if (scope.engine->hasException) + return false; + return Encode(o->get(name)); +} + +ReturnedValue Reflect::method_getOwnPropertyDescriptor(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + if (!argc || !argv[0].isObject()) + return f->engine()->throwTypeError(); + + return ObjectPrototype::method_getOwnPropertyDescriptor(f, thisObject, argv, argc); +} + +ReturnedValue Reflect::method_getPrototypeOf(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + if (!argc || !argv[0].isObject()) + return f->engine()->throwTypeError(); + + return ObjectPrototype::method_getPrototypeOf(f, thisObject, argv, argc); +} + +ReturnedValue Reflect::method_has(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (!argc || !argv[0].isObject()) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, static_cast<const Object *>(argv)); + Value undef = Primitive::undefinedValue(); + const Value *index = argc > 1 ? &argv[1] : &undef; + + bool hasProperty = false; + uint n = index->asArrayIndex(); + if (n < UINT_MAX) { + (void) o->getIndexed(n, &hasProperty); + return Encode(hasProperty); + } + + ScopedStringOrSymbol name(scope, index->toPropertyKey(scope.engine)); + if (scope.engine->hasException) + return false; + (void) o->get(name, &hasProperty); + return Encode(hasProperty); +} + +ReturnedValue Reflect::method_isExtensible(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + if (!argc || !argv[0].isObject()) + return f->engine()->throwTypeError(); + + const Object *o = static_cast<const Object *>(argv); + return Encode(o->isExtensible()); +} + + +ReturnedValue Reflect::method_ownKeys(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + if (!argc || !argv[0].isObject()) + return f->engine()->throwTypeError(); + + return ObjectPrototype::method_getOwnPropertyNames(f, thisObject, argv, argc); +} + +ReturnedValue Reflect::method_preventExtensions(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + if (!argc || !argv[0].isObject()) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, static_cast<const Object *>(argv)); + o->setInternalClass(o->internalClass()->nonExtensible()); + return Encode(true); +} + +ReturnedValue Reflect::method_set(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + // ### Fix third receiver argument + Scope scope(f); + if (!argc || !argv[0].isObject()) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, static_cast<const Object *>(argv)); + Value undef = Primitive::undefinedValue(); + const Value *index = argc > 1 ? &argv[1] : &undef; + const Value &val = argc > 2 ? argv[2] : undef; + + uint n = index->asArrayIndex(); + if (n < UINT_MAX) { + bool result = o->putIndexed(n, val); + return Encode(result); + } + + ScopedStringOrSymbol name(scope, index->toPropertyKey(scope.engine)); + if (scope.engine->hasException) + return false; + bool result = o->put(name, val); + return Encode(result); +} + +ReturnedValue Reflect::method_setPrototypeOf(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + if (argc < 2 || !argv[0].isObject()) + return f->engine()->throwTypeError(); + + return ObjectPrototype::method_setPrototypeOf(f, thisObject, argv, argc); +} diff --git a/src/qml/jsruntime/qv4reflect_p.h b/src/qml/jsruntime/qv4reflect_p.h new file mode 100644 index 0000000000..73d257e006 --- /dev/null +++ b/src/qml/jsruntime/qv4reflect_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4REFLECT_H +#define QV4REFLECT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Heap { + +struct Reflect : Object { + void init(); +}; + +} + +struct Reflect : Object { + V4_OBJECT2(Reflect, Object) + + static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_construct(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_deleteProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_isExtensible(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_ownKeys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_preventExtensions(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_setPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 000e2c3a7e..4fce63c5cf 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -45,10 +45,6 @@ #include "qv4scopedvalue_p.h" #include "qv4jscall_p.h" -#include <private/qqmljsengine_p.h> -#include <private/qqmljslexer_p.h> -#include <private/qqmljsparser_p.h> -#include <private/qqmljsast_p.h> #include "private/qlocale_tools_p.h" #include <QtCore/QDebug> @@ -280,7 +276,8 @@ void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor) ScopedObject ctor(scope, constructor); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(2)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(2)); + ctor->addSymbolSpecies(); // Properties deprecated in the spec but required by "the web" :( ctor->defineAccessorProperty(QStringLiteral("lastMatch"), method_get_lastMatch_n<0>, nullptr); @@ -387,7 +384,7 @@ ReturnedValue RegExpPrototype::method_exec(const FunctionObject *b, const Value } // fill in result data - ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->internalClasses[EngineBase::Class_RegExpExecArray], scope.engine->arrayPrototype())); + ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->internalClasses(EngineBase::Class_RegExpExecArray))); int len = r->value()->captureCount(); array->arrayReserve(len); ScopedValue v(scope); diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index 181628241b..a9ebe8384f 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -129,11 +129,11 @@ struct RegExpObject: Object { void initProperties(); int lastIndex() const { - Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex())); + Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex()->identifier())); return propertyData(Index_LastIndex)->toInt32(); } void setLastIndex(int index) { - Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex())); + Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex()->identifier())); return setProperty(Index_LastIndex, Primitive::fromInt32(index)); } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 9729228511..8759a72074 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -61,6 +61,8 @@ #include <private/qqmlengine_p.h> #include <private/qqmljavascriptexpression_p.h> #include "qv4qobjectwrapper_p.h" +#include "qv4symbol_p.h" +#include "qv4generatorobject_p.h" #include <private/qv8engine_p.h> #endif @@ -314,37 +316,27 @@ ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId) QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeFunctions[functionId]; Q_ASSERT(clos); ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context); + if (clos->isGenerator()) + return GeneratorFunction::create(current, clos)->asReturnedValue(); return FunctionObject::createScriptFunction(current, clos)->asReturnedValue(); } -bool Runtime::method_deleteElement(ExecutionEngine *engine, const Value &base, const Value &index) +bool Runtime::method_deleteProperty(ExecutionEngine *engine, const Value &base, const Value &index) { Scope scope(engine); - ScopedObject o(scope, base); - if (o) { - uint n = index.asArrayIndex(); - if (n < UINT_MAX) - return o->deleteIndexedProperty(n); - } - - ScopedString name(scope, index.toString(engine)); - return method_deleteMemberString(engine, base, name); -} - -bool Runtime::method_deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex) -{ - Scope scope(engine); - ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - return method_deleteMemberString(engine, base, name); -} - -bool Runtime::method_deleteMemberString(ExecutionEngine *engine, const Value &base, String *name) -{ - Scope scope(engine); - ScopedObject obj(scope, base.toObject(engine)); + ScopedObject o(scope, base.toObject(engine)); if (scope.engine->hasException) return Encode::undefined(); - return obj->deleteProperty(name); + Q_ASSERT(o); + + uint n = index.asArrayIndex(); + if (n < UINT_MAX) + return o->deleteIndexedProperty(n); + + ScopedStringOrSymbol name(scope, index.toPropertyKey(engine)); + if (engine->hasException) + return false; + return o->deleteProperty(name); } bool Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex) @@ -361,8 +353,16 @@ QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Val if (!rhs) return engine->throwTypeError(); - // 11.8.6, 7: call "HasInstance", which we term instanceOf, and return the result. - return rhs->instanceOf(lval); + Scope scope(engine); + ScopedValue hasInstance(scope, rhs->get(engine->symbol_hasInstance())); + if (hasInstance->isUndefined()) + return rhs->instanceOf(lval); + FunctionObject *f = hasInstance->as<FunctionObject>(); + if (!f) + return engine->throwTypeError(); + + ScopedValue result(scope, f->call(&rval, &lval, 1)); + return Encode(result->toBoolean()); } QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left, const Value &right) @@ -371,7 +371,7 @@ QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left if (!ro) return engine->throwTypeError(); Scope scope(engine); - ScopedString s(scope, left.toString(engine)); + ScopedStringOrSymbol s(scope, left.toPropertyKey(engine)); if (scope.hasException()) return Encode::undefined(); bool r = ro->hasProperty(s); @@ -408,27 +408,59 @@ Heap::String *RuntimeHelpers::stringFromNumber(ExecutionEngine *engine, double n ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeHint) { - if (typeHint == PREFERREDTYPE_HINT) { - if (object->as<DateObject>()) - typeHint = STRING_HINT; - else - typeHint = NUMBER_HINT; + ExecutionEngine *engine = object->internalClass()->engine; + if (engine->hasException) + return Encode::undefined(); + + String *hint; + switch (typeHint) { + case STRING_HINT: + hint = engine->id_string(); + break; + case NUMBER_HINT: + hint = engine->id_number(); + break; + default: + hint = engine->id_default(); + break; } - ExecutionEngine *engine = object->internalClass()->engine; + Scope scope(engine); + ScopedFunctionObject toPrimitive(scope, object->get(engine->symbol_toPrimitive())); if (engine->hasException) return Encode::undefined(); + if (toPrimitive) { + ScopedValue result(scope, toPrimitive->call(object, hint, 1)); + if (engine->hasException) + return Encode::undefined(); + if (!result->isPrimitive()) + return engine->throwTypeError(); + return result->asReturnedValue(); + } + + if (hint == engine->id_default()) + hint = engine->id_number(); + return ordinaryToPrimitive(engine, object, hint); +} + + +ReturnedValue RuntimeHelpers::ordinaryToPrimitive(ExecutionEngine *engine, const Object *object, String *typeHint) +{ + Q_ASSERT(!engine->hasException); String *meth1 = engine->id_toString(); String *meth2 = engine->id_valueOf(); - if (typeHint == NUMBER_HINT) + if (typeHint->identifier() == engine->id_number()->identifier()) { qSwap(meth1, meth2); + } else { + Q_ASSERT(typeHint->identifier() == engine->id_string()->identifier()); + } Scope scope(engine); ScopedValue result(scope); - ScopedValue conv(scope, object->get(meth1)); + ScopedValue conv(scope, object->get(meth1)); if (FunctionObject *o = conv->as<FunctionObject>()) { result = o->call(object, nullptr, 0); if (result->isPrimitive()) @@ -449,7 +481,6 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH } - Heap::Object *RuntimeHelpers::convertToObject(ExecutionEngine *engine, const Value &value) { Q_ASSERT(!value.isObject()); @@ -461,7 +492,9 @@ Heap::Object *RuntimeHelpers::convertToObject(ExecutionEngine *engine, const Val case Value::Boolean_Type: return engine->newBooleanObject(value.booleanValue()); case Value::Managed_Type: - Q_ASSERT(value.isString()); + Q_ASSERT(value.isStringOrSymbol()); + if (!value.isString()) + return engine->newSymbolObject(value.symbolValue()); return engine->newStringObject(value.stringValue()); case Value::Integer_Type: default: // double @@ -488,6 +521,10 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, Value val case Value::Managed_Type: { if (value.isString()) return static_cast<const String &>(value).d(); + if (value.isSymbol()) { + engine->throwTypeError(QLatin1String("Cannot convert a symbol to a string.")); + return nullptr; + } value = Primitive::fromReturnedValue(RuntimeHelpers::toPrimitive(value, hint)); Q_ASSERT(value.isPrimitive()); if (value.isString()) @@ -598,7 +635,7 @@ static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine, Q_ASSERT(!!o); // can't fail as null/undefined is covered above } - ScopedString name(scope, index.toString(engine)); + ScopedStringOrSymbol name(scope, index.toPropertyKey(engine)); if (scope.hasException()) return Encode::undefined(); return o->get(name); @@ -617,7 +654,7 @@ ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value & uint idx = 0; if (index.asArrayIndex(idx)) { if (Heap::Base *b = object.heapObject()) { - if (b->vtable()->isObject) { + if (b->internalClass->vtable->isObject) { Heap::Object *o = static_cast<Heap::Object *>(b); if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); @@ -652,7 +689,9 @@ static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Val return o->putIndexed(idx, value); } - ScopedString name(scope, index.toString(engine)); + ScopedStringOrSymbol name(scope, index.toPropertyKey(engine)); + if (engine->hasException) + return false; return o->put(name, value); } @@ -661,7 +700,7 @@ bool Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, uint idx = 0; if (index.asArrayIndex(idx)) { if (Heap::Base *b = object.heapObject()) { - if (b->vtable()->isObject) { + if (b->internalClass->vtable->isObject) { Heap::Object *o = static_cast<Heap::Object *>(b); if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); @@ -677,25 +716,105 @@ bool Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, return setElementFallback(engine, object, index, value); } -ReturnedValue Runtime::method_foreachIterator(ExecutionEngine *engine, const Value &in) +ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value &in, int iterator) { Scope scope(engine); ScopedObject o(scope, (Object *)nullptr); if (!in.isNullOrUndefined()) o = in.toObject(engine); - return engine->newForEachIteratorObject(o)->asReturnedValue(); + if (engine->hasException) + return Encode::undefined(); + if (iterator) { + if (!o) + return engine->throwTypeError(); + ScopedFunctionObject f(scope, o->get(engine->symbol_iterator())); + if (!f) + return engine->throwTypeError(); + JSCallData cData(scope, 0, nullptr, o); + ScopedObject it(scope, f->call(cData)); + if (!it) + return engine->throwTypeError(); + return it->asReturnedValue(); + } + return engine->newForInIteratorObject(o)->asReturnedValue(); } -ReturnedValue Runtime::method_foreachNextPropertyName(const Value &foreach_iterator) +ReturnedValue Runtime::method_iteratorNext(ExecutionEngine *engine, const Value &iterator, Value *value) { - Q_ASSERT(foreach_iterator.isObject()); + Q_ASSERT(iterator.isObject()); + + Scope scope(engine); + ScopedFunctionObject f(scope, static_cast<const Object &>(iterator).get(engine->id_next())); + if (!f) + return engine->throwTypeError(); + JSCallData cData(scope, 0, nullptr, &iterator); + ScopedObject o(scope, f->call(cData)); + if (!o) + return engine->throwTypeError(); + ScopedValue d(scope, o->get(engine->id_done())); + bool done = d->toBoolean(); + if (done) { + *value = Encode::undefined(); + } else { + *value = o->get(engine->id_value()); + } + return Encode(done); +} - ForEachIteratorObject *it = static_cast<ForEachIteratorObject *>(foreach_iterator.objectValue()); - Q_ASSERT(it->as<ForEachIteratorObject>()); +ReturnedValue Runtime::method_iteratorClose(ExecutionEngine *engine, const Value &iterator, const Value &done) +{ + Q_ASSERT(iterator.isObject()); + Q_ASSERT(done.isBoolean()); + if (done.booleanValue()) + return Encode::undefined(); - return it->nextPropertyName(); + Scope scope(engine); + bool hadException = engine->hasException; + ScopedValue e(scope); + if (hadException) { + e = *engine->exceptionValue; + engine->hasException = false; + } + ScopedFunctionObject f(scope, static_cast<const Object &>(iterator).get(engine->id_return())); + ScopedObject o(scope); + if (f) { + JSCallData cData(scope, 0, nullptr, &iterator); + o = f->call(cData); + } + if (hadException || !f) { + *engine->exceptionValue = e; + engine->hasException = hadException; + return Encode::undefined(); + } + if (engine->hasException) + return Encode::undefined(); + + if (!o) + return engine->throwTypeError(); + return Encode::undefined(); } +ReturnedValue Runtime::method_destructureRestElement(ExecutionEngine *engine, const Value &iterator) +{ + Q_ASSERT(iterator.isObject()); + + Scope scope(engine); + ScopedArrayObject array(scope, engine->newArrayObject()); + array->arrayCreate(); + uint index = 0; + while (1) { + ScopedValue n(scope); + ScopedValue done(scope, method_iteratorNext(engine, iterator, n)); + if (engine->hasException) + return Encode::undefined(); + Q_ASSERT(done->isBoolean()); + if (done->booleanValue()) + break; + array->arraySet(index, n); + ++index; + } + return array->asReturnedValue(); +} void Runtime::method_storeNameSloppy(ExecutionEngine *engine, int nameIndex, const Value &value) { @@ -1033,24 +1152,31 @@ ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, V ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc) { Scope scope(engine); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + ScopedObject lookupObject(scope, base); - if (!base->isObject()) { + if (!lookupObject) { Q_ASSERT(!base->isEmpty()); if (base->isNullOrUndefined()) { QString message = QStringLiteral("Cannot call method '%1' of %2") - .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(), - base->toQStringNoThrow()); + .arg(name->toQString(), base->toQStringNoThrow()); return engine->throwTypeError(message); } - ScopedValue thisObject(scope, RuntimeHelpers::convertToObject(engine, *base)); - if (engine->hasException) // type error - return Encode::undefined(); - base = thisObject; + if (base->isManaged()) { + Managed *m = static_cast<Managed *>(base); + lookupObject = m->internalClass()->prototype; + Q_ASSERT(m->internalClass()->prototype); + } else { + lookupObject = RuntimeHelpers::convertToObject(engine, *base); + if (engine->hasException) // type error + return Encode::undefined(); + if (!engine->currentStackFrame->v4Function->isStrict()) + base = lookupObject; + } } - ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - ScopedFunctionObject f(scope, static_cast<Object *>(base)->get(name)); + ScopedFunctionObject f(scope, static_cast<Object *>(lookupObject)->get(name)); if (!f) { QString error = QStringLiteral("Property '%1' of object %2 is not a function") @@ -1080,7 +1206,7 @@ ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, Value *base, ScopedValue thisObject(scope, base->toObject(engine)); base = thisObject; - ScopedString str(scope, index.toString(engine)); + ScopedStringOrSymbol str(scope, index.toPropertyKey(engine)); if (engine->hasException) return Encode::undefined(); @@ -1130,6 +1256,60 @@ ReturnedValue Runtime::method_callQmlContextObjectProperty(ExecutionEngine *engi return fo->call(qmlContextValue, argv, argc); } +struct CallArgs { + Value *argv; + int argc; +}; + +static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc) +{ + ScopedValue it(scope); + ScopedValue done(scope); + + int argCount = 0; + + Value *v = scope.alloc<Scope::Uninitialized>(); + Value *arguments = v; + for (int i = 0; i < argc; ++i) { + if (!argv[i].isEmpty()) { + *v = argv[i]; + ++argCount; + v = scope.alloc<Scope::Uninitialized>(); + continue; + } + // spread element + ++i; + it = Runtime::method_getIterator(scope.engine, argv[i], /* ForInIterator */ 1); + if (scope.engine->hasException) + return { nullptr, 0 }; + while (1) { + done = Runtime::method_iteratorNext(scope.engine, it, v); + if (scope.engine->hasException) + return { nullptr, 0 }; + Q_ASSERT(done->isBoolean()); + if (done->booleanValue()) + break; + ++argCount; + v = scope.alloc<Scope::Uninitialized>(); + } + } + return { arguments, argCount }; +} + +ReturnedValue Runtime::method_callWithSpread(ExecutionEngine *engine, const Value &function, const Value &thisObject, Value *argv, int argc) +{ + Q_ASSERT(argc >= 1); + if (!function.isFunctionObject()) + return engine->throwTypeError(); + + Scope scope(engine); + CallArgs arguments = createSpreadArguments(scope, argv, argc); + if (engine->hasException) + return Encode::undefined(); + + return static_cast<const FunctionObject &>(function).call(&thisObject, arguments.argv, arguments.argc); +} + ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &function, Value *argv, int argc) { if (!function.isFunctionObject()) @@ -1138,6 +1318,20 @@ ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &fu return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc); } +ReturnedValue Runtime::method_constructWithSpread(ExecutionEngine *engine, const Value &function, Value *argv, int argc) +{ + Q_UNIMPLEMENTED(); + if (!function.isFunctionObject()) + return engine->throwTypeError(); + + Scope scope(engine); + CallArgs arguments = createSpreadArguments(scope, argv, argc); + if (engine->hasException) + return Encode::undefined(); + + return static_cast<const FunctionObject &>(function).callAsConstructor(arguments.argv, arguments.argc); +} + void Runtime::method_throwException(ExecutionEngine *engine, const Value &value) { if (!value.isEmpty()) @@ -1161,6 +1355,8 @@ ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value & case Value::Managed_Type: if (value.isString()) res = engine->id_string(); + else if (value.isSymbol()) + res = engine->id_symbol(); else if (value.objectValue()->as<FunctionObject>()) res = engine->id_function(); else @@ -1183,25 +1379,54 @@ QV4::ReturnedValue Runtime::method_typeofName(ExecutionEngine *engine, int nameI return method_typeofValue(engine, prop); } -/* The next three methods are a bit tricky. They can't open up a Scope, as that - * would mess up the pushing of the context. - * - * Instead the push/pop pair acts as a non local scope. - */ -ReturnedValue Runtime::method_createWithContext(ExecutionContext *parent, const Value &o) +ReturnedValue Runtime::method_createWithContext(ExecutionEngine *engine, Value *jsStackFrame) { - Q_ASSERT(o.isObject()); - const Object &obj = static_cast<const Object &>(o); - return parent->newWithContext(obj.d())->asReturnedValue(); + QV4::Value &accumulator = jsStackFrame[CallData::Accumulator]; + accumulator = accumulator.toObject(engine); + if (engine->hasException) + return Encode::undefined(); + Q_ASSERT(accumulator.isObject()); + const Object &obj = static_cast<const Object &>(accumulator); + ExecutionContext *context = static_cast<ExecutionContext *>(jsStackFrame + CallData::Context); + return context->newWithContext(obj.d())->asReturnedValue(); +} + +ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex) +{ + ExecutionEngine *e = parent->engine(); + return parent->newCatchContext(e->currentStackFrame, blockIndex, + e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex])->asReturnedValue(); } -ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int exceptionVarNameIndex) +ReturnedValue Runtime::method_createBlockContext(ExecutionContext *parent, int index) { ExecutionEngine *e = parent->engine(); - return parent->newCatchContext(e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex], - e->catchException(nullptr))->asReturnedValue(); + return parent->newBlockContext(e->currentStackFrame, index)->asReturnedValue(); +} + +ReturnedValue Runtime::method_cloneBlockContext(ExecutionContext *previous) +{ + return ExecutionContext::cloneBlockContext(static_cast<Heap::CallContext *>(previous->d()))->asReturnedValue(); +} + + +ReturnedValue Runtime::method_createScriptContext(ExecutionEngine *engine, int index) +{ + Q_ASSERT(engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_GlobalContext || + engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_QmlContext); + ReturnedValue c = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue(); + engine->setScriptContext(c); + return c; +} + +ReturnedValue Runtime::method_popScriptContext(ExecutionEngine *engine) +{ + ReturnedValue root = engine->rootContext()->asReturnedValue(); + engine->setScriptContext(root); + return root; } + void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex) { Scope scope(engine); @@ -1214,61 +1439,71 @@ ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *value return engine->newArrayObject(values, length)->asReturnedValue(); } -ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4::Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags) +ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId, int argc, const QV4::Value *args) { Scope scope(engine); - QV4::InternalClass *klass = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeClasses[classId]; - ScopedObject o(scope, engine->newObject(klass, engine->objectPrototype())); + Scoped<InternalClass> klass(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeClasses[classId]); + ScopedObject o(scope, engine->newObject(klass->d())); - { - bool needSparseArray = arrayGetterSetterCountAndFlags >> 30; - if (needSparseArray) - o->initSparseArray(); - } + Q_ASSERT(uint(argc) >= klass->d()->size); - for (uint i = 0; i < klass->size; ++i) + for (uint i = 0; i < klass->d()->size; ++i) o->setProperty(i, *args++); - if (arrayValueCount > 0) { - ScopedValue entry(scope); - for (int i = 0; i < arrayValueCount; ++i) { - uint idx = args->toUInt32(); - ++args; - entry = *args++; - o->arraySet(idx, entry); - } - } + Q_ASSERT((argc - klass->d()->size) % 3 == 0); + int additionalArgs = (argc - int(klass->d()->size))/3; + + if (!additionalArgs) + return o->asReturnedValue(); - uint arrayGetterSetterCount = arrayGetterSetterCountAndFlags & ((1 << 30) - 1); - if (arrayGetterSetterCount > 0) { - ScopedProperty pd(scope); - for (uint i = 0; i < arrayGetterSetterCount; ++i) { - uint idx = args->toUInt32(); - ++args; - pd->value = *args; - ++args; - pd->set = *args; - ++args; - o->arraySet(idx, pd, Attr_Accessor); + Scoped<StringOrSymbol> name(scope); + ScopedProperty pd(scope); + for (int i = 0; i < additionalArgs; ++i) { + Q_ASSERT(args->isInteger()); + ObjectLiteralArgument arg = ObjectLiteralArgument(args->integerValue()); + name = args[1].toPropertyKey(engine); + if (engine->hasException) + return Encode::undefined(); + Q_ASSERT(arg == ObjectLiteralArgument::Value || args[2].isFunctionObject()); + if (arg == ObjectLiteralArgument::Value || arg == ObjectLiteralArgument::Getter) { + pd->value = args[2]; + pd->set = Primitive::emptyValue(); + } else { + pd->value = Primitive::emptyValue(); + pd->set = args[2]; } - } + bool ok = o->__defineOwnProperty__(scope.engine, name, pd, (arg == ObjectLiteralArgument::Value ? Attr_Data : Attr_Accessor)); + if (!ok) + return engine->throwTypeError(); + args += 3; + } return o.asReturnedValue(); } QV4::ReturnedValue Runtime::method_createMappedArgumentsObject(ExecutionEngine *engine) { Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_CallContext); - QV4::InternalClass *ic = engine->internalClasses[EngineBase::Class_ArgumentsObject]; - return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->objectPrototype(), engine->currentStackFrame)->asReturnedValue(); + Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_ArgumentsObject); + return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue(); } QV4::ReturnedValue Runtime::method_createUnmappedArgumentsObject(ExecutionEngine *engine) { - QV4::InternalClass *ic = engine->internalClasses[EngineBase::Class_StrictArgumentsObject]; - return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->objectPrototype(), engine->currentStackFrame)->asReturnedValue(); + Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_StrictArgumentsObject); + return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue(); } +QV4::ReturnedValue Runtime::method_createRestParameter(ExecutionEngine *engine, int argIndex) +{ + const Value *values = engine->currentStackFrame->originalArguments + argIndex; + int nValues = engine->currentStackFrame->originalArgumentsCount - argIndex; + if (nValues <= 0) + return engine->newArrayObject(0)->asReturnedValue(); + return engine->newArrayObject(values, nValues)->asReturnedValue(); +} + + ReturnedValue Runtime::method_loadQmlContext(NoThrowEngine *engine) { Heap::QmlContext *ctx = engine->qmlContext(); @@ -1485,24 +1720,133 @@ ReturnedValue Runtime::method_lessEqual(const Value &left, const Value &right) return Encode(r); } +struct LazyScope +{ + ExecutionEngine *engine = nullptr; + Value *stackMark = nullptr; + ~LazyScope() { + if (engine) + engine->jsStackTop = stackMark; + } + template <typename T> + void set(Value **scopedValue, T value, ExecutionEngine *e) { + if (!engine) { + engine = e; + stackMark = engine->jsStackTop; + } + if (!*scopedValue) + *scopedValue = e->jsAlloca(1); + **scopedValue = value; + } +}; + Bool Runtime::method_compareEqual(const Value &left, const Value &right) { TRACE2(left, right); - if (left.rawValue() == right.rawValue()) - // NaN != NaN - return !left.isNaN(); + Value lhs = left; + Value rhs = right; - if (left.type() == right.type()) { - if (left.isDouble() && left.doubleValue() == 0 && right.doubleValue() == 0) - return true; // this takes care of -0 == +0 (which obviously have different raw values) - if (!left.isManaged()) - return false; - if (left.isString() == right.isString()) - return left.cast<Managed>()->isEqualTo(right.cast<Managed>()); +#ifndef V4_BOOTSTRAP + LazyScope scope; + Value *lhsGuard = nullptr; + Value *rhsGuard = nullptr; +#endif + + redo: + if (lhs.asReturnedValue() == rhs.asReturnedValue()) + return !lhs.isNaN(); + + int lt = lhs.quickType(); + int rt = rhs.quickType(); + if (rt < lt) { + qSwap(lhs, rhs); + qSwap(lt, rt); } - return RuntimeHelpers::equalHelper(left, right); + switch (lt) { + case QV4::Value::QT_ManagedOrUndefined: + if (lhs.isUndefined()) + return rhs.isNullOrUndefined(); + Q_FALLTHROUGH(); + case QV4::Value::QT_ManagedOrUndefined1: + case QV4::Value::QT_ManagedOrUndefined2: + case QV4::Value::QT_ManagedOrUndefined3: + // LHS: Managed + switch (rt) { + case QV4::Value::QT_ManagedOrUndefined: + if (rhs.isUndefined()) + return false; + Q_FALLTHROUGH(); + case QV4::Value::QT_ManagedOrUndefined1: + case QV4::Value::QT_ManagedOrUndefined2: + case QV4::Value::QT_ManagedOrUndefined3: { +#ifndef V4_BOOTSTRAP + // RHS: Managed + Heap::Base *l = lhs.m(); + Heap::Base *r = rhs.m(); + Q_ASSERT(l); + Q_ASSERT(r); + if (l->internalClass->vtable->isStringOrSymbol == r->internalClass->vtable->isStringOrSymbol) + return static_cast<QV4::Managed &>(lhs).isEqualTo(&static_cast<QV4::Managed &>(rhs)); + if (l->internalClass->vtable->isStringOrSymbol) { + scope.set(&rhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(rhs), PREFERREDTYPE_HINT), r->internalClass->engine); + rhs = rhsGuard->asReturnedValue(); + break; + } else { + Q_ASSERT(r->internalClass->vtable->isStringOrSymbol); + scope.set(&lhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT), l->internalClass->engine); + lhs = lhsGuard->asReturnedValue(); + break; + } +#endif + return false; + } + case QV4::Value::QT_Empty: + Q_UNREACHABLE(); + case QV4::Value::QT_Null: + return false; + case QV4::Value::QT_Bool: + case QV4::Value::QT_Int: + rhs = Primitive::fromDouble(rhs.int_32()); + // fall through + default: // double +#ifndef V4_BOOTSTRAP + if (lhs.m()->internalClass->vtable->isStringOrSymbol) { + return lhs.m()->internalClass->vtable->isString ? (RuntimeHelpers::toNumber(lhs) == rhs.doubleValue()) : false; + } else { + scope.set(&lhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT), lhs.m()->internalClass->engine); + lhs = lhsGuard->asReturnedValue(); + } +#else + Q_UNIMPLEMENTED(); +#endif + } + goto redo; + case QV4::Value::QT_Empty: + Q_UNREACHABLE(); + case QV4::Value::QT_Null: + return rhs.isNull(); + case QV4::Value::QT_Bool: + case QV4::Value::QT_Int: + switch (rt) { + case QV4::Value::QT_ManagedOrUndefined: + case QV4::Value::QT_ManagedOrUndefined1: + case QV4::Value::QT_ManagedOrUndefined2: + case QV4::Value::QT_ManagedOrUndefined3: + case QV4::Value::QT_Empty: + case QV4::Value::QT_Null: + Q_UNREACHABLE(); + case QV4::Value::QT_Bool: + case QV4::Value::QT_Int: + return lhs.int_32() == rhs.int_32(); + default: // double + return lhs.int_32() == rhs.doubleValue(); + } + default: // double + Q_ASSERT(rhs.isDouble()); + return lhs.doubleValue() == rhs.doubleValue(); + } } ReturnedValue Runtime::method_equal(const Value &left, const Value &right) diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index 3a26c23990..72af90d1dc 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -100,6 +100,7 @@ enum TypeHint { struct Q_QML_PRIVATE_EXPORT RuntimeHelpers { static ReturnedValue objectDefaultValue(const Object *object, int typeHint); static ReturnedValue toPrimitive(const Value &value, TypeHint typeHint); + static ReturnedValue ordinaryToPrimitive(ExecutionEngine *engine, const Object *object, String *typeHint); static double stringToNumber(const QString &s); static Heap::String *stringFromNumber(ExecutionEngine *engine, double number); diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index 91232256a9..09f6a65dde 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -99,9 +99,11 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { F(ReturnedValue, callElement, (ExecutionEngine *engine, Value *base, const Value &index, Value *argv, int argc)) \ F(ReturnedValue, callValue, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \ F(ReturnedValue, callPossiblyDirectEval, (ExecutionEngine *engine, Value *argv, int argc)) \ + F(ReturnedValue, callWithSpread, (ExecutionEngine *engine, const Value &func, const Value &thisObject, Value *argv, int argc)) \ \ /* construct */ \ F(ReturnedValue, construct, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \ + F(ReturnedValue, constructWithSpread, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \ \ /* load & store */ \ F(void, storeNameStrict, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ @@ -117,15 +119,17 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { F(ReturnedValue, typeofName, (ExecutionEngine *engine, int nameIndex)) \ \ /* delete */ \ - F(bool, deleteElement, (ExecutionEngine *engine, const Value &base, const Value &index)) \ - F(bool, deleteMember, (ExecutionEngine *engine, const Value &base, int nameIndex)) \ - F(bool, deleteMemberString, (ExecutionEngine *engine, const Value &base, String *name)) \ + F(bool, deleteProperty, (ExecutionEngine *engine, const Value &base, const Value &index)) \ F(bool, deleteName, (ExecutionEngine *engine, int nameIndex)) \ \ /* exceptions & scopes */ \ F(void, throwException, (ExecutionEngine *engine, const Value &value)) \ - F(ReturnedValue, createWithContext, (ExecutionContext *parent, const Value &o)) \ - F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int exceptionVarNameIndex)) \ + F(ReturnedValue, createWithContext, (ExecutionEngine *, Value *jsStackFrame)) \ + F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex)) \ + F(ReturnedValue, createBlockContext, (ExecutionContext *parent, int index)) \ + F(ReturnedValue, createScriptContext, (ExecutionEngine *engine, int index)) \ + F(ReturnedValue, cloneBlockContext, (ExecutionContext *previous)) \ + F(ReturnedValue, popScriptContext, (ExecutionEngine *engine)) \ \ /* closures */ \ F(ReturnedValue, closure, (ExecutionEngine *engine, int functionId)) \ @@ -134,14 +138,17 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { F(void, declareVar, (ExecutionEngine *engine, bool deletable, int nameIndex)) \ F(ReturnedValue, createMappedArgumentsObject, (ExecutionEngine *engine)) \ F(ReturnedValue, createUnmappedArgumentsObject, (ExecutionEngine *engine)) \ + F(ReturnedValue, createRestParameter, (ExecutionEngine *engine, int argIndex)) \ \ /* literals */ \ F(ReturnedValue, arrayLiteral, (ExecutionEngine *engine, Value *values, uint length)) \ - F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, const Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)) \ + F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, int classId, int argc, const Value *args)) \ \ - /* foreach */ \ - F(ReturnedValue, foreachIterator, (ExecutionEngine *engine, const Value &in)) \ - F(ReturnedValue, foreachNextPropertyName, (const Value &foreach_iterator)) \ + /* for-in, for-of and array destructuring */ \ + F(ReturnedValue, getIterator, (ExecutionEngine *engine, const Value &in, int iterator)) \ + F(ReturnedValue, iteratorNext, (ExecutionEngine *engine, const Value &iterator, Value *value)) \ + F(ReturnedValue, iteratorClose, (ExecutionEngine *engine, const Value &iterator, const Value &done)) \ + F(ReturnedValue, destructureRestElement, (ExecutionEngine *engine, const Value &iterator)) \ \ /* unary operators */ \ F(ReturnedValue, uMinus, (const Value &value)) \ diff --git a/src/qml/jsruntime/qv4runtimecodegen.cpp b/src/qml/jsruntime/qv4runtimecodegen.cpp index fe18ddf9ed..9866966936 100644 --- a/src/qml/jsruntime/qv4runtimecodegen.cpp +++ b/src/qml/jsruntime/qv4runtimecodegen.cpp @@ -52,13 +52,16 @@ void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName, _module->finalUrl = fileName; _context = nullptr; - Compiler::ScanFunctions scan(this, sourceCode, Compiler::GlobalCode); + Compiler::ScanFunctions scan(this, sourceCode, Compiler::ContextType::Global); // fake a global environment - scan.enterEnvironment(nullptr, Compiler::FunctionCode); + scan.enterEnvironment(nullptr, Compiler::ContextType::Function, QString()); scan(ast); scan.leaveEnvironment(); - int index = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : nullptr); + if (hasError) + return; + + int index = defineFunction(ast->name.toString(), ast, ast->formals, ast->body); _module->rootContext = _module->functions.at(index); } diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index bb20f384b3..73ee17cd40 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -117,8 +117,45 @@ struct Scope { engine->jsStackTop = mark; } - QML_NEARLY_ALWAYS_INLINE Value *alloc(int nValues) const { - return engine->jsAlloca(nValues); + enum AllocMode { + Undefined, + Empty, + /* Be careful when using Uninitialized, the stack has to be fully initialized before calling into the memory manager again */ + Uninitialized + }; + template <AllocMode mode = Undefined> + QML_NEARLY_ALWAYS_INLINE Value *alloc(int nValues) const + { + Value *ptr = engine->jsAlloca(nValues); + switch (mode) { + case Undefined: + for (int i = 0; i < nValues; ++i) + ptr[i] = Primitive::undefinedValue(); + break; + case Empty: + for (int i = 0; i < nValues; ++i) + ptr[i] = Primitive::emptyValue(); + break; + case Uninitialized: + break; + } + return ptr; + } + template <AllocMode mode = Undefined> + QML_NEARLY_ALWAYS_INLINE Value *alloc() const + { + Value *ptr = engine->jsAlloca(1); + switch (mode) { + case Undefined: + *ptr = Primitive::undefinedValue(); + break; + case Empty: + *ptr = Primitive::emptyValue(); + break; + case Uninitialized: + break; + } + return ptr; } bool hasException() const { @@ -136,31 +173,31 @@ struct ScopedValue { ScopedValue(const Scope &scope) { - ptr = scope.engine->jsStackTop++; + ptr = scope.alloc<Scope::Uninitialized>(); ptr->setRawValue(0); } ScopedValue(const Scope &scope, const Value &v) { - ptr = scope.engine->jsStackTop++; + ptr = scope.alloc<Scope::Uninitialized>(); *ptr = v; } ScopedValue(const Scope &scope, Heap::Base *o) { - ptr = scope.engine->jsStackTop++; + ptr = scope.alloc<Scope::Uninitialized>(); ptr->setM(o); } ScopedValue(const Scope &scope, Managed *m) { - ptr = scope.engine->jsStackTop++; + ptr = scope.alloc<Scope::Uninitialized>(); ptr->setRawValue(m->asReturnedValue()); } ScopedValue(const Scope &scope, const ReturnedValue &v) { - ptr = scope.engine->jsStackTop++; + ptr = scope.alloc<Scope::Uninitialized>(); ptr->setRawValue(v); } @@ -214,66 +251,66 @@ struct Scoped QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Undefined>(); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value &v) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); setPointer(v.as<T>()); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, Heap::Base *o) { Value v; v = o; - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); setPointer(v.as<T>()); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ScopedValue &v) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); setPointer(v.ptr->as<T>()); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value &v, ConvertType) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); ptr->setRawValue(value_convert<T>(scope.engine, v)); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value *v) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); setPointer(v ? v->as<T>() : nullptr); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, T *t) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); setPointer(t); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const T *t) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); setPointer(t); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, typename T::Data *t) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); *ptr = t; } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ReturnedValue &v) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); setPointer(QV4::Value::fromReturnedValue(v).as<T>()); } QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ReturnedValue &v, ConvertType) { - ptr = scope.engine->jsAlloca(1); + ptr = scope.alloc<Scope::Uninitialized>(); ptr->setRawValue(value_convert<T>(scope.engine, QV4::Value::fromReturnedValue(v))); } diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 5cd62c90f1..efd528860f 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -60,7 +60,7 @@ using namespace QV4; -Script::Script(ExecutionEngine *v4, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit) +Script::Script(ExecutionEngine *v4, QmlContext *qml, const QQmlRefPointer<CompiledData::CompilationUnit> &compilationUnit) : line(1), column(0), context(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false) , compilationUnit(compilationUnit), vmFunction(nullptr), parseAsBinding(true) { @@ -128,7 +128,7 @@ void Script::parse() RuntimeCodegen cg(v4, &jsGenerator, strictMode); if (inheritContext) cg.setUseFastLookups(false); - cg.generateFromProgram(sourceFile, sourceFile, sourceCode, program, &module, compilationMode); + cg.generateFromProgram(sourceFile, sourceFile, sourceCode, program, &module, contextType); if (v4->hasException) return; @@ -171,19 +171,16 @@ Function *Script::function() return vmFunction; } -QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator, +QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator, const QString &fileName, const QString &finalUrl, const QString &source, - QList<QQmlError> *reportedErrors, Directives *directivesCollector) + QList<QQmlError> *reportedErrors) { using namespace QV4::Compiler; using namespace QQmlJS::AST; - Engine ee; - if (directivesCollector) - ee.setDirectives(directivesCollector); - Lexer lexer(&ee); + Lexer lexer(jsEngine); lexer.setCode(source, /*line*/1, /*qml mode*/false); - Parser parser(&ee); + Parser parser(jsEngine); parser.parseProgram(); @@ -219,7 +216,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compi Codegen cg(unitGenerator, /*strict mode*/false); cg.setUseFastLookups(false); - cg.generateFromProgram(fileName, finalUrl, source, program, module, GlobalCode); + cg.generateFromProgram(fileName, finalUrl, source, program, module, ContextType::Global); errors = cg.qmlErrors(); if (!errors.isEmpty()) { if (reportedErrors) diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index b4ac150044..e7189664e2 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -62,12 +62,16 @@ QT_BEGIN_NAMESPACE class QQmlContextData; +namespace QQmlJS { +class Engine; +} + namespace QV4 { struct Q_QML_EXPORT Script { - Script(ExecutionContext *scope, QV4::Compiler::CompilationMode mode, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) + Script(ExecutionContext *scope, QV4::Compiler::ContextType mode, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) : sourceFile(source), line(line), column(column), sourceCode(sourceCode) - , context(scope), strictMode(false), inheritContext(false), parsed(false), compilationMode(mode) + , context(scope), strictMode(false), inheritContext(false), parsed(false), contextType(mode) , vmFunction(nullptr), parseAsBinding(false) {} Script(ExecutionEngine *engine, QmlContext *qml, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) : sourceFile(source), line(line), column(column), sourceCode(sourceCode) @@ -76,7 +80,7 @@ struct Q_QML_EXPORT Script { if (qml) qmlContext.set(engine, *qml); } - Script(ExecutionEngine *engine, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit); + Script(ExecutionEngine *engine, QmlContext *qml, const QQmlRefPointer<CompiledData::CompilationUnit> &compilationUnit); ~Script(); QString sourceFile; int line; @@ -86,7 +90,7 @@ struct Q_QML_EXPORT Script { bool strictMode; bool inheritContext; bool parsed; - QV4::Compiler::CompilationMode compilationMode = QV4::Compiler::EvalCode; + QV4::Compiler::ContextType contextType = QV4::Compiler::ContextType::Eval; QV4::PersistentValue qmlContext; QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit; Function *vmFunction; @@ -97,10 +101,9 @@ struct Q_QML_EXPORT Script { Function *function(); - static QQmlRefPointer<CompiledData::CompilationUnit> precompile( - QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator, + static QQmlRefPointer<CompiledData::CompilationUnit> precompile(QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator, const QString &fileName, const QString &finalUrl, const QString &source, - QList<QQmlError> *reportedErrors = nullptr, QQmlJS::Directives *directivesCollector = nullptr); + QList<QQmlError> *reportedErrors = nullptr); static Script *createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlContext, const QString &fileName, const QUrl &originalUrl, QString *error); static ReturnedValue evaluate(ExecutionEngine *engine, const QString &script, QmlContext *qmlContext); diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 7d29d0b517..f0aa39b786 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -227,7 +227,7 @@ namespace Heap { template <typename Container> struct QQmlSequence : Object { void init(const Container &container); - void init(QObject *object, int propertyIndex); + void init(QObject *object, int propertyIndex, bool readOnly); void destroy() { delete container; object.destroy(); @@ -237,7 +237,8 @@ struct QQmlSequence : Object { mutable Container *container; QQmlQPointer<QObject> object; int propertyIndex; - bool isReference; + bool isReference : 1; + bool isReadOnly : 1; }; } @@ -294,6 +295,9 @@ public: return false; } + if (d()->isReadOnly) + return false; + if (d()->isReference) { if (!d()->object) return false; @@ -366,6 +370,8 @@ public: /* Qt containers have int (rather than uint) allowable indexes. */ if (index > INT_MAX) return false; + if (d()->isReadOnly) + return false; if (d()->isReference) { if (!d()->object) return false; @@ -432,11 +438,13 @@ public: const QV4::Value *m_compareFn; }; - void sort(const FunctionObject *f, const Value *, const Value *argv, int argc) + bool sort(const FunctionObject *f, const Value *, const Value *argv, int argc) { + if (d()->isReadOnly) + return false; if (d()->isReference) { if (!d()->object) - return; + return false; loadReference(); } @@ -450,6 +458,8 @@ public: if (d()->isReference) storeReference(); + + return true; } static QV4::ReturnedValue method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int) @@ -480,6 +490,10 @@ public: generateWarning(scope.engine, QLatin1String("Index out of range during length set")); RETURN_UNDEFINED(); } + + if (This->d()->isReadOnly) + THROW_TYPE_ERROR(); + /* Read the sequence from the QObject property if we're a reference */ if (This->d()->isReference) { if (!This->d()->object) @@ -572,6 +586,7 @@ void Heap::QQmlSequence<Container>::init(const Container &container) this->container = new Container(container); propertyIndex = -1; isReference = false; + isReadOnly = false; object.init(); QV4::Scope scope(internalClass->engine); @@ -581,12 +596,13 @@ void Heap::QQmlSequence<Container>::init(const Container &container) } template <typename Container> -void Heap::QQmlSequence<Container>::init(QObject *object, int propertyIndex) +void Heap::QQmlSequence<Container>::init(QObject *object, int propertyIndex, bool readOnly) { Object::init(); this->container = new Container; this->propertyIndex = propertyIndex; isReference = true; + this->isReadOnly = readOnly; this->object.init(object); QV4::Scope scope(internalClass->engine); QV4::Scoped<QV4::QQmlSequence<Container> > o(scope, this); @@ -668,7 +684,8 @@ ReturnedValue SequencePrototype::method_sort(const FunctionObject *b, const Valu #define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \ if (QQml##SequenceElementTypeName##List *s = o->as<QQml##SequenceElementTypeName##List>()) { \ - s->sort(b, thisObject, argv, argc); \ + if (!s->sort(b, thisObject, argv, argc)) \ + THROW_TYPE_ERROR(); \ } else FOREACH_QML_SEQUENCE_TYPE(CALL_SORT) @@ -691,11 +708,11 @@ bool SequencePrototype::isSequenceType(int sequenceTypeId) #define NEW_REFERENCE_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ if (sequenceType == qMetaTypeId<SequenceType>()) { \ - QV4::ScopedObject obj(scope, engine->memoryManager->allocObject<QQml##ElementTypeName##List>(object, propertyIndex)); \ + QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QQml##ElementTypeName##List>(object, propertyIndex, readOnly)); \ return obj.asReturnedValue(); \ } else -ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int sequenceType, QObject *object, int propertyIndex, bool *succeeded) +ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int sequenceType, QObject *object, int propertyIndex, bool readOnly, bool *succeeded) { QV4::Scope scope(engine); // This function is called when the property is a QObject Q_PROPERTY of @@ -709,7 +726,7 @@ ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int s #define NEW_COPY_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ if (sequenceType == qMetaTypeId<SequenceType>()) { \ - QV4::ScopedObject obj(scope, engine->memoryManager->allocObject<QQml##ElementTypeName##List>(v.value<SequenceType >())); \ + QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QQml##ElementTypeName##List>(v.value<SequenceType >())); \ return obj.asReturnedValue(); \ } else diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index e9bef2f604..da71215bed 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -59,6 +59,8 @@ #include "qv4context_p.h" #include "qv4string_p.h" +QT_REQUIRE_CONFIG(qml_sequence_object); + QT_BEGIN_NAMESPACE namespace QV4 { @@ -72,7 +74,7 @@ struct SequencePrototype : public QV4::Object static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static bool isSequenceType(int sequenceTypeId); - static ReturnedValue newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded); + static ReturnedValue newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool readOnly, bool *succeeded); static ReturnedValue fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded); static int metaTypeForSequence(const Object *object); static QVariant toVariant(Object *object); diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp index 14def49d0a..31b51cbfe3 100644 --- a/src/qml/jsruntime/qv4serialize.cpp +++ b/src/qml/jsruntime/qv4serialize.cpp @@ -40,13 +40,17 @@ #include "qv4serialize_p.h" #include <private/qv8engine_p.h> +#if QT_CONFIG(qml_list_model) #include <private/qqmllistmodel_p.h> #include <private/qqmllistmodelworkeragent_p.h> +#endif #include <private/qv4value_p.h> #include <private/qv4dateobject_p.h> #include <private/qv4regexpobject_p.h> +#if QT_CONFIG(qml_sequence_object) #include <private/qv4sequenceobject_p.h> +#endif #include <private/qv4objectproto_p.h> #include <private/qv4qobjectwrapper_p.h> @@ -82,8 +86,12 @@ enum Type { WorkerNumber, WorkerDate, WorkerRegexp, +#if QT_CONFIG(qml_list_model) WorkerListModel, +#endif +#if QT_CONFIG(qml_sequence_object) WorkerSequence +#endif }; static inline quint32 valueheader(Type type, quint32 size = 0) @@ -228,6 +236,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine } else if (const QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) { // XXX TODO: Generalize passing objects between the main thread and worker scripts so // that others can trivially plug in their elements. +#if QT_CONFIG(qml_list_model) QQmlListModel *lm = qobject_cast<QQmlListModel *>(qobjectWrapper->object()); if (lm && lm->agent()) { QQmlListModelWorkerAgent *agent = lm->agent(); @@ -236,9 +245,13 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine push(data, (void *)agent); return; } +#else + Q_UNUSED(qobjectWrapper); +#endif // No other QObject's are allowed to be sent push(data, valueheader(WorkerUndefined)); } else if (const Object *o = v.as<Object>()) { +#if QT_CONFIG(qml_sequence_object) if (o->isListType()) { // valid sequence. we generate a length (sequence length + 1 for the sequence type) uint seqLength = ScopedValue(scope, o->get(engine->id_length()))->toUInt32(); @@ -256,6 +269,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine return; } +#endif // regular object QV4::ScopedValue val(scope, v); @@ -353,6 +367,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) data += ALIGN(length * sizeof(quint16)); return Encode(engine->newRegExpObject(pattern, flags)); } +#if QT_CONFIG(qml_list_model) case WorkerListModel: { void *ptr = popPtr(data); @@ -369,6 +384,8 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) agent->setEngine(engine); return rv->asReturnedValue(); } +#endif +#if QT_CONFIG(qml_sequence_object) case WorkerSequence: { ScopedValue value(scope); @@ -387,6 +404,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) QVariant seqVariant = QV4::SequencePrototype::toVariant(array, sequenceType, &succeeded); return QV4::SequencePrototype::fromVariant(engine, seqVariant, &succeeded); } +#endif } Q_ASSERT(!"Unreachable"); return QV4::Encode::undefined(); diff --git a/src/qml/jsruntime/qv4setiterator.cpp b/src/qml/jsruntime/qv4setiterator.cpp new file mode 100644 index 0000000000..715a4e30d5 --- /dev/null +++ b/src/qml/jsruntime/qv4setiterator.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qv4iterator_p.h> +#include <private/qv4setiterator_p.h> +#include <private/qv4setobject_p.h> +#include <private/qv4symbol_p.h> + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(SetIteratorObject); + +void SetIteratorPrototype::init(ExecutionEngine *e) +{ + defineDefaultProperty(QStringLiteral("next"), method_next, 0); + + Scope scope(e); + ScopedString val(scope, e->newString(QLatin1String("Set Iterator"))); + defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val); +} + +ReturnedValue SetIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int) +{ + Scope scope(b); + const SetIteratorObject *thisObject = that->as<SetIteratorObject>(); + if (!thisObject) + return scope.engine->throwTypeError(QLatin1String("Not a Set Iterator instance")); + + Scoped<SetObject> s(scope, thisObject->d()->iteratedSet); + quint32 index = thisObject->d()->setNextIndex; + IteratorKind itemKind = thisObject->d()->iterationKind; + + + if (!s) { + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); + } + + Scoped<ArrayObject> arr(scope, s->d()->setArray); + + while (index < arr->getLength()) { + ScopedValue e(scope, arr->getIndexed(index)); + index += 1; + thisObject->d()->setNextIndex = index; + + if (itemKind == KeyValueIteratorKind) { + // Return CreateIterResultObject(CreateArrayFromList(« e, e »), false). + ScopedArrayObject resultArray(scope, scope.engine->newArrayObject()); + resultArray->arrayReserve(2); + resultArray->arrayPut(0, e); + resultArray->arrayPut(1, e); + resultArray->setArrayLengthUnchecked(2); + + return IteratorPrototype::createIterResultObject(scope.engine, resultArray, false); + } + + return IteratorPrototype::createIterResultObject(scope.engine, e, false); + } + + thisObject->d()->iteratedSet.set(scope.engine, nullptr); + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); +} + diff --git a/src/qml/jsruntime/qv4setiterator_p.h b/src/qml/jsruntime/qv4setiterator_p.h new file mode 100644 index 0000000000..78eda6d57b --- /dev/null +++ b/src/qml/jsruntime/qv4setiterator_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4SETITERATOR_P_H +#define QV4SETITERATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" +#include "qv4iterator_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Heap { + +#define SetIteratorObjectMembers(class, Member) \ + Member(class, Pointer, Object *, iteratedSet) \ + Member(class, NoMark, IteratorKind, iterationKind) \ + Member(class, NoMark, quint32, setNextIndex) + +DECLARE_HEAP_OBJECT(SetIteratorObject, Object) { + DECLARE_MARKOBJECTS(SetIteratorObject); + void init(Object *obj, QV4::ExecutionEngine *engine) + { + Object::init(); + this->iteratedSet.set(engine, obj); + this->setNextIndex = 0; + } +}; + +} + +struct SetIteratorPrototype : Object +{ + V4_PROTOTYPE(iteratorPrototype) + void init(ExecutionEngine *engine); + + static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); +}; + +struct SetIteratorObject : Object +{ + V4_OBJECT2(SetIteratorObject, Object) + Q_MANAGED_TYPE(SetIteratorObject) + V4_PROTOTYPE(setIteratorPrototype) + + void init(ExecutionEngine *engine); +}; + + +} + +QT_END_NAMESPACE + +#endif // QV4SETITERATOR_P_H + diff --git a/src/qml/jsruntime/qv4setobject.cpp b/src/qml/jsruntime/qv4setobject.cpp new file mode 100644 index 0000000000..4245cd2ea3 --- /dev/null +++ b/src/qml/jsruntime/qv4setobject.cpp @@ -0,0 +1,297 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qv4setobject_p.h" +#include "qv4setiterator_p.h" +#include "qv4symbol_p.h" + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(SetCtor); +DEFINE_OBJECT_VTABLE(SetObject); + +void Heap::SetCtor::init(QV4::ExecutionContext *scope) +{ + Heap::FunctionObject::init(scope, QStringLiteral("Set")); +} + +ReturnedValue SetCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +{ + Scope scope(f); + Scoped<SetObject> a(scope, scope.engine->memoryManager->allocate<SetObject>()); + Scoped<ArrayObject> arr(scope, scope.engine->newArrayObject()); + a->d()->setArray.set(scope.engine, arr->d()); + + if (argc > 0) { + ScopedValue iterable(scope, argv[0]); + if (!iterable->isUndefined() && !iterable->isNull()) { + ScopedFunctionObject adder(scope, a->get(ScopedString(scope, scope.engine->newString(QString::fromLatin1("add"))))); + if (!adder) { + return scope.engine->throwTypeError(); + } + ScopedObject iter(scope, Runtime::method_getIterator(scope.engine, iterable, true)); + CHECK_EXCEPTION(); + if (!iter) { + return a.asReturnedValue(); + } + + Value *nextValue = scope.alloc(1); + ScopedValue done(scope); + forever { + done = Runtime::method_iteratorNext(scope.engine, iter, nextValue); + CHECK_EXCEPTION(); + if (done->toBoolean()) { + return a.asReturnedValue(); + } + + adder->call(a, nextValue, 1); + if (scope.engine->hasException) { + ScopedValue falsey(scope, Encode(false)); + return Runtime::method_iteratorClose(scope.engine, iter, falsey); + } + } + } + } + + return a.asReturnedValue(); +} + +ReturnedValue SetCtor::call(const FunctionObject *f, const Value *, const Value *, int) +{ + Scope scope(f); + return scope.engine->throwTypeError(QString::fromLatin1("Set requires new")); +} + +void SetPrototype::init(ExecutionEngine *engine, Object *ctor) +{ + Scope scope(engine); + ScopedObject o(scope); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + ctor->addSymbolSpecies(); + defineDefaultProperty(engine->id_constructor(), (o = ctor)); + + defineDefaultProperty(QStringLiteral("add"), method_add, 1); + defineDefaultProperty(QStringLiteral("clear"), method_clear, 0); + defineDefaultProperty(QStringLiteral("delete"), method_delete, 1); + defineDefaultProperty(QStringLiteral("entries"), method_entries, 0); + defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1); + defineDefaultProperty(QStringLiteral("has"), method_has, 1); + defineAccessorProperty(QStringLiteral("size"), method_get_size, nullptr); + + // Per the spec, the value for 'keys' is the same as 'values'. + ScopedString valString(scope, scope.engine->newIdentifier(QStringLiteral("values"))); + ScopedFunctionObject valuesFn(scope, FunctionObject::createBuiltinFunction(engine, valString, SetPrototype::method_values, 0)); + defineDefaultProperty(QStringLiteral("keys"), valuesFn); + defineDefaultProperty(QStringLiteral("values"), valuesFn); + + defineDefaultProperty(engine->symbol_iterator(), valuesFn); + + ScopedString val(scope, engine->newString(QLatin1String("Set"))); + defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), val); +} + +ReturnedValue SetPrototype::method_add(const FunctionObject *b, const Value *thisObject, const Value *argv, int) +{ + Scope scope(b); + Scoped<SetObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<ArrayObject> arr(scope, that->d()->setArray); + ScopedValue sk(scope); + qint64 len = arr->getLength(); + + for (int i = 0; i < len; ++i) { + sk = arr->getIndexed(i); + if (sk->sameValueZero(argv[0])) + return that.asReturnedValue(); + } + + sk = argv[0]; + if (sk->isDouble()) { + if (sk->doubleValue() == 0 && std::signbit(sk->doubleValue())) + sk = Primitive::fromDouble(+0); + } + + arr->putIndexed(len, sk); + return that.asReturnedValue(); +} + +ReturnedValue SetPrototype::method_clear(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<SetObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<ArrayObject> arr(scope, scope.engine->newArrayObject()); + that->d()->setArray.set(scope.engine, arr->d()); + return Encode::undefined(); +} + +// delete value +ReturnedValue SetPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int) +{ + Scope scope(b); + Scoped<SetObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<ArrayObject> arr(scope, that->d()->setArray); + ScopedValue sk(scope); + qint64 len = arr->getLength(); + + bool found = false; + int idx = 0; + + for (; idx < len; ++idx) { + sk = arr->getIndexed(idx); + if (sk->sameValueZero(argv[0])) { + found = true; + break; + } + } + + if (found == true) { + Scoped<ArrayObject> newArr(scope, scope.engine->newArrayObject()); + for (int j = 0, newIdx = 0; j < len; ++j, newIdx++) { + if (j == idx) { + newIdx--; // skip the entry + continue; + } + newArr->putIndexed(newIdx, ScopedValue(scope, arr->getIndexed(j))); + } + + that->d()->setArray.set(scope.engine, newArr->d()); + return Encode(true); + } else { + return Encode(false); + } +} + +ReturnedValue SetPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<SetObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<SetIteratorObject> ao(scope, scope.engine->newSetIteratorObject(that)); + ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind; + return ao->asReturnedValue(); +} + +ReturnedValue SetPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<SetObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + ScopedFunctionObject callbackfn(scope, argv[0]); + if (!callbackfn) + return scope.engine->throwTypeError(); + + ScopedValue thisArg(scope, Primitive::undefinedValue()); + if (argc > 1) + thisArg = ScopedValue(scope, argv[1]); + + Scoped<ArrayObject> arr(scope, that->d()->setArray); + ScopedValue sk(scope); + qint64 len = arr->getLength(); + + Value *arguments = scope.alloc(3); + for (int i = 0; i < len; ++i) { + sk = arr->getIndexed(i); + + arguments[0] = sk; + arguments[1] = sk; + arguments[2] = that; + callbackfn->call(thisArg, arguments, 3); + CHECK_EXCEPTION(); + } + return Encode::undefined(); +} + +ReturnedValue SetPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int) +{ + Scope scope(b); + Scoped<SetObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<ArrayObject> arr(scope, that->d()->setArray); + ScopedValue sk(scope); + qint64 len = arr->getLength(); + + for (int i = 0; i < len; ++i) { + sk = arr->getIndexed(i); + if (sk->sameValueZero(argv[0])) + return Encode(true); + } + + return Encode(false); +} + +ReturnedValue SetPrototype::method_get_size(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<SetObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<ArrayObject> arr(scope, that->d()->setArray); + qint64 len = arr->getLength(); + return Encode((uint)len); +} + +ReturnedValue SetPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<SetObject> that(scope, thisObject); + if (!that) + return scope.engine->throwTypeError(); + + Scoped<SetIteratorObject> ao(scope, scope.engine->newSetIteratorObject(that)); + ao->d()->iterationKind = IteratorKind::ValueIteratorKind; + return ao->asReturnedValue(); +} + diff --git a/src/qml/jsruntime/qv4setobject_p.h b/src/qml/jsruntime/qv4setobject_p.h new file mode 100644 index 0000000000..ba0ed6bfe7 --- /dev/null +++ b/src/qml/jsruntime/qv4setobject_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Crimson AS <info@crimson.no> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4SETOBJECT_P_H +#define QV4SETOBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" +#include "qv4objectproto_p.h" +#include "qv4functionobject_p.h" +#include "qv4string_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Heap { + +struct SetCtor : FunctionObject { + void init(QV4::ExecutionContext *scope); +}; + +#define SetObjectMembers(class, Member) \ + Member(class, Pointer, ArrayObject *, setArray) + +DECLARE_HEAP_OBJECT(SetObject, Object) { + DECLARE_MARKOBJECTS(SetObject); + void init() { Object::init(); } +}; + +} + +struct SetCtor: FunctionObject +{ + V4_OBJECT2(SetCtor, FunctionObject) + + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct SetObject : Object +{ + V4_OBJECT2(SetObject, Object) + V4_PROTOTYPE(setPrototype) +}; + +struct SetPrototype : Object +{ + void init(ExecutionEngine *engine, Object *ctor); + + static ReturnedValue method_add(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_clear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_delete(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_size(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +}; + + +} // namespace QV4 + + +QT_END_NAMESPACE + +#endif // QV4SETOBJECT_P_H diff --git a/src/qml/jsruntime/qv4sparsearray.cpp b/src/qml/jsruntime/qv4sparsearray.cpp index 2a3e28bf63..8930c9a94d 100644 --- a/src/qml/jsruntime/qv4sparsearray.cpp +++ b/src/qml/jsruntime/qv4sparsearray.cpp @@ -395,7 +395,7 @@ void SparseArray::freeTree(SparseArrayNode *root, int alignment) SparseArray::SparseArray() : numEntries(0) { - freeList = Primitive::emptyValue(UINT_MAX).asReturnedValue(); + freeList = Encode(-1); header.p = 0; header.left = nullptr; header.right = nullptr; diff --git a/src/qml/jsruntime/qv4sparsearray_p.h b/src/qml/jsruntime/qv4sparsearray_p.h index 51869b259f..c1e50c8dcf 100644 --- a/src/qml/jsruntime/qv4sparsearray_p.h +++ b/src/qml/jsruntime/qv4sparsearray_p.h @@ -52,6 +52,7 @@ // #include "qv4global_p.h" +#include "qv4value_p.h" #include <QtCore/qlist.h> //#define Q_MAP_DEBUG @@ -151,7 +152,7 @@ struct Q_QML_EXPORT SparseArray SparseArray(const SparseArray &other); - ReturnedValue freeList; + Value freeList; private: SparseArray &operator=(const SparseArray &other); diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index 447992ebec..27c73a2b77 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -52,8 +52,17 @@ using namespace QV4; #ifndef V4_BOOTSTRAP +void Heap::StringOrSymbol::markObjects(Heap::Base *that, MarkStack *markStack) +{ + StringOrSymbol *s = static_cast<StringOrSymbol *>(that); + Heap::Base *id = s->identifier.asHeapObject(); + if (id) + id->mark(markStack); +} + void Heap::String::markObjects(Heap::Base *that, MarkStack *markStack) { + StringOrSymbol::markObjects(that, markStack); String *s = static_cast<String *>(that); if (s->subtype < StringType_Complex) return; @@ -68,6 +77,7 @@ void Heap::String::markObjects(Heap::Base *that, MarkStack *markStack) } } +DEFINE_MANAGED_VTABLE(StringOrSymbol); DEFINE_MANAGED_VTABLE(String); @@ -76,7 +86,7 @@ bool String::isEqualTo(Managed *t, Managed *o) if (t == o) return true; - if (!o->d()->vtable()->isString) + if (!o->vtable()->isString) return false; return static_cast<String *>(t)->isEqualTo(static_cast<String *>(o)); @@ -128,7 +138,8 @@ void Heap::ComplexString::init(Heap::String *ref, int from, int len) this->len = len; } -void Heap::String::destroy() { +void Heap::StringOrSymbol::destroy() +{ if (text) { internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(-text->size) * (int)sizeof(QChar)); if (!text->ref.deref()) @@ -174,7 +185,7 @@ void Heap::String::simplifyString() const text = result.data_ptr(); text->ref.ref(); const ComplexString *cs = static_cast<const ComplexString *>(this); - identifier = nullptr; + identifier = Identifier::invalid(); cs->left = cs->right = nullptr; internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(text->size) * (qptrdiff)sizeof(QChar)); @@ -227,10 +238,12 @@ void Heap::String::append(const String *data, QChar *ch) } } -void Heap::String::createHashValue() const +void Heap::StringOrSymbol::createHashValue() const { - if (!text) - simplifyString(); + if (!text) { + Q_ASSERT(internalClass->vtable->isString); + static_cast<const Heap::String *>(this)->simplifyString(); + } Q_ASSERT(text); const QChar *ch = reinterpret_cast<const QChar *>(text->data()); const QChar *end = ch + text->size; @@ -248,4 +261,3 @@ uint String::toArrayIndex(const QString &str) { return QV4::String::toArrayIndex(str.constData(), str.constData() + str.length()); } - diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 5466cc274d..0deb542ea2 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -64,9 +64,10 @@ struct Identifier; namespace Heap { -struct Q_QML_PRIVATE_EXPORT String : Base { - static void markObjects(Heap::Base *that, MarkStack *markStack); +struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base +{ enum StringType { + StringType_Symbol, StringType_Regular, StringType_ArrayIndex, StringType_Unknown, @@ -75,13 +76,20 @@ struct Q_QML_PRIVATE_EXPORT String : Base { StringType_Complex = StringType_AddedString }; -#ifndef V4_BOOTSTRAP - void init(const QString &text); + mutable QStringData *text; + mutable Identifier identifier; + mutable uint subtype; + mutable uint stringHash; + + static void markObjects(Heap::Base *that, MarkStack *markStack); void destroy(); - void simplifyString() const; - int length() const; - std::size_t retainedTextSize() const { - return subtype >= StringType_Complex ? 0 : (std::size_t(text->size) * sizeof(QChar)); + + inline QString toQString() const { + if (!text) + return QString(); + QStringDataPtr ptr = { text }; + text->ref.ref(); + return QString(ptr); } void createHashValue() const; inline unsigned hashValue() const { @@ -91,6 +99,22 @@ struct Q_QML_PRIVATE_EXPORT String : Base { return stringHash; } +}; + +struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol { + static void markObjects(Heap::Base *that, MarkStack *markStack); + +#ifndef V4_BOOTSTRAP + const VTable *vtable() const { + return internalClass->vtable; + } + + void init(const QString &text); + void simplifyString() const; + int length() const; + std::size_t retainedTextSize() const { + return subtype >= StringType_Complex ? 0 : (std::size_t(text->size) * sizeof(QChar)); + } inline QString toQString() const { if (subtype >= StringType_Complex) simplifyString(); @@ -104,7 +128,7 @@ struct Q_QML_PRIVATE_EXPORT String : Base { if (hashValue() != other->hashValue()) return false; Q_ASSERT(subtype < StringType_Complex); - if (identifier && identifier == other->identifier) + if (identifier.isValid() && identifier == other->identifier) return true; if (subtype == Heap::String::StringType_ArrayIndex && other->subtype == Heap::String::StringType_ArrayIndex) return true; @@ -114,10 +138,6 @@ struct Q_QML_PRIVATE_EXPORT String : Base { bool startsWithUpper() const; - mutable QStringData *text; - mutable Identifier *identifier; - mutable uint subtype; - mutable uint stringHash; private: static void append(const String *data, QChar *ch); #endif @@ -146,9 +166,27 @@ int String::length() const { } -struct Q_QML_PRIVATE_EXPORT String : public Managed { +struct Q_QML_PRIVATE_EXPORT StringOrSymbol : public Managed { #ifndef V4_BOOTSTRAP - V4_MANAGED(String, Managed) + V4_MANAGED(StringOrSymbol, Managed) + enum { + IsStringOrSymbol = true + }; + + inline void makeIdentifier() const; + Identifier identifier() const { return d()->identifier; } + + uint asArrayIndex() const; + + inline QString toQString() const { + return d()->toQString(); + } +#endif +}; + +struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol { +#ifndef V4_BOOTSTRAP + V4_MANAGED(String, StringOrSymbol) Q_MANAGED_TYPE(String) V4_INTERNALCLASS(String) V4_NEEDS_DESTROY @@ -187,12 +225,6 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { } uint toUInt(bool *ok) const; - void makeIdentifier() const { - if (d()->identifier) - return; - makeIdentifierImpl(); - } - // slow path Q_NEVER_INLINE void makeIdentifierImpl() const; @@ -210,8 +242,6 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { bool startsWithUpper() const { return d()->startsWithUpper(); } - Identifier *identifier() const { return d()->identifier; } - protected: static bool isEqualTo(Managed *that, Managed *o); static uint getLength(const Managed *m); @@ -254,7 +284,7 @@ public: uint h = toArrayIndex(ch, end); if (h != UINT_MAX) { if (subtype) - *subtype = Heap::String::StringType_ArrayIndex; + *subtype = Heap::StringOrSymbol::StringType_ArrayIndex; return h; } @@ -264,7 +294,7 @@ public: } if (subtype) - *subtype = Heap::String::StringType_Regular; + *subtype = (toUInt(ch) == '@') ? Heap::StringOrSymbol::StringType_Symbol : Heap::StringOrSymbol::StringType_Regular; return h; } }; @@ -280,9 +310,29 @@ struct ComplexString : String { } }; +inline +void StringOrSymbol::makeIdentifier() const { + if (d()->identifier.isValid()) + return; + Q_ASSERT(isString()); + static_cast<const String *>(this)->makeIdentifierImpl(); +} + +inline +uint StringOrSymbol::asArrayIndex() const { + if (isString()) + return static_cast<const String *>(this)->asArrayIndex(); + return UINT_MAX; +} + +template<> +inline const StringOrSymbol *Value::as() const { + return isManaged() && m()->internalClass->vtable->isStringOrSymbol ? static_cast<const String *>(this) : nullptr; +} + template<> inline const String *Value::as() const { - return isManaged() && m()->vtable()->isString ? static_cast<const String *>(this) : nullptr; + return isManaged() && m()->internalClass->vtable->isString ? static_cast<const String *>(this) : nullptr; } template<> diff --git a/src/qml/jsruntime/qv4stringiterator.cpp b/src/qml/jsruntime/qv4stringiterator.cpp new file mode 100644 index 0000000000..810ed333e4 --- /dev/null +++ b/src/qml/jsruntime/qv4stringiterator.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Crimson AS <info@crimson.no> +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qv4iterator_p.h> +#include <private/qv4stringiterator_p.h> +#include <private/qv4symbol_p.h> + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(StringIteratorObject); + +void StringIteratorPrototype::init(ExecutionEngine *e) +{ + defineDefaultProperty(QStringLiteral("next"), method_next, 0); + + Scope scope(e); + ScopedString val(scope, e->newString(QLatin1String("String Iterator"))); + defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val); +} + +ReturnedValue StringIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int) +{ + Scope scope(b); + const StringIteratorObject *thisObject = that->as<StringIteratorObject>(); + if (!thisObject) + return scope.engine->throwTypeError(QLatin1String("Not an String Iterator instance")); + + ScopedString s(scope, thisObject->d()->iteratedString); + if (!s) { + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); + } + + quint32 index = thisObject->d()->nextIndex; + + QString str = s->toQString(); + quint32 len = str.length(); + + if (index >= len) { + thisObject->d()->iteratedString.set(scope.engine, nullptr); + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); + } + + QChar ch = str.at(index); + int num = 1; + if (ch.unicode() >= 0xd800 && ch.unicode() <= 0xdbff && index + 1 != len) { + ch = str.at(index + 1); + if (ch.unicode() >= 0xdc00 && ch.unicode() <= 0xdfff) + num = 2; + } + + thisObject->d()->nextIndex += num; + + ScopedString resultString(scope, scope.engine->newString(s->toQString().mid(index, num))); + return IteratorPrototype::createIterResultObject(scope.engine, resultString, false); +} + diff --git a/src/qml/jsruntime/qv4stringiterator_p.h b/src/qml/jsruntime/qv4stringiterator_p.h new file mode 100644 index 0000000000..672ccc9963 --- /dev/null +++ b/src/qml/jsruntime/qv4stringiterator_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4STRINGITERATOR_P_H +#define QV4STRINGITERATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" +#include "qv4string_p.h" + +QT_BEGIN_NAMESPACE + + +namespace QV4 { + +namespace Heap { + +#define StringIteratorObjectMembers(class, Member) \ + Member(class, Pointer, String *, iteratedString) \ + Member(class, NoMark, quint32, nextIndex) + +DECLARE_HEAP_OBJECT(StringIteratorObject, Object) { + DECLARE_MARKOBJECTS(StringIteratorObject); + void init(String *str, QV4::ExecutionEngine *engine) + { + Object::init(); + this->iteratedString.set(engine, str); + this->nextIndex = 0; + } +}; + +} + +struct StringIteratorPrototype : Object +{ + V4_PROTOTYPE(iteratorPrototype) + void init(ExecutionEngine *engine); + + static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); +}; + +struct StringIteratorObject : Object +{ + V4_OBJECT2(StringIteratorObject, Object) + Q_MANAGED_TYPE(StringIteratorObject) + V4_PROTOTYPE(stringIteratorPrototype) + + void init(ExecutionEngine *engine); +}; + + +} + +QT_END_NAMESPACE + +#endif // QV4ARRAYITERATOR_P_H + diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 61176b3706..3639edac17 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -44,8 +44,10 @@ #include "qv4objectproto_p.h" #include <private/qv4mm_p.h> #include "qv4scopedvalue_p.h" +#include "qv4symbol_p.h" #include "qv4alloca_p.h" #include "qv4jscall_p.h" +#include "qv4stringiterator_p.h" #include <QtCore/QDateTime> #include <QtCore/QDebug> #include <QtCore/QStringList> @@ -152,16 +154,58 @@ ReturnedValue StringCtor::callAsConstructor(const FunctionObject *f, const Value value = argv[0].toString(v4); else value = v4->newString(); + CHECK_EXCEPTION(); return Encode(v4->newStringObject(value)); } ReturnedValue StringCtor::call(const FunctionObject *m, const Value *, const Value *argv, int argc) { ExecutionEngine *v4 = m->engine(); - if (argc) - return argv[0].toString(v4)->asReturnedValue(); - else + if (!argc) return v4->newString()->asReturnedValue(); + if (argv[0].isSymbol()) + return v4->newString(argv[0].symbolValue()->descriptiveString())->asReturnedValue(); + return argv[0].toString(v4)->asReturnedValue(); +} + +ReturnedValue StringCtor::method_fromCharCode(const FunctionObject *b, const Value *, const Value *argv, int argc) +{ + QString str(argc, Qt::Uninitialized); + QChar *ch = str.data(); + for (int i = 0, ei = argc; i < ei; ++i) { + *ch = QChar(argv[i].toUInt16()); + ++ch; + } + *ch = 0; + return Encode(b->engine()->newString(str)); +} + + + +ReturnedValue StringCtor::method_fromCodePoint(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + ExecutionEngine *e = f->engine(); + QString result(argc*2, Qt::Uninitialized); // assume worst case + QChar *ch = result.data(); + for (int i = 0; i < argc; ++i) { + double num = argv[i].toNumber(); + if (e->hasException) + return Encode::undefined(); + int cp = static_cast<int>(num); + if (cp != num || cp < 0 || cp > 0x10ffff) + return e->throwRangeError(QStringLiteral("String.fromCodePoint: argument out of range.")); + if (cp > 0xffff) { + *ch = QChar::highSurrogate(cp); + ++ch; + *ch = QChar::lowSurrogate(cp); + } else { + *ch = cp; + } + ++ch; + } + *ch = 0; + result.truncate(ch - result.constData()); + return e->newString(result)->asReturnedValue(); } void StringPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -169,15 +213,23 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor) Scope scope(engine); ScopedObject o(scope); + // need to set this once again, as these were not fully defined when creating the string proto + Heap::InternalClass *ic = scope.engine->classes[ExecutionEngine::Class_StringObject]->changePrototype(scope.engine->objectPrototype()->d()); + d()->internalClass.set(scope.engine, ic); + d()->string.set(scope.engine, scope.engine->id_empty()->d()); + setProperty(scope.engine, Heap::StringObject::LengthPropertyIndex, Primitive::fromInt32(0)); + ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); - ctor->defineDefaultProperty(QStringLiteral("fromCharCode"), method_fromCharCode, 1); + ctor->defineDefaultProperty(QStringLiteral("fromCharCode"), StringCtor::method_fromCharCode, 1); + ctor->defineDefaultProperty(QStringLiteral("fromCodePoint"), StringCtor::method_fromCodePoint, 1); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(engine->id_toString(), method_toString); defineDefaultProperty(engine->id_valueOf(), method_toString); // valueOf and toString are identical defineDefaultProperty(QStringLiteral("charAt"), method_charAt, 1); defineDefaultProperty(QStringLiteral("charCodeAt"), method_charCodeAt, 1); + defineDefaultProperty(QStringLiteral("codePointAt"), method_codePointAt, 1); defineDefaultProperty(QStringLiteral("concat"), method_concat, 1); defineDefaultProperty(QStringLiteral("endsWith"), method_endsWith, 1); defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1); @@ -185,6 +237,9 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare, 1); defineDefaultProperty(QStringLiteral("match"), method_match, 1); + defineDefaultProperty(QStringLiteral("normalize"), method_normalize, 0); + defineDefaultProperty(QStringLiteral("padEnd"), method_padEnd, 1); + defineDefaultProperty(QStringLiteral("padStart"), method_padStart, 1); defineDefaultProperty(QStringLiteral("repeat"), method_repeat, 1); defineDefaultProperty(QStringLiteral("replace"), method_replace, 2); defineDefaultProperty(QStringLiteral("search"), method_search, 1); @@ -198,6 +253,7 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("toUpperCase"), method_toUpperCase); defineDefaultProperty(QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase); defineDefaultProperty(QStringLiteral("trim"), method_trim); + defineDefaultProperty(engine->symbol_iterator(), method_iterator); } static Heap::String *thisAsString(ExecutionEngine *v4, const QV4::Value *thisObject) @@ -270,6 +326,29 @@ ReturnedValue StringPrototype::method_charCodeAt(const FunctionObject *b, const return Encode(qt_qnan()); } +ReturnedValue StringPrototype::method_codePointAt(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + ExecutionEngine *v4 = f->engine(); + QString value = getThisString(v4, thisObject); + if (v4->hasException) + return QV4::Encode::undefined(); + + int index = argc ? argv[0].toInteger() : 0; + if (v4->hasException) + return QV4::Encode::undefined(); + + if (index < 0 || index >= value.size()) + return Encode::undefined(); + + uint first = value.at(index).unicode(); + if (QChar::isHighSurrogate(first) && index + 1 < value.size()) { + uint second = value.at(index + 1).unicode(); + if (QChar::isLowSurrogate(second)) + return Encode(QChar::surrogateToUcs4(first, second)); + } + return Encode(first); +} + ReturnedValue StringPrototype::method_concat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { ExecutionEngine *v4 = b->engine(); @@ -298,12 +377,11 @@ ReturnedValue StringPrototype::method_endsWith(const FunctionObject *b, const Va if (v4->hasException) return QV4::Encode::undefined(); - QString searchString; - if (argc) { - if (argv[0].as<RegExpObject>()) - return v4->throwTypeError(); - searchString = argv[0].toQString(); - } + if (argc && argv[0].as<RegExpObject>()) + return v4->throwTypeError(); + QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString(); + if (v4->hasException) + return Encode::undefined(); int pos = value.length(); if (argc > 1) @@ -323,9 +401,9 @@ ReturnedValue StringPrototype::method_indexOf(const FunctionObject *b, const Val if (v4->hasException) return QV4::Encode::undefined(); - QString searchString; - if (argc) - searchString = argv[0].toQString(); + QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString(); + if (v4->hasException) + return Encode::undefined(); int pos = 0; if (argc > 1) @@ -345,12 +423,11 @@ ReturnedValue StringPrototype::method_includes(const FunctionObject *b, const Va if (v4->hasException) return QV4::Encode::undefined(); - QString searchString; - if (argc) { - if (argv[0].as<RegExpObject>()) - return v4->throwTypeError(); - searchString = argv[0].toQString(); - } + if (argc && argv[0].as<RegExpObject>()) + return v4->throwTypeError(); + QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString(); + if (v4->hasException) + return Encode::undefined(); int pos = 0; if (argc > 1) { @@ -374,9 +451,9 @@ ReturnedValue StringPrototype::method_lastIndexOf(const FunctionObject *b, const if (v4->hasException) return QV4::Encode::undefined(); - QString searchString; - if (argc) - searchString = argv[0].toQString(); + QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString(); + if (v4->hasException) + return Encode::undefined(); double position = argc > 1 ? RuntimeHelpers::toNumber(argv[1]) : +qInf(); if (std::isnan(position)) @@ -455,6 +532,115 @@ ReturnedValue StringPrototype::method_match(const FunctionObject *b, const Value return a.asReturnedValue(); } +ReturnedValue StringPrototype::method_normalize(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + ExecutionEngine *v4 = f->engine(); + const QString value = getThisString(v4, thisObject); + if (v4->hasException) + return Encode::undefined(); + + QString::NormalizationForm form = QString::NormalizationForm_C; + if (argc >= 1 && !argv[0].isUndefined()) { + QString f = argv[0].toQString(); + if (v4->hasException) + return Encode::undefined(); + if (f == QLatin1String("NFC")) + form = QString::NormalizationForm_C; + else if (f == QLatin1String("NFD")) + form = QString::NormalizationForm_D; + else if (f == QLatin1String("NFKC")) + form = QString::NormalizationForm_KC; + else if (f == QLatin1String("NFKD")) + form = QString::NormalizationForm_KD; + else + return v4->throwRangeError(QLatin1String("String.prototype.normalize: Invalid normalization form.")); + } + QString normalized = value.normalized(form); + return v4->newString(normalized)->asReturnedValue(); +} + +ReturnedValue StringPrototype::method_padEnd(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + ExecutionEngine *v4 = f->engine(); + if (thisObject->isNullOrUndefined()) + return v4->throwTypeError(); + + Scope scope(v4); + ScopedString s(scope, thisAsString(v4, thisObject)); + if (v4->hasException) + return Encode::undefined(); + if (!argc) + return s->asReturnedValue(); + + int maxLen = argv[0].toInteger(); + if (maxLen <= s->d()->length()) + return s->asReturnedValue(); + QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : QString::fromLatin1(" "); + if (v4->hasException) + return Encode::undefined(); + + if (fillString.isEmpty()) + return s->asReturnedValue(); + + QString padded = s->toQString(); + int oldLength = padded.length(); + int toFill = maxLen - oldLength; + padded.resize(maxLen); + QChar *ch = padded.data() + oldLength; + while (toFill) { + int copy = qMin(fillString.length(), toFill); + memcpy(ch, fillString.constData(), copy*sizeof(QChar)); + toFill -= copy; + ch += copy; + } + *ch = 0; + + return v4->newString(padded)->asReturnedValue(); +} + +ReturnedValue StringPrototype::method_padStart(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + ExecutionEngine *v4 = f->engine(); + if (thisObject->isNullOrUndefined()) + return v4->throwTypeError(); + + Scope scope(v4); + ScopedString s(scope, thisAsString(v4, thisObject)); + if (v4->hasException) + return Encode::undefined(); + if (!argc) + return s->asReturnedValue(); + + int maxLen = argv[0].toInteger(); + if (maxLen <= s->d()->length()) + return s->asReturnedValue(); + QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : QString::fromLatin1(" "); + if (v4->hasException) + return Encode::undefined(); + + if (fillString.isEmpty()) + return s->asReturnedValue(); + + QString original = s->toQString(); + int oldLength = original.length(); + int toFill = maxLen - oldLength; + QString padded; + padded.resize(maxLen); + QChar *ch = padded.data(); + while (toFill) { + int copy = qMin(fillString.length(), toFill); + memcpy(ch, fillString.constData(), copy*sizeof(QChar)); + toFill -= copy; + ch += copy; + } + memcpy(ch, original.constData(), oldLength*sizeof(QChar)); + ch += oldLength; + *ch = 0; + + return v4->newString(padded)->asReturnedValue(); +} + + ReturnedValue StringPrototype::method_repeat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { ExecutionEngine *v4 = b->engine(); @@ -777,12 +963,11 @@ ReturnedValue StringPrototype::method_startsWith(const FunctionObject *b, const if (v4->hasException) return QV4::Encode::undefined(); - QString searchString; - if (argc) { - if (argv[0].as<RegExpObject>()) - return v4->throwTypeError(); - searchString = argv[0].toQString(); - } + if (argc && argv[0].as<RegExpObject>()) + return v4->throwTypeError(); + QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString(); + if (v4->hasException) + return Encode::undefined(); int pos = 0; if (argc > 1) @@ -892,17 +1077,6 @@ ReturnedValue StringPrototype::method_toLocaleUpperCase(const FunctionObject *b, return method_toUpperCase(b, thisObject, argv, argc); } -ReturnedValue StringPrototype::method_fromCharCode(const FunctionObject *b, const Value *, const Value *argv, int argc) -{ - QString str(argc, Qt::Uninitialized); - QChar *ch = str.data(); - for (int i = 0, ei = argc; i < ei; ++i) { - *ch = QChar(argv[i].toUInt16()); - ++ch; - } - return Encode(b->engine()->newString(str)); -} - ReturnedValue StringPrototype::method_trim(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); @@ -923,3 +1097,16 @@ ReturnedValue StringPrototype::method_trim(const FunctionObject *b, const Value return Encode(v4->newString(QString(chars + start, end - start + 1))); } + + + +ReturnedValue StringPrototype::method_iterator(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + ScopedString s(scope, thisObject->toString(scope.engine)); + if (!s || thisObject->isNullOrUndefined()) + return scope.engine->throwTypeError(); + + Scoped<StringIteratorObject> si(scope, scope.engine->memoryManager->allocate<StringIteratorObject>(s->d(), scope.engine)); + return si->asReturnedValue(); +} diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index 7d25678b61..407d617d57 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -70,6 +70,8 @@ DECLARE_HEAP_OBJECT(StringObject, Object) { LengthPropertyIndex = 0 }; + void init(bool /*don't init*/) + { Object::init(); } void init(); void init(const QV4::String *string); @@ -108,6 +110,9 @@ struct StringCtor: FunctionObject static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue method_fromCharCode(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_fromCodePoint(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; struct StringPrototype: StringObject @@ -118,6 +123,7 @@ struct StringPrototype: StringObject static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_charAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_charCodeAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_codePointAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_concat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_endsWith(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_indexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); @@ -125,6 +131,9 @@ struct StringPrototype: StringObject static ReturnedValue method_lastIndexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_localeCompare(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_match(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_normalize(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_padEnd(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_padStart(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_repeat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_replace(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_search(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); @@ -137,8 +146,8 @@ struct StringPrototype: StringObject static ReturnedValue method_toLocaleLowerCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_toUpperCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_toLocaleUpperCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_fromCharCode(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_trim(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_iterator(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; } diff --git a/src/qml/jsruntime/qv4symbol.cpp b/src/qml/jsruntime/qv4symbol.cpp new file mode 100644 index 0000000000..7bb33dce69 --- /dev/null +++ b/src/qml/jsruntime/qv4symbol.cpp @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qv4symbol_p.h> +#include <qv4functionobject_p.h> +#include <qv4identifiertable_p.h> + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(SymbolCtor); +DEFINE_MANAGED_VTABLE(Symbol); +DEFINE_OBJECT_VTABLE(SymbolObject); + +void Heap::Symbol::init(const QString &s) +{ + Q_ASSERT(s.at(0) == QLatin1Char('@')); + identifier = Identifier::fromHeapObject(this); + QString desc(s); + text = desc.data_ptr(); + text->ref.ref(); +} + +void Heap::SymbolCtor::init(QV4::ExecutionContext *scope) +{ + Heap::FunctionObject::init(scope, QStringLiteral("Symbol")); +} + +void Heap::SymbolObject::init(const QV4::Symbol *s) +{ + Object::init(); + symbol.set(internalClass->engine, s->d()); +} + +ReturnedValue QV4::SymbolCtor::call(const QV4::FunctionObject *f, const QV4::Value *, const QV4::Value *argv, int argc) +{ + Scope scope(f); + QString desc = QChar::fromLatin1('@'); + if (argc && !argv[0].isUndefined()) { + ScopedString s(scope, argv[0].toString(scope.engine)); + if (scope.hasException()) + return Encode::undefined(); + desc += s->toQString(); + } + return Symbol::create(scope.engine, desc)->asReturnedValue(); +} + +ReturnedValue SymbolCtor::method_for(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + ScopedValue k(scope, argc ? argv[0]: Primitive::undefinedValue()); + ScopedString key(scope, k->toString(scope.engine)); + if (scope.hasException()) + return Encode::undefined(); + QString desc = QLatin1Char('@') + key->toQString(); + return scope.engine->identifierTable->insertSymbol(desc)->asReturnedValue(); +} + +ReturnedValue SymbolCtor::method_keyFor(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + ExecutionEngine *e = f->engine(); + if (!argc || !argv[0].isSymbol()) + return e->throwTypeError(QLatin1String("Symbol.keyFor: Argument is not a symbol.")); + const Symbol &arg = static_cast<const Symbol &>(argv[0]); + Heap::Symbol *s = e->identifierTable->symbolForId(arg.identifier()); + Q_ASSERT(!s || s == arg.d()); + if (s) + return e->newString(arg.toQString().mid((1)))->asReturnedValue(); + return Encode::undefined(); +} + +void SymbolPrototype::init(ExecutionEngine *engine, Object *ctor) +{ + Scope scope(engine); + ScopedValue v(scope); + ctor->defineReadonlyProperty(engine->id_prototype(), (v = this)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); + + ctor->defineDefaultProperty(QStringLiteral("for"), SymbolCtor::method_for, 1); + ctor->defineDefaultProperty(QStringLiteral("keyFor"), SymbolCtor::method_keyFor, 1); + ctor->defineReadonlyProperty(QStringLiteral("hasInstance"), *engine->symbol_hasInstance()); + ctor->defineReadonlyProperty(QStringLiteral("isConcatSpreadable"), *engine->symbol_isConcatSpreadable()); + ctor->defineReadonlyProperty(QStringLiteral("iterator"), *engine->symbol_iterator()); + ctor->defineReadonlyProperty(QStringLiteral("match"), *engine->symbol_match()); + ctor->defineReadonlyProperty(QStringLiteral("replace"), *engine->symbol_replace()); + ctor->defineReadonlyProperty(QStringLiteral("search"), *engine->symbol_search()); + ctor->defineReadonlyProperty(QStringLiteral("species"), *engine->symbol_species()); + ctor->defineReadonlyProperty(QStringLiteral("split"), *engine->symbol_split()); + ctor->defineReadonlyProperty(QStringLiteral("toPrimitive"), *engine->symbol_toPrimitive()); + ctor->defineReadonlyProperty(QStringLiteral("toStringTag"), *engine->symbol_toStringTag()); + ctor->defineReadonlyProperty(QStringLiteral("unscopables"), *engine->symbol_unscopables()); + + defineDefaultProperty(QStringLiteral("constructor"), (v = ctor)); + defineDefaultProperty(QStringLiteral("toString"), method_toString); + defineDefaultProperty(QStringLiteral("valueOf"), method_valueOf); + defineDefaultProperty(engine->symbol_toPrimitive(), method_symbolToPrimitive, 1, Attr_ReadOnly_ButConfigurable); + + v = engine->newString(QStringLiteral("Symbol")); + defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), v); + +} + +ReturnedValue SymbolPrototype::method_toString(const FunctionObject *f, const Value *thisObject, const Value *, int) +{ + Scope scope(f); + Scoped<Symbol> s(scope, thisObject->as<Symbol>()); + if (!s) { + if (const SymbolObject *o = thisObject->as<SymbolObject>()) + s = o->d()->symbol; + else + return scope.engine->throwTypeError(); + } + return scope.engine->newString(s->descriptiveString())->asReturnedValue(); +} + +ReturnedValue SymbolPrototype::method_valueOf(const FunctionObject *f, const Value *thisObject, const Value *, int) +{ + Scope scope(f); + Scoped<Symbol> s(scope, thisObject->as<Symbol>()); + if (!s) { + if (const SymbolObject *o = thisObject->as<SymbolObject>()) + s = o->d()->symbol; + else + return scope.engine->throwTypeError(); + } + return s->asReturnedValue(); +} + +ReturnedValue SymbolPrototype::method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *, int) +{ + if (thisObject->isSymbol()) + return thisObject->asReturnedValue(); + if (const SymbolObject *o = thisObject->as<SymbolObject>()) + return o->d()->symbol->asReturnedValue(); + return f->engine()->throwTypeError(); +} + +Heap::Symbol *Symbol::create(ExecutionEngine *e, const QString &s) +{ + Q_ASSERT(s.at(0) == QLatin1Char('@')); + return e->memoryManager->alloc<Symbol>(s); +} + +QString Symbol::descriptiveString() const +{ + return QLatin1String("Symbol(") + toQString().midRef(1) + QLatin1String(")"); +} diff --git a/src/qml/jsruntime/qv4symbol_p.h b/src/qml/jsruntime/qv4symbol_p.h new file mode 100644 index 0000000000..3cf6bc5dde --- /dev/null +++ b/src/qml/jsruntime/qv4symbol_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4_SYMBOL_H +#define QV4_SYMBOL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4string_p.h" +#include "qv4functionobject_p.h" + +QT_BEGIN_NAMESPACE + + +namespace QV4 { + +namespace Heap { + +struct SymbolCtor : FunctionObject { + void init(QV4::ExecutionContext *scope); +}; + +struct Symbol : StringOrSymbol { + void init(const QString &s); +}; + +#define SymbolObjectMembers(class, Member) \ + Member(class, Pointer, Symbol *, symbol) + +DECLARE_HEAP_OBJECT(SymbolObject, Object) { + DECLARE_MARKOBJECTS(SymbolObject); + void init(const QV4::Symbol *s); +}; + +} + +struct SymbolCtor : FunctionObject +{ + V4_OBJECT2(SymbolCtor, FunctionObject) + + static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + 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); +}; + +struct SymbolPrototype : Object +{ + V4_PROTOTYPE(objectPrototype) + void init(ExecutionEngine *engine, Object *ctor); + + static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_valueOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue method_symbolToPrimitive(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +}; + +struct Symbol : StringOrSymbol +{ + V4_MANAGED(Symbol, StringOrSymbol) + Q_MANAGED_TYPE(Symbol) + V4_INTERNALCLASS(Symbol) + V4_NEEDS_DESTROY + + static Heap::Symbol *create(ExecutionEngine *e, const QString &s); + + QString descriptiveString() const; +}; + +struct SymbolObject : Object +{ + V4_OBJECT2(SymbolObject, Object) + Q_MANAGED_TYPE(SymbolObject) + V4_INTERNALCLASS(SymbolObject) + V4_PROTOTYPE(symbolPrototype) + + static bool put(Managed *, StringOrSymbol *, const Value &) { return false; } + static bool putIndexed(Managed *, uint, const Value &) { return false; } + +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index ea1532b8ce..3eadfe04a6 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -36,15 +36,20 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ + #include "qv4typedarray_p.h" +#include "qv4arrayiterator_p.h" #include "qv4arraybuffer_p.h" #include "qv4string_p.h" #include "qv4jscall_p.h" +#include "qv4symbol_p.h" #include <cmath> using namespace QV4; +DEFINE_OBJECT_VTABLE(IntrinsicTypedArrayCtor); +DEFINE_OBJECT_VTABLE(IntrinsicTypedArrayPrototype); DEFINE_OBJECT_VTABLE(TypedArrayCtor); DEFINE_OBJECT_VTABLE(TypedArrayPrototype); DEFINE_OBJECT_VTABLE(TypedArray); @@ -216,9 +221,12 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V if (!argc || !argv[0].isObject()) { // ECMA 6 22.2.1.1 - double l = argc ? argv[0].toNumber() : 0; + qint64 l = argc ? argv[0].toIndex() : 0; if (scope.engine->hasException) return Encode::undefined(); + // ### lift UINT_MAX restriction + if (l < 0 || l > UINT_MAX) + return scope.engine->throwRangeError(QLatin1String("Index out of range.")); uint len = (uint)l; if (l != len) scope.engine->throwRangeError(QStringLiteral("Non integer length for typed array.")); @@ -312,7 +320,10 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V return scope.engine->throwTypeError(); uint elementSize = operations[that->d()->type].bytesPerElement; - Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(l * elementSize)); + size_t bufferSize; + if (mul_overflow(size_t(l), size_t(elementSize), &bufferSize)) + return scope.engine->throwRangeError(QLatin1String("new TypedArray: invalid length")); + Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(bufferSize)); if (scope.engine->hasException) return Encode::undefined(); @@ -337,9 +348,9 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V return array.asReturnedValue(); } -ReturnedValue TypedArrayCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) +ReturnedValue TypedArrayCtor::call(const FunctionObject *f, const Value *, const Value *, int) { - return callAsConstructor(f, argv, argc); + return f->engine()->throwTypeError(QStringLiteral("calling a TypedArray constructor without new is invalid")); } void Heap::TypedArray::init(Type t) @@ -351,9 +362,9 @@ void Heap::TypedArray::init(Type t) Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type t) { - QV4::InternalClass *ic = e->internalClasses[EngineBase::Class_Empty]->changeVTable(staticVTable()); - ic = ic->changePrototype(e->typedArrayPrototype[t].d()); - return e->memoryManager->allocObject<TypedArray>(ic, e->typedArrayPrototype + t, t); + Scope scope(e); + Scoped<InternalClass> ic(scope, e->newInternalClass(staticVTable(), e->typedArrayPrototype + t)); + return e->memoryManager->allocObject<TypedArray>(ic->d(), t); } ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProperty) @@ -395,21 +406,18 @@ void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(3)); - ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); + + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(3)); + ctor->defineReadonlyProperty(engine->id_prototype(), *this); ctor->defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Primitive::fromInt32(operations[ctor->d()->type].bytesPerElement)); + ctor->setPrototype(engine->intrinsicTypedArrayCtor()); + + setPrototype(engine->intrinsicTypedArrayPrototype()); defineDefaultProperty(engine->id_constructor(), (o = ctor)); - defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr); - defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr); - defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr); - defineAccessorProperty(QStringLiteral("length"), method_get_length, nullptr); defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Primitive::fromInt32(operations[ctor->d()->type].bytesPerElement)); - - defineDefaultProperty(QStringLiteral("set"), method_set, 1); - defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 0); } -ReturnedValue TypedArrayPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int) +ReturnedValue IntrinsicTypedArrayPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); const TypedArray *v = thisObject->as<TypedArray>(); @@ -419,7 +427,7 @@ ReturnedValue TypedArrayPrototype::method_get_buffer(const FunctionObject *b, co return v->d()->buffer->asReturnedValue(); } -ReturnedValue TypedArrayPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int) +ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); const TypedArray *v = thisObject->as<TypedArray>(); @@ -429,7 +437,7 @@ ReturnedValue TypedArrayPrototype::method_get_byteLength(const FunctionObject *b return Encode(v->d()->byteLength); } -ReturnedValue TypedArrayPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int) +ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); const TypedArray *v = thisObject->as<TypedArray>(); @@ -439,7 +447,7 @@ ReturnedValue TypedArrayPrototype::method_get_byteOffset(const FunctionObject *b return Encode(v->d()->byteOffset); } -ReturnedValue TypedArrayPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int) +ReturnedValue IntrinsicTypedArrayPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); const TypedArray *v = thisObject->as<TypedArray>(); @@ -449,7 +457,43 @@ ReturnedValue TypedArrayPrototype::method_get_length(const FunctionObject *b, co return Encode(v->d()->byteLength/v->d()->type->bytesPerElement); } -ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +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<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O)); + ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind; + return ao->asReturnedValue(); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<TypedArray> O(scope, thisObject); + if (!O) + THROW_TYPE_ERROR(); + + Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O)); + ao->d()->iterationKind = IteratorKind::KeyIteratorKind; + return ao->asReturnedValue(); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + Scoped<TypedArray> O(scope, thisObject); + if (!O) + THROW_TYPE_ERROR(); + + Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O)); + ao->d()->iterationKind = IteratorKind::ValueIteratorKind; + return ao->asReturnedValue(); +} + +ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); Scoped<TypedArray> a(scope, *thisObject); @@ -538,7 +582,7 @@ ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Val RETURN_UNDEFINED(); } -ReturnedValue TypedArrayPrototype::method_subarray(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) +ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) { Scope scope(builtin); Scoped<TypedArray> a(scope, *thisObject); @@ -578,3 +622,47 @@ ReturnedValue TypedArrayPrototype::method_subarray(const FunctionObject *builtin arguments[2] = Encode(newLen); return constructor->callAsConstructor(arguments, 3); } + +ReturnedValue IntrinsicTypedArrayPrototype::method_get_toStringTag(const FunctionObject *, const Value *thisObject, const Value *, int) +{ + const TypedArray *a = thisObject->as<TypedArray>(); + if (!a) + return Encode::undefined(); + + return a->engine()->newString(QString::fromLatin1(a->d()->type->name))->asReturnedValue(); +} + +ReturnedValue IntrinsicTypedArrayCtor::callAsConstructor(const FunctionObject *f, const Value *, int) +{ + return f->engine()->throwTypeError(); +} + +ReturnedValue IntrinsicTypedArrayCtor::call(const FunctionObject *f, const Value *, const Value *, int) +{ + return f->engine()->throwTypeError(); +} + +void IntrinsicTypedArrayPrototype::init(ExecutionEngine *engine, IntrinsicTypedArrayCtor *ctor) +{ + ctor->defineReadonlyProperty(engine->id_prototype(), *this); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); + ctor->addSymbolSpecies(); + + defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr); + defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr); + defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr); + defineAccessorProperty(QStringLiteral("length"), method_get_length, nullptr); + + defineDefaultProperty(QStringLiteral("entries"), method_entries, 0); + defineDefaultProperty(QStringLiteral("keys"), method_keys, 0); + defineDefaultProperty(QStringLiteral("set"), method_set, 1); + defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 0); + + Scope scope(engine); + ScopedString valuesString(scope, engine->newIdentifier(QStringLiteral("values"))); + ScopedObject values(scope, FunctionObject::createBuiltinFunction(engine, valuesString, method_values, 0)); + defineDefaultProperty(QStringLiteral("values"), values); + defineDefaultProperty(engine->symbol_iterator(), values); + + defineAccessorProperty(engine->symbol_toStringTag(), method_get_toStringTag, nullptr); +} diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index 129c662c97..43ff1ec5b7 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -97,12 +97,18 @@ DECLARE_HEAP_OBJECT(TypedArray, Object) { void init(Type t); }; +struct IntrinsicTypedArrayCtor : FunctionObject { +}; + struct TypedArrayCtor : FunctionObject { void init(QV4::ExecutionContext *scope, TypedArray::Type t); TypedArray::Type type; }; +struct IntrinsicTypedArrayPrototype : Object { +}; + struct TypedArrayPrototype : Object { inline void init(TypedArray::Type t); TypedArray::Type type; @@ -137,6 +143,14 @@ struct Q_QML_PRIVATE_EXPORT TypedArray : Object static bool putIndexed(Managed *m, uint index, const Value &value); }; +struct IntrinsicTypedArrayCtor: FunctionObject +{ + V4_OBJECT2(IntrinsicTypedArrayCtor, FunctionObject) + + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + struct TypedArrayCtor: FunctionObject { V4_OBJECT2(TypedArrayCtor, FunctionObject) @@ -145,21 +159,34 @@ struct TypedArrayCtor: FunctionObject static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; - -struct TypedArrayPrototype : Object +struct IntrinsicTypedArrayPrototype : Object { - V4_OBJECT2(TypedArrayPrototype, Object) + V4_OBJECT2(IntrinsicTypedArrayPrototype, Object) V4_PROTOTYPE(objectPrototype) - void init(ExecutionEngine *engine, TypedArrayCtor *ctor); + void init(ExecutionEngine *engine, IntrinsicTypedArrayCtor *ctor); static ReturnedValue method_get_buffer(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_get_byteLength(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); 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_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_set(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_get_toStringTag(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + +}; + +struct TypedArrayPrototype : Object +{ + V4_OBJECT2(TypedArrayPrototype, Object) + V4_PROTOTYPE(objectPrototype) + + void init(ExecutionEngine *engine, TypedArrayCtor *ctor); }; inline void diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp index 0d4711df3c..9febd41a00 100644 --- a/src/qml/jsruntime/qv4value.cpp +++ b/src/qml/jsruntime/qv4value.cpp @@ -40,6 +40,7 @@ #include <qv4runtime_p.h> #include <qv4string_p.h> #ifndef V4_BOOTSTRAP +#include <qv4symbol_p.h> #include <qv4object_p.h> #include <qv4objectproto_p.h> #include <private/qv4mm_p.h> @@ -84,7 +85,7 @@ bool Value::toBooleanImpl(Value val) #ifdef V4_BOOTSTRAP Q_UNIMPLEMENTED(); #else - if (b->vtable()->isString) + if (b->internalClass->vtable->isString) return static_cast<Heap::String *>(b)->length() > 0; #endif return true; @@ -107,6 +108,11 @@ double Value::toNumberImpl(Value val) #else if (String *s = val.stringValue()) return RuntimeHelpers::stringToNumber(s->toQString()); + if (val.isSymbol()) { + Managed &m = static_cast<Managed &>(val); + m.engine()->throwTypeError(); + return 0; + } { Q_ASSERT(val.isObject()); Scope scope(val.objectValue()->engine()); @@ -145,6 +151,8 @@ QString Value::toQStringNoThrow() const case Value::Managed_Type: if (String *s = stringValue()) return s->toQString(); + if (Symbol *s = symbolValue()) + return s->descriptiveString(); { Q_ASSERT(isObject()); Scope scope(objectValue()->engine()); @@ -197,9 +205,12 @@ QString Value::toQString() const else return QStringLiteral("false"); case Value::Managed_Type: - if (String *s = stringValue()) + if (String *s = stringValue()) { return s->toQString(); - { + } else if (isSymbol()) { + static_cast<const Managed *>(this)->engine()->throwTypeError(); + return QString(); + } else { Q_ASSERT(isObject()); Scope scope(objectValue()->engine()); ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT)); @@ -217,6 +228,17 @@ QString Value::toQString() const } } // switch } + +Heap::StringOrSymbol *Value::toPropertyKey(ExecutionEngine *e) const +{ + Scope scope(e); + ScopedValue v(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT)); + if (!v->isStringOrSymbol()) + v = v->toString(e); + if (e->hasException) + return nullptr; + return static_cast<Heap::StringOrSymbol *>(v->m()); +} #endif // V4_BOOTSTRAP bool Value::sameValue(Value other) const { @@ -235,6 +257,25 @@ bool Value::sameValue(Value other) const { return false; } +bool Value::sameValueZero(Value other) const { + if (_val == other._val) + return true; + String *s = stringValue(); + String *os = other.stringValue(); + if (s && os) + return s->isEqualTo(os); + if (isInteger() && other.isDouble()) + return double(int_32()) == other.doubleValue(); + if (isDouble() && other.isInteger()) + return other.int_32() == doubleValue(); + if (isDouble() && other.isDouble()) { + if (doubleValue() == 0 && other.doubleValue() == 0) { + return true; + } + } + return false; +} + #ifndef V4_BOOTSTRAP Heap::String *Value::toString(ExecutionEngine *e, Value val) { diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index a5ee6b5373..16bbf241ff 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -56,6 +56,7 @@ #include <QtCore/QString> #include "qv4global_p.h" #include <private/qv4heap_p.h> +#include <private/qv4internalclass_p.h> #include <private/qnumeric_p.h> @@ -184,23 +185,7 @@ public: QML_NEARLY_ALWAYS_INLINE void setEmpty() { - setTagValue(quint32(ValueTypeInternal::Empty), value()); - } - - QML_NEARLY_ALWAYS_INLINE void setEmpty(int i) - { - setTagValue(quint32(ValueTypeInternal::Empty), quint32(i)); - } - - QML_NEARLY_ALWAYS_INLINE void setEmpty(quint32 i) - { - setTagValue(quint32(ValueTypeInternal::Empty), i); - } - - QML_NEARLY_ALWAYS_INLINE quint32 emptyValue() - { - Q_ASSERT(isEmpty()); - return quint32(value()); + setTagValue(quint32(ValueTypeInternal::Empty), 0); } // ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible @@ -248,7 +233,8 @@ public: IsManagedOrUndefined_Shift = 64-15, IsIntegerConvertible_Shift = 64-15, IsIntegerOrBool_Shift = 64-16, - QuickType_Shift = 64 - 17 + QuickType_Shift = 64 - 17, + IsPositiveIntShift = 31 }; static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49 @@ -315,6 +301,14 @@ public: } inline bool isNaN() const { return (tag() & 0x7ffc0000 ) == 0x00040000; } + inline bool isPositiveInt() const { +#if QT_POINTER_SIZE == 4 + return isInteger() && int_32() >= 0; +#else + return (_val >> IsPositiveIntShift) == (quint64(ValueTypeInternal::Integer) << 1); +#endif + } + QML_NEARLY_ALWAYS_INLINE double doubleValue() const { Q_ASSERT(isDouble()); double d; @@ -331,6 +325,8 @@ public: Q_ASSERT(isDouble()); } inline bool isString() const; + inline bool isStringOrSymbol() const; + inline bool isSymbol() const; inline bool isObject() const; inline bool isFunctionObject() const; inline bool isInt32() { @@ -362,7 +358,17 @@ public: QML_NEARLY_ALWAYS_INLINE String *stringValue() const { if (!isString()) return nullptr; - return reinterpret_cast<String*>(const_cast<Value *>(this)); + return reinterpret_cast<String *>(const_cast<Value *>(this)); + } + QML_NEARLY_ALWAYS_INLINE StringOrSymbol *stringOrSymbolValue() const { + if (!isStringOrSymbol()) + return nullptr; + return reinterpret_cast<StringOrSymbol *>(const_cast<Value *>(this)); + } + QML_NEARLY_ALWAYS_INLINE Symbol *symbolValue() const { + if (!isSymbol()) + return nullptr; + return reinterpret_cast<Symbol *>(const_cast<Value *>(this)); } QML_NEARLY_ALWAYS_INLINE Object *objectValue() const { if (!isObject()) @@ -388,6 +394,8 @@ public: int toUInt16() const; inline int toInt32() const; inline unsigned int toUInt32() const; + qint64 toLength() const; + inline qint64 toIndex() const; bool toBoolean() const { if (integerCompatible()) @@ -407,6 +415,8 @@ public: return reinterpret_cast<Heap::String *>(m()); return toString(e, *this); } + Heap::StringOrSymbol *toPropertyKey(ExecutionEngine *e) const; + static Heap::String *toString(ExecutionEngine *e, Value val); Heap::Object *toObject(ExecutionEngine *e) const { if (isObject()) @@ -428,11 +438,11 @@ public: if (!isManaged()) return nullptr; - Q_ASSERT(m()->vtable()); + Q_ASSERT(m()->internalClass->vtable); #if !defined(QT_NO_QOBJECT_CHECK) static_cast<const T *>(this)->qt_check_for_QMANAGED_macro(static_cast<const T *>(this)); #endif - const VTable *vt = m()->vtable(); + const VTable *vt = m()->internalClass->vtable; while (vt) { if (vt == T::staticVTable()) return static_cast<const T *>(this); @@ -465,8 +475,9 @@ public: ReturnedValue asReturnedValue() const { return _val; } static Value fromReturnedValue(ReturnedValue val) { Value v; v._val = val; return v; } - // Section 9.12 + // As per ES specs bool sameValue(Value other) const; + bool sameValueZero(Value other) const; inline void mark(MarkStack *markStack); @@ -500,18 +511,32 @@ inline void Value::mark(MarkStack *markStack) inline bool Value::isString() const { Heap::Base *b = heapObject(); - return b && b->vtable()->isString; + return b && b->internalClass->vtable->isString; +} + +bool Value::isStringOrSymbol() const +{ + Heap::Base *b = heapObject(); + return b && b->internalClass->vtable->isStringOrSymbol; } + +bool Value::isSymbol() const +{ + Heap::Base *b = heapObject(); + return b && b->internalClass->vtable->isStringOrSymbol && !b->internalClass->vtable->isString; +} + inline bool Value::isObject() const + { Heap::Base *b = heapObject(); - return b && b->vtable()->isObject; + return b && b->internalClass->vtable->isObject; } inline bool Value::isFunctionObject() const { Heap::Base *b = heapObject(); - return b && b->vtable()->isFunctionObject; + return b && b->internalClass->vtable->isFunctionObject; } inline bool Value::isPrimitive() const @@ -532,33 +557,26 @@ inline double Value::toNumber() const #ifndef V4_BOOTSTRAP inline uint Value::asArrayIndex() const { -#if QT_POINTER_SIZE == 8 - if (!isNumber()) - return UINT_MAX; - if (isInteger()) - return int_32() >= 0 ? (uint)int_32() : UINT_MAX; -#else - if (isInteger() && int_32() >= 0) + if (Q_LIKELY(isPositiveInt())) { return (uint)int_32(); - if (!isDouble()) + } + if (Q_UNLIKELY(!isDouble())) return UINT_MAX; -#endif double d = doubleValue(); uint idx = (uint)d; - if (idx != d) - return UINT_MAX; - return idx; + if (idx == d) + return idx; + return UINT_MAX; } inline bool Value::asArrayIndex(uint &idx) const { - if (Q_LIKELY(!isDouble())) { - if (Q_LIKELY(isInteger() && int_32() >= 0)) { - idx = (uint)int_32(); - return true; - } - return false; + if (Q_LIKELY(isPositiveInt())) { + idx = (uint)int_32(); + return true; } + if (Q_UNLIKELY(!isDouble())) + return false; double d = doubleValue(); idx = (uint)d; return (idx == d && idx != UINT_MAX); @@ -576,7 +594,6 @@ ReturnedValue Heap::Base::asReturnedValue() const struct Q_QML_PRIVATE_EXPORT Primitive : public Value { inline static Primitive emptyValue(); - inline static Primitive emptyValue(uint v); static inline Primitive fromBoolean(bool b); static inline Primitive fromInt32(int i); inline static Primitive undefinedValue(); @@ -602,14 +619,7 @@ inline Primitive Primitive::undefinedValue() inline Primitive Primitive::emptyValue() { Primitive v; - v.setEmpty(0); - return v; -} - -inline Primitive Primitive::emptyValue(uint e) -{ - Primitive v; - v.setEmpty(e); + v.setEmpty(); return v; } @@ -788,6 +798,31 @@ inline unsigned int Value::toUInt32() const return static_cast<unsigned int>(toInt32()); } +inline qint64 Value::toLength() const +{ + if (Q_LIKELY(integerCompatible())) + return int_32() < 0 ? 0 : int_32(); + double i = Primitive::toInteger(isDouble() ? doubleValue() : toNumberImpl()); + if (i <= 0) + return 0; + if (i > (static_cast<qint64>(1) << 53) - 1) + return (static_cast<qint64>(1) << 53) - 1; + return static_cast<qint64>(i); +} + +inline qint64 Value::toIndex() const +{ + qint64 idx; + if (Q_LIKELY(integerCompatible())) { + idx = int_32(); + } else { + idx = static_cast<qint64>(Primitive::toInteger(isDouble() ? doubleValue() : toNumberImpl())); + } + if (idx > (static_cast<qint64>(1) << 53) - 1) + idx = -1; + return idx; +} + inline double Value::toInteger() const { if (integerCompatible()) @@ -831,7 +866,7 @@ struct ValueArray { WriteBarrier::write(e, base(), values[index].data_ptr(), v.asReturnedValue()); } void set(EngineBase *e, uint index, Heap::Base *b) { - WriteBarrier::write(e, base(), values[index].data_ptr(), b->asReturnedValue()); + WriteBarrier::write(e, base(), values[index].data_ptr(), Value::fromHeapObject(b).asReturnedValue()); } inline const Value &operator[] (uint index) const { Q_ASSERT(index < alloc); @@ -884,7 +919,6 @@ struct ValueArray { // have wrong offsets between host and target. Q_STATIC_ASSERT(offsetof(ValueArray<0>, values) == 8); - } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index a1f5b01fa9..f625368b62 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -55,12 +55,13 @@ #include <private/qv4string_p.h> #include <private/qv4profiling_p.h> #include <private/qv4jscall_p.h> +#include <private/qv4generatorobject_p.h> #include <private/qqmljavascriptexpression_p.h> #include <iostream> #include "qv4alloca_p.h" -#include <private/qv4jit_p.h> +#include <private/qv4baselinejit_p.h> #undef COUNT_INSTRUCTIONS @@ -343,7 +344,7 @@ static struct InstrCount { #endif #define CHECK_EXCEPTION \ if (engine->hasException) \ - goto catchException + goto handleUnwind static inline Heap::CallContext *getScope(QV4::Value *stack, int level) { @@ -361,95 +362,6 @@ static inline const QV4::Value &constant(Function *function, int index) return function->compilationUnit->constants[index]; } - -static bool compareEqual(QV4::Value lhs, QV4::Value rhs) -{ - redo: - if (lhs.asReturnedValue() == rhs.asReturnedValue()) - return !lhs.isNaN(); - - int lt = lhs.quickType(); - int rt = rhs.quickType(); - if (rt < lt) { - qSwap(lhs, rhs); - qSwap(lt, rt); - } - - switch (lt) { - case QV4::Value::QT_ManagedOrUndefined: - if (lhs.isUndefined()) - return rhs.isNullOrUndefined(); - Q_FALLTHROUGH(); - case QV4::Value::QT_ManagedOrUndefined1: - case QV4::Value::QT_ManagedOrUndefined2: - case QV4::Value::QT_ManagedOrUndefined3: - // LHS: Managed - switch (rt) { - case QV4::Value::QT_ManagedOrUndefined: - if (rhs.isUndefined()) - return false; - Q_FALLTHROUGH(); - case QV4::Value::QT_ManagedOrUndefined1: - case QV4::Value::QT_ManagedOrUndefined2: - case QV4::Value::QT_ManagedOrUndefined3: { - // RHS: Managed - Heap::Base *l = lhs.m(); - Heap::Base *r = rhs.m(); - Q_ASSERT(l); - Q_ASSERT(r); - if (l->vtable()->isString == r->vtable()->isString) - return static_cast<QV4::Managed &>(lhs).isEqualTo(&static_cast<QV4::Managed &>(rhs)); - if (l->vtable()->isString) { - rhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(rhs), PREFERREDTYPE_HINT)); - break; - } else { - Q_ASSERT(r->vtable()->isString); - lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT)); - break; - } - return false; - } - case QV4::Value::QT_Empty: - Q_UNREACHABLE(); - case QV4::Value::QT_Null: - return false; - case QV4::Value::QT_Bool: - case QV4::Value::QT_Int: - rhs = Primitive::fromDouble(rhs.int_32()); - // fall through - default: // double - if (lhs.m()->vtable()->isString) - return RuntimeHelpers::toNumber(lhs) == rhs.doubleValue(); - else - lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT)); - } - goto redo; - case QV4::Value::QT_Empty: - Q_UNREACHABLE(); - case QV4::Value::QT_Null: - return rhs.isNull(); - case QV4::Value::QT_Bool: - case QV4::Value::QT_Int: - switch (rt) { - case QV4::Value::QT_ManagedOrUndefined: - case QV4::Value::QT_ManagedOrUndefined1: - case QV4::Value::QT_ManagedOrUndefined2: - case QV4::Value::QT_ManagedOrUndefined3: - case QV4::Value::QT_Empty: - case QV4::Value::QT_Null: - Q_UNREACHABLE(); - case QV4::Value::QT_Bool: - case QV4::Value::QT_Int: - return lhs.int_32() == rhs.int_32(); - default: // double - return lhs.int_32() == rhs.doubleValue(); - } - default: // double - Q_ASSERT(rhs.isDouble()); - return lhs.doubleValue() == rhs.doubleValue(); - } -} - static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs) { redo: @@ -462,7 +374,7 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs) case QV4::Value::QT_ManagedOrUndefined2: case QV4::Value::QT_ManagedOrUndefined3: // LHS: Managed - if (lhs.m()->vtable()->isString) + if (lhs.m()->internalClass->vtable->isString) return RuntimeHelpers::stringToNumber(static_cast<String &>(lhs).toQString()) == rhs; accumulator = lhs; lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(accumulator), PREFERREDTYPE_HINT)); @@ -479,7 +391,7 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs) } } -#define STORE_IP() frame.instructionPointer = int(code - codeStart); +#define STORE_IP() frame.instructionPointer = int(code - function->codeData); #define STORE_ACC() accumulator = acc; #define ACC Primitive::fromReturnedValue(acc) #define VALUE_TO_INT(i, val) \ @@ -508,6 +420,11 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj CppStackFrame frame; frame.originalArguments = argv; frame.originalArgumentsCount = argc; + frame.yield = nullptr; + frame.unwindHandler = nullptr; + frame.unwindLabel = nullptr; + frame.unwindLevel = 0; + Function *function; { @@ -553,10 +470,6 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling QV4::Debugging::Debugger *debugger = engine->debugger(); - const uchar *exceptionHandler = nullptr; - - QV4::Value &accumulator = frame.jsFrame->accumulator; - QV4::ReturnedValue acc = Encode::undefined(); #ifdef V4_ENABLE_JIT if (function->jittedCode == nullptr && debugger == nullptr) { @@ -570,12 +483,29 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj if (debugger) debugger->enteringFunction(); + ReturnedValue result; if (function->jittedCode != nullptr && debugger == nullptr) { - acc = function->jittedCode(&frame, engine); + result = function->jittedCode(&frame, engine); } else { // interpreter - const uchar *code = function->codeData; - const uchar *codeStart = code; + result = interpret(frame, function->codeData); + } + + if (QV4::Debugging::Debugger *debugger = engine->debugger()) + debugger->leavingFunction(result); + engine->currentStackFrame = frame.parent; + engine->jsStackTop = stack; + + return result; +} + +QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code) +{ + QV4::Function *function = frame.v4Function; + QV4::Value &accumulator = frame.jsFrame->accumulator; + QV4::ReturnedValue acc = accumulator.asReturnedValue(); + Value *stack = reinterpret_cast<Value *>(frame.jsFrame); + ExecutionEngine *engine = function->internalClass->engine; MOTH_JUMP_TABLE; @@ -629,12 +559,14 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj MOTH_BEGIN_INSTR(LoadLocal) auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m()); + Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext); acc = cc->locals[index].asReturnedValue(); MOTH_END_INSTR(LoadLocal) MOTH_BEGIN_INSTR(StoreLocal) CHECK_EXCEPTION; auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m()); + Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext); QV4::WriteBarrier::write(engine, cc, cc->locals.values[index].data_ptr(), acc); MOTH_END_INSTR(StoreLocal) @@ -689,16 +621,10 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj MOTH_BEGIN_INSTR(LoadElement) STORE_IP(); - acc = Runtime::method_loadElement(engine, STACK_VALUE(base), STACK_VALUE(index)); - CHECK_EXCEPTION; - MOTH_END_INSTR(LoadElement) - - MOTH_BEGIN_INSTR(LoadElementA) - STORE_IP(); STORE_ACC(); acc = Runtime::method_loadElement(engine, STACK_VALUE(base), accumulator); CHECK_EXCEPTION; - MOTH_END_INSTR(LoadElementA) + MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) STORE_IP(); @@ -710,16 +636,10 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj MOTH_BEGIN_INSTR(LoadProperty) STORE_IP(); - acc = Runtime::method_loadProperty(engine, STACK_VALUE(base), name); - CHECK_EXCEPTION; - MOTH_END_INSTR(LoadProperty) - - MOTH_BEGIN_INSTR(LoadPropertyA) - STORE_IP(); STORE_ACC(); acc = Runtime::method_loadProperty(engine, accumulator, name); CHECK_EXCEPTION; - MOTH_END_INSTR(LoadPropertyA) + MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) STORE_IP(); @@ -784,12 +704,30 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj CHECK_EXCEPTION; MOTH_END_INSTR(LoadIdObject) + MOTH_BEGIN_INSTR(Yield) + frame.yield = code; + return acc; + MOTH_END_INSTR(Yield) + + MOTH_BEGIN_INSTR(Resume) + // check exception, in case the generator was called with throw() or return() + if (engine->hasException) { + // an empty value indicates that the generator was called with return() + if (engine->exceptionValue->asReturnedValue() != Primitive::emptyValue().asReturnedValue()) + goto handleUnwind; + engine->hasException = false; + *engine->exceptionValue = Primitive::undefinedValue(); + } else { + code += offset; + } + MOTH_END_INSTR(Resume) + MOTH_BEGIN_INSTR(CallValue) STORE_IP(); Value func = STACK_VALUE(name); if (Q_UNLIKELY(!func.isFunctionObject())) { acc = engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow())); - goto catchException; + goto handleUnwind; } acc = static_cast<const FunctionObject &>(func).call(nullptr, stack + argv, argc); CHECK_EXCEPTION; @@ -809,7 +747,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj if (Q_UNLIKELY(!f.isFunctionObject())) { acc = engine->throwTypeError(); - goto catchException; + goto handleUnwind; } acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc); @@ -852,15 +790,49 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj CHECK_EXCEPTION; MOTH_END_INSTR(CallContextObjectProperty) - MOTH_BEGIN_INSTR(SetExceptionHandler) - exceptionHandler = offset ? code + offset : nullptr; - MOTH_END_INSTR(SetExceptionHandler) + MOTH_BEGIN_INSTR(CallWithSpread) + STORE_IP(); + acc = Runtime::method_callWithSpread(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc); + CHECK_EXCEPTION; + MOTH_END_INSTR(CallWithSpread) + + MOTH_BEGIN_INSTR(Construct) + STORE_IP(); + acc = Runtime::method_construct(engine, STACK_VALUE(func), stack + argv, argc); + CHECK_EXCEPTION; + MOTH_END_INSTR(Construct) + + MOTH_BEGIN_INSTR(ConstructWithSpread) + STORE_IP(); + acc = Runtime::method_constructWithSpread(engine, STACK_VALUE(func), stack + argv, argc); + CHECK_EXCEPTION; + MOTH_END_INSTR(ConstructWithSpread) + + MOTH_BEGIN_INSTR(SetUnwindHandler) + frame.unwindHandler = offset ? code + offset : nullptr; + MOTH_END_INSTR(SetUnwindHandler) + + MOTH_BEGIN_INSTR(UnwindDispatch) + CHECK_EXCEPTION; + if (frame.unwindLevel) { + --frame.unwindLevel; + if (frame.unwindLevel) + goto handleUnwind; + code = frame.unwindLabel; + } + MOTH_END_INSTR(UnwindDispatch) + + MOTH_BEGIN_INSTR(UnwindToLabel) + frame.unwindLevel = level; + frame.unwindLabel = code + offset; + goto handleUnwind; + MOTH_END_INSTR(UnwindToLabel) MOTH_BEGIN_INSTR(ThrowException) STORE_IP(); STORE_ACC(); Runtime::method_throwException(engine, accumulator); - goto catchException; + goto handleUnwind; MOTH_END_INSTR(ThrowException) MOTH_BEGIN_INSTR(GetException) @@ -870,14 +842,15 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj MOTH_END_INSTR(HasException) MOTH_BEGIN_INSTR(SetException) - *engine->exceptionValue = acc; - engine->hasException = true; + if (acc != Primitive::emptyValue().asReturnedValue()) { + *engine->exceptionValue = acc; + engine->hasException = true; + } MOTH_END_INSTR(SetException) MOTH_BEGIN_INSTR(PushCatchContext) - STACK_VALUE(reg) = STACK_VALUE(CallData::Context); ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); - STACK_VALUE(CallData::Context) = Runtime::method_createCatchContext(c, name); + STACK_VALUE(CallData::Context) = Runtime::method_createCatchContext(c, index, name); MOTH_END_INSTR(PushCatchContext) MOTH_BEGIN_INSTR(CreateCallContext) @@ -887,54 +860,72 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj MOTH_BEGIN_INSTR(PushWithContext) STORE_IP(); STORE_ACC(); - accumulator = accumulator.toObject(engine); + auto ctx = Runtime::method_createWithContext(engine, stack); CHECK_EXCEPTION; - STACK_VALUE(reg) = STACK_VALUE(CallData::Context); - ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); - STACK_VALUE(CallData::Context) = Runtime::method_createWithContext(c, accumulator); + STACK_VALUE(CallData::Context) = ctx; MOTH_END_INSTR(PushWithContext) + MOTH_BEGIN_INSTR(PushBlockContext) + STORE_ACC(); + ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); + STACK_VALUE(CallData::Context) = Runtime::method_createBlockContext(c, index); + MOTH_END_INSTR(PushBlockContext) + + MOTH_BEGIN_INSTR(CloneBlockContext) + STORE_ACC(); + ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); + STACK_VALUE(CallData::Context) = Runtime::method_cloneBlockContext(c); + MOTH_END_INSTR(CloneBlockContext) + + MOTH_BEGIN_INSTR(PushScriptContext) + STACK_VALUE(CallData::Context) = Runtime::method_createScriptContext(engine, index); + MOTH_END_INSTR(PushScriptContext) + + MOTH_BEGIN_INSTR(PopScriptContext) + STACK_VALUE(CallData::Context) = Runtime::method_popScriptContext(engine); + MOTH_END_INSTR(PopScriptContext) + MOTH_BEGIN_INSTR(PopContext) - STACK_VALUE(CallData::Context) = STACK_VALUE(reg); + ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); + STACK_VALUE(CallData::Context) = c->d()->outer; MOTH_END_INSTR(PopContext) - MOTH_BEGIN_INSTR(ForeachIteratorObject) + MOTH_BEGIN_INSTR(GetIterator) STORE_ACC(); - acc = Runtime::method_foreachIterator(engine, accumulator); + acc = Runtime::method_getIterator(engine, accumulator, iterator); CHECK_EXCEPTION; - MOTH_END_INSTR(ForeachIteratorObject) + MOTH_END_INSTR(GetIterator) - MOTH_BEGIN_INSTR(ForeachNextPropertyName) + MOTH_BEGIN_INSTR(IteratorNext) STORE_ACC(); - acc = Runtime::method_foreachNextPropertyName(accumulator); + acc = Runtime::method_iteratorNext(engine, accumulator, &STACK_VALUE(value)); CHECK_EXCEPTION; - MOTH_END_INSTR(ForeachNextPropertyName) + MOTH_END_INSTR(IteratorNext) - MOTH_BEGIN_INSTR(DeleteMember) - if (!Runtime::method_deleteMember(engine, STACK_VALUE(base), member)) { - if (function->isStrict()) { - STORE_IP(); - engine->throwTypeError(); - goto catchException; - } - acc = Encode(false); - } else { - acc = Encode(true); - } - MOTH_END_INSTR(DeleteMember) + MOTH_BEGIN_INSTR(IteratorClose) + STORE_ACC(); + acc = Runtime::method_iteratorClose(engine, accumulator, STACK_VALUE(done)); + CHECK_EXCEPTION; + MOTH_END_INSTR(IteratorClose) + + MOTH_BEGIN_INSTR(DestructureRestElement) + STORE_ACC(); + acc = Runtime::method_destructureRestElement(engine, ACC); + CHECK_EXCEPTION; + MOTH_END_INSTR(DestructureRestElement) - MOTH_BEGIN_INSTR(DeleteSubscript) - if (!Runtime::method_deleteElement(engine, STACK_VALUE(base), STACK_VALUE(index))) { + MOTH_BEGIN_INSTR(DeleteProperty) + if (!Runtime::method_deleteProperty(engine, STACK_VALUE(base), STACK_VALUE(index))) { if (function->isStrict()) { STORE_IP(); engine->throwTypeError(); - goto catchException; + goto handleUnwind; } acc = Encode(false); } else { acc = Encode(true); } - MOTH_END_INSTR(DeleteSubscript) + MOTH_END_INSTR(DeleteProperty) MOTH_BEGIN_INSTR(DeleteName) if (!Runtime::method_deleteName(engine, name)) { @@ -942,7 +933,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj STORE_IP(); QString n = function->compilationUnit->runtimeStrings[name]->toQString(); engine->throwSyntaxError(QStringLiteral("Can't delete property %1").arg(n)); - goto catchException; + goto handleUnwind; } acc = Encode(false); } else { @@ -970,7 +961,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj MOTH_BEGIN_INSTR(DefineObjectLiteral) QV4::Value *arguments = stack + args; - acc = Runtime::method_objectLiteral(engine, arguments, internalClassId, arrayValueCount, arrayGetterSetterCountAndFlags); + acc = Runtime::method_objectLiteral(engine, internalClassId, argc, arguments); MOTH_END_INSTR(DefineObjectLiteral) MOTH_BEGIN_INSTR(CreateMappedArgumentsObject) @@ -981,6 +972,10 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj acc = Runtime::method_createUnmappedArgumentsObject(engine); MOTH_END_INSTR(CreateUnmappedArgumentsObject) + MOTH_BEGIN_INSTR(CreateRestParameter) + acc = Runtime::method_createRestParameter(engine, argIndex); + MOTH_END_INSTR(CreateRestParameter) + MOTH_BEGIN_INSTR(ConvertThisToObject) Value *t = &stack[CallData::This]; if (!t->isObject()) { @@ -993,11 +988,10 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj } MOTH_END_INSTR(ConvertThisToObject) - MOTH_BEGIN_INSTR(Construct) - STORE_IP(); - acc = Runtime::method_construct(engine, STACK_VALUE(func), stack + argv, argc); + MOTH_BEGIN_INSTR(ToObject) + acc = ACC.toObject(engine)->asReturnedValue(); CHECK_EXCEPTION; - MOTH_END_INSTR(Construct) + MOTH_END_INSTR(ToObject) MOTH_BEGIN_INSTR(Jump) code += offset; @@ -1023,6 +1017,16 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj } MOTH_END_INSTR(JumpFalse) + MOTH_BEGIN_INSTR(JumpNoException) + if (!engine->hasException) + code += offset; + MOTH_END_INSTR(JumpNoException) + + MOTH_BEGIN_INSTR(JumpNotUndefined) + if (Q_LIKELY(acc != QV4::Encode::undefined())) + code += offset; + MOTH_END_INSTR(JumpNotUndefined) + MOTH_BEGIN_INSTR(CmpEqNull) acc = Encode(ACC.isNullOrUndefined()); MOTH_END_INSTR(CmpEqNull) @@ -1059,7 +1063,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj acc = Encode(left.int_32() == ACC.int_32()); } else { STORE_ACC(); - acc = Encode(compareEqual(left, accumulator)); + acc = Encode(bool(Runtime::method_compareEqual(left, accumulator))); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpEq) @@ -1070,7 +1074,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj acc = Encode(bool(left.int_32() != ACC.int_32())); } else { STORE_ACC(); - acc = Encode(!compareEqual(left, accumulator)); + acc = Encode(bool(!Runtime::method_compareEqual(left, accumulator))); CHECK_EXCEPTION; } MOTH_END_INSTR(CmpNe) @@ -1154,27 +1158,11 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj MOTH_END_INSTR(CmpIn) MOTH_BEGIN_INSTR(CmpInstanceOf) - // 11.8.6, 5: rval must be an Object - if (Q_UNLIKELY(!Primitive::fromReturnedValue(acc).isObject())) { - acc = engine->throwTypeError(); - goto catchException; - } - - // 11.8.6, 7: call "HasInstance", which we term instanceOf, and return the result. - acc = Primitive::fromReturnedValue(acc).objectValue()->instanceOf(STACK_VALUE(lhs)); + STORE_ACC(); + acc = Runtime::method_instanceof(engine, STACK_VALUE(lhs), ACC); CHECK_EXCEPTION; MOTH_END_INSTR(CmpInstanceOf) - MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt) - if (STACK_VALUE(lhs).int_32() != rhs || STACK_VALUE(lhs).isUndefined()) - code += offset; - MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt) - - MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt) - if (STACK_VALUE(lhs).int_32() == rhs && !STACK_VALUE(lhs).isUndefined()) - code += offset; - MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt) - MOTH_BEGIN_INSTR(UNot) if (ACC.integerCompatible()) { acc = Encode(!static_cast<bool>(ACC.int_32())); @@ -1259,6 +1247,16 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj } MOTH_END_INSTR(Sub) + MOTH_BEGIN_INSTR(Exp) + const Value left = STACK_VALUE(lhs); + double base = left.toNumber(); + double exp = ACC.toNumber(); + if (qIsInf(exp) && (base == 1 || base == -1)) + acc = Encode(qSNaN()); + else + acc = Encode(pow(base,exp)); + MOTH_END_INSTR(Exp) + MOTH_BEGIN_INSTR(Mul) const Value left = STACK_VALUE(lhs); if (Q_LIKELY(Value::integerCompatible(left, ACC))) { @@ -1351,7 +1349,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj MOTH_END_INSTR(ShlConst) MOTH_BEGIN_INSTR(Ret) - goto functionExit; + return acc; MOTH_END_INSTR(Ret) MOTH_BEGIN_INSTR(Debug) @@ -1369,21 +1367,12 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj STACK_VALUE(result) = Runtime::method_loadQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine)); MOTH_END_INSTR(LoadQmlImportedScripts) - catchException: - Q_ASSERT(engine->hasException); - if (!exceptionHandler) { + handleUnwind: + Q_ASSERT(engine->hasException || frame.unwindLevel); + if (!frame.unwindHandler) { acc = Encode::undefined(); - goto functionExit; + return acc; } - code = exceptionHandler; - } + code = frame.unwindHandler; } - -functionExit: - if (QV4::Debugging::Debugger *debugger = engine->debugger()) - debugger->leavingFunction(ACC.asReturnedValue()); - engine->currentStackFrame = frame.parent; - engine->jsStackTop = stack; - - return acc; } diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h index 3b7723ca7e..67bfd537c5 100644 --- a/src/qml/jsruntime/qv4vme_moth_p.h +++ b/src/qml/jsruntime/qv4vme_moth_p.h @@ -72,6 +72,7 @@ public: return exec(reinterpret_cast<const FunctionObject *>(d), thisObject, argv, argc); } static QV4::ReturnedValue exec(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc); + static QV4::ReturnedValue interpret(CppStackFrame &frame, const char *codeEntry); }; } // namespace Moth |