diff options
Diffstat (limited to 'src/qml/jsruntime')
116 files changed, 9776 insertions, 3029 deletions
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 4bc877bd9d..ec5803b2df 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -14,21 +14,29 @@ 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 \ $$PWD/qv4numberobject.cpp \ $$PWD/qv4object.cpp \ $$PWD/qv4objectproto.cpp \ + $$PWD/qv4propertykey.cpp \ + $$PWD/qv4proxy.cpp \ $$PWD/qv4qmlcontext.cpp \ + $$PWD/qv4reflect.cpp \ $$PWD/qv4regexpobject.cpp \ + $$PWD/qv4stackframe.cpp \ + $$PWD/qv4stringiterator.cpp \ $$PWD/qv4stringobject.cpp \ $$PWD/qv4variantobject.cpp \ $$PWD/qv4objectiterator.cpp \ @@ -36,13 +44,18 @@ 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 \ + $$PWD/qv4estable.cpp qtConfig(qml-debug): SOURCES += $$PWD/qv4profiling.cpp @@ -64,22 +77,30 @@ 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 \ $$PWD/qv4numberobject_p.h \ $$PWD/qv4object_p.h \ $$PWD/qv4objectproto_p.h \ + $$PWD/qv4propertykey_p.h \ + $$PWD/qv4proxy_p.h \ $$PWD/qv4qmlcontext_p.h \ + $$PWD/qv4reflect_p.h \ $$PWD/qv4regexpobject_p.h \ $$PWD/qv4runtimecodegen_p.h \ + $$PWD/qv4stackframe_p.h \ + $$PWD/qv4stringiterator_p.h \ $$PWD/qv4stringobject_p.h \ $$PWD/qv4variantobject_p.h \ $$PWD/qv4property_p.h \ @@ -87,16 +108,30 @@ 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 \ + $$PWD/qv4estable_p.h \ + $$PWD/qv4vtable_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..7b501b9fbb 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()->propertyKey())); setProperty(v4, CalleePropertyIndex, context->d()->function); - Q_ASSERT(LengthPropertyIndex == internalClass->find(v4->id_length())); + Q_ASSERT(LengthPropertyIndex == internalClass->find(v4->id_length()->propertyKey())); setProperty(v4, LengthPropertyIndex, Primitive::fromInt32(context->argc())); + Q_ASSERT(SymbolIteratorPropertyIndex == internalClass->find(v4->symbol_iterator()->propertyKey())); + 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()->propertyKey())); + Q_ASSERT(SymbolIteratorPropertyIndex == internalClass->find(v4->symbol_iterator()->propertyKey())); + 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()->propertyKey())); setProperty(v4, LengthPropertyIndex, Primitive::fromInt32(frame->originalArgumentsCount)); } @@ -117,106 +120,111 @@ void ArgumentsObject::fullyCreate() d()->fullyCreated = true; } -bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, const Property *desc, PropertyAttributes attrs) +bool ArgumentsObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *desc, PropertyAttributes attrs) { - fullyCreate(); + if (!id.isArrayIndex()) + return Object::virtualDefineOwnProperty(m, id, desc, attrs); - Scope scope(engine); + ArgumentsObject *a = static_cast<ArgumentsObject *>(m); + a->fullyCreate(); + + uint index = id.asArrayIndex(); + Scope scope(m); ScopedProperty map(scope); PropertyAttributes mapAttrs; - uint numAccessors = qMin(d()->nFormals, context()->argc()); + uint numAccessors = qMin(a->d()->nFormals, a->context()->argc()); bool isMapped = false; - if (arrayData() && index < numAccessors && - arrayData()->attributes(index).isAccessor() && - arrayData()->get(index) == scope.engine->argumentsAccessors[index].getter()->asReturnedValue()) + if (a->arrayData() && index < numAccessors && + a->arrayData()->attributes(index).isAccessor() && + a->arrayData()->get(index) == scope.engine->argumentsAccessors[index].getter()->asReturnedValue()) isMapped = true; if (isMapped) { - Q_ASSERT(arrayData()); - mapAttrs = arrayData()->attributes(index); - arrayData()->getProperty(index, map, &mapAttrs); - setArrayAttributes(index, Attr_Data); - ArrayData::Index arrayIndex{ arrayData(), arrayData()->mappedIndex(index) }; - arrayIndex.set(scope.engine, d()->mappedArguments->values[index]); + Q_ASSERT(a->arrayData()); + mapAttrs = a->arrayData()->attributes(index); + a->arrayData()->getProperty(index, map, &mapAttrs); + a->setArrayAttributes(index, Attr_Data); + PropertyIndex arrayIndex{ a->arrayData(), a->arrayData()->values.values + a->arrayData()->mappedIndex(index) }; + arrayIndex.set(scope.engine, a->d()->mappedArguments->values[index]); } - bool result = Object::defineOwnProperty2(scope.engine, index, desc, attrs); - if (!result) { + bool result = Object::virtualDefineOwnProperty(m, id, desc, attrs); + if (!result) return false; - } if (isMapped && attrs.isData()) { - Q_ASSERT(arrayData()); + Q_ASSERT(a->arrayData()); ScopedFunctionObject setter(scope, map->setter()); JSCallData jsCallData(scope, 1); - *jsCallData->thisObject = this->asReturnedValue(); + *jsCallData->thisObject = a->asReturnedValue(); jsCallData->args[0] = desc->value; setter->call(jsCallData); if (attrs.isWritable()) { - setArrayAttributes(index, mapAttrs); - arrayData()->setProperty(engine, index, map); + a->setArrayAttributes(index, mapAttrs); + a->arrayData()->setProperty(m->engine(), index, map); } } return result; } -ReturnedValue ArgumentsObject::getIndexed(const Managed *m, uint index, bool *hasProperty) +ReturnedValue ArgumentsObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m); - if (args->fullyCreated()) - return Object::getIndexed(m, index, hasProperty); - - if (index < static_cast<uint>(args->context()->argc())) { - if (hasProperty) - *hasProperty = true; - return args->context()->args()[index].asReturnedValue(); + if (id.isArrayIndex() && !args->fullyCreated()) { + uint index = id.asArrayIndex(); + if (index < static_cast<uint>(args->context()->argc())) { + if (hasProperty) + *hasProperty = true; + return args->context()->args()[index].asReturnedValue(); + } } - if (hasProperty) - *hasProperty = false; - return Encode::undefined(); + return Object::virtualGet(m, id, receiver, hasProperty); } -bool ArgumentsObject::putIndexed(Managed *m, uint index, const Value &value) +bool ArgumentsObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { ArgumentsObject *args = static_cast<ArgumentsObject *>(m); - if (!args->fullyCreated() && index >= static_cast<uint>(args->context()->argc())) - args->fullyCreate(); - - if (args->fullyCreated()) - return Object::putIndexed(m, index, value); - - args->context()->setArg(index, value); - return true; + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + if (!args->fullyCreated() && index >= static_cast<uint>(args->context()->argc())) + args->fullyCreate(); + + if (!args->fullyCreated()) { + args->context()->setArg(index, value); + return true; + } + } + return Object::virtualPut(m, id, value, receiver); } -bool ArgumentsObject::deleteIndexedProperty(Managed *m, uint index) +bool ArgumentsObject::virtualDeleteProperty(Managed *m, PropertyKey id) { ArgumentsObject *args = static_cast<ArgumentsObject *>(m); if (!args->fullyCreated()) args->fullyCreate(); - return Object::deleteIndexedProperty(m, index); + return Object::virtualDeleteProperty(m, id); } -PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index) +PropertyAttributes ArgumentsObject::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p) { const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m); - if (args->fullyCreated()) - return Object::queryIndexed(m, index); + if (!id.isArrayIndex() || args->fullyCreated()) + return Object::virtualGetOwnProperty(m, id, p); - uint numAccessors = qMin(args->d()->nFormals, args->context()->argc()); + uint index = id.asArrayIndex(); uint argCount = args->context()->argc(); if (index >= argCount) return PropertyAttributes(); - if (index >= numAccessors) - return Attr_Data; - return Attr_Accessor; + if (p) + p->value = args->context()->args()[index]; + return Attr_Data; } DEFINE_OBJECT_VTABLE(ArgumentsGetterFunction); -ReturnedValue ArgumentsGetterFunction::call(const FunctionObject *getter, const Value *thisObject, const Value *, int) +ReturnedValue ArgumentsGetterFunction::virtualCall(const FunctionObject *getter, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = getter->engine(); Scope scope(v4); @@ -231,7 +239,7 @@ ReturnedValue ArgumentsGetterFunction::call(const FunctionObject *getter, const DEFINE_OBJECT_VTABLE(ArgumentsSetterFunction); -ReturnedValue ArgumentsSetterFunction::call(const FunctionObject *setter, const Value *thisObject, const Value *argv, int argc) +ReturnedValue ArgumentsSetterFunction::virtualCall(const FunctionObject *setter, const Value *thisObject, const Value *argv, int argc) { ExecutionEngine *v4 = setter->engine(); Scope scope(v4); @@ -245,10 +253,8 @@ ReturnedValue ArgumentsSetterFunction::call(const FunctionObject *setter, const return Encode::undefined(); } -uint ArgumentsObject::getLength(const Managed *m) +qint64 ArgumentsObject::virtualGetLength(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..9ac9ac5d8b 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); }; @@ -108,7 +109,7 @@ struct ArgumentsGetterFunction: FunctionObject V4_OBJECT2(ArgumentsGetterFunction, FunctionObject) uint index() const { return d()->index; } - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; inline void @@ -123,7 +124,7 @@ struct ArgumentsSetterFunction: FunctionObject V4_OBJECT2(ArgumentsSetterFunction, FunctionObject) uint index() const { return d()->index; } - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; inline void @@ -142,15 +143,15 @@ 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); - static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - 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 bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *desc, PropertyAttributes attrs); + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); + static bool virtualDeleteProperty(Managed *m, PropertyKey id); + static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p); + static qint64 virtualGetLength(const Managed *m); void fullyCreate(); diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp index 59a2b9d913..f80c9a0ab2 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; @@ -52,7 +53,7 @@ void Heap::ArrayBufferCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("ArrayBuffer")); } -ReturnedValue ArrayBufferCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue ArrayBufferCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { ExecutionEngine *v4 = f->engine(); Scope scope(v4); @@ -73,9 +74,9 @@ ReturnedValue ArrayBufferCtor::callAsConstructor(const FunctionObject *f, const } -ReturnedValue ArrayBufferCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) +ReturnedValue ArrayBufferCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) { - return callAsConstructor(f, argv, argc); + return virtualCallAsConstructor(f, argv, argc, f); } ReturnedValue ArrayBufferCtor::method_isView(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -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/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h index 59e78ee85f..089dbc522f 100644 --- a/src/qml/jsruntime/qv4arraybuffer_p.h +++ b/src/qml/jsruntime/qv4arraybuffer_p.h @@ -78,8 +78,8 @@ struct ArrayBufferCtor: FunctionObject { V4_OBJECT2(ArrayBufferCtor, 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); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_isView(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index 855407e6f7..ce1d0503df 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -47,25 +47,7 @@ using namespace QV4; -QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON - -const QV4::VTable QV4::ArrayData::static_vtbl = { - nullptr, - 0, - 0, - QV4::ArrayData::IsExecutionContext, - QV4::ArrayData::IsString, - QV4::ArrayData::IsObject, - QV4::ArrayData::IsFunctionObject, - QV4::ArrayData::IsErrorObject, - QV4::ArrayData::IsArrayData, - 0, - QV4::ArrayData::MyType, - "ArrayData", - Q_VTABLE_FUNCTION(QV4::ArrayData, destroy), - ArrayData::Data::markObjects, - isEqualTo -}; +DEFINE_MANAGED_VTABLE(ArrayData); const ArrayVTable SimpleArrayData::static_vtbl = { @@ -99,18 +81,9 @@ const ArrayVTable SparseArrayData::static_vtbl = SparseArrayData::length }; -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 +168,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 +177,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 +341,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 +363,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 +445,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; } @@ -593,7 +564,7 @@ uint ArrayData::append(Object *obj, ArrayObject *otherObj, uint n) if (!other || ArgumentsObject::isNonStrictArgumentsObject(otherObj)) { ScopedValue v(scope); for (uint i = 0; i < n; ++i) - obj->arraySet(oldSize + i, (v = otherObj->getIndexed(i))); + obj->arraySet(oldSize + i, (v = otherObj->get(i))); } else if (other && other->isSparse()) { Heap::SparseArrayData *os = static_cast<Heap::SparseArrayData *>(other->d()); if (other->hasAttributes()) { 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..650f58463e --- /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->get(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..05f6b7dfec 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; @@ -55,7 +59,7 @@ void Heap::ArrayCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Array")); } -ReturnedValue ArrayCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue ArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { ExecutionEngine *v4 = static_cast<const ArrayCtor *>(f)->engine(); Scope scope(v4); @@ -80,22 +84,28 @@ ReturnedValue ArrayCtor::callAsConstructor(const FunctionObject *f, const Value return a.asReturnedValue(); } -ReturnedValue ArrayCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) +ReturnedValue ArrayCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) { - return callAsConstructor(f, argv, argc); + return virtualCallAsConstructor(f, argv, argc, f); } 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->getOwnProperty(PropertyKey::fromArrayIndex(k)) == Attr_Invalid) { + 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->get(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->getOwnProperty(PropertyKey::fromArrayIndex(k)) != Attr_Invalid) + 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->getOwnProperty(PropertyKey::fromArrayIndex(k)) != Attr_Invalid) { + 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); @@ -171,9 +383,9 @@ ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value } else if (eltAsObj && eltAsObj->isListType()) { const uint startIndex = result->getLength(); for (int i = 0, len = eltAsObj->getLength(); i < len; ++i) { - entry = eltAsObj->getIndexed(i); + entry = eltAsObj->get(i); // spec says not to throw if this fails - result->putIndexed(startIndex + i, entry); + result->put(startIndex + i, entry); } } else { result->arraySet(result->getLength(), argv[i]); @@ -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->get(from, &fromPresent)); + + if (fromPresent) { + instance->setIndexed(to, fromVal, QV4::Object::DoThrowOnRejection); + CHECK_EXCEPTION(); + } else { + bool didDelete = instance->deleteProperty(PropertyKey::fromArrayIndex(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); @@ -202,7 +496,7 @@ ReturnedValue ArrayPrototype::method_find(const FunctionObject *b, const Value * ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); for (uint k = 0; k < len; ++k) { - arguments[0] = instance->getIndexed(k); + arguments[0] = instance->get(k); CHECK_EXCEPTION(); arguments[1] = Primitive::fromDouble(k); @@ -236,7 +530,7 @@ ReturnedValue ArrayPrototype::method_findIndex(const FunctionObject *b, const Va ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); for (uint k = 0; k < len; ++k) { - arguments[0] = instance->getIndexed(k); + arguments[0] = instance->get(k); CHECK_EXCEPTION(); arguments[1] = Primitive::fromDouble(k); @@ -282,7 +576,7 @@ ReturnedValue ArrayPrototype::method_join(const FunctionObject *b, const Value * if (i) R += r4; - e = a->getIndexed(i); + e = a->get(i); CHECK_EXCEPTION(); if (!e->isNullOrUndefined()) R += e->toQString(); @@ -327,10 +621,10 @@ ReturnedValue ArrayPrototype::method_pop(const FunctionObject *b, const Value *t RETURN_UNDEFINED(); } - ScopedValue result(scope, instance->getIndexed(len - 1)); + ScopedValue result(scope, instance->get(len - 1)); CHECK_EXCEPTION(); - if (!instance->deleteIndexedProperty(len - 1)) + if (!instance->deleteProperty(PropertyKey::fromArrayIndex(len - 1))) return scope.engine->throwTypeError(); if (instance->isArrayObject()) @@ -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); @@ -381,7 +675,7 @@ ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value * len = instance->arrayData()->length(); } else { for (int i = 0, ei = argc; i < ei; ++i) { - if (!instance->putIndexed(len + i, argv[i])) + if (!instance->put(len + i, argv[i])) return scope.engine->throwTypeError(); } len += argc; @@ -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; @@ -411,19 +708,19 @@ ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Valu ScopedValue hval(scope); for (; lo < hi; ++lo, --hi) { bool loExists, hiExists; - lval = instance->getIndexed(lo, &loExists); - hval = instance->getIndexed(hi, &hiExists); + lval = instance->get(lo, &loExists); + hval = instance->get(hi, &hiExists); CHECK_EXCEPTION(); bool ok; if (hiExists) - ok = instance->putIndexed(lo, hval); + ok = instance->put(lo, hval); else - ok = instance->deleteIndexedProperty(lo); + ok = instance->deleteProperty(PropertyKey::fromArrayIndex(lo)); if (ok) { if (loExists) - ok = instance->putIndexed(hi, lval); + ok = instance->put(hi, lval); else - ok = instance->deleteIndexedProperty(hi); + ok = instance->deleteProperty(PropertyKey::fromArrayIndex(hi)); } if (!ok) return scope.engine->throwTypeError(); @@ -454,23 +751,23 @@ ReturnedValue ArrayPrototype::method_shift(const FunctionObject *b, const Value if (!instance->protoHasArray() && !instance->arrayData()->attrs && instance->arrayData()->length() <= len && instance->arrayData()->type != Heap::ArrayData::Custom) { result = instance->arrayData()->vtable()->pop_front(instance); } else { - result = instance->getIndexed(0); + result = instance->get(uint(0)); CHECK_EXCEPTION(); ScopedValue v(scope); // do it the slow way for (uint k = 1; k < len; ++k) { bool exists; - v = instance->getIndexed(k, &exists); + v = instance->get(k, &exists); CHECK_EXCEPTION(); bool ok; if (exists) - ok = instance->putIndexed(k - 1, v); + ok = instance->put(k - 1, v); else - ok = instance->deleteIndexedProperty(k - 1); + ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k - 1)); if (!ok) return scope.engine->throwTypeError(); } - bool ok = instance->deleteIndexedProperty(len - 1); + bool ok = instance->deleteProperty(PropertyKey::fromArrayIndex(len - 1)); if (!ok) return scope.engine->throwTypeError(); } @@ -518,7 +815,7 @@ ReturnedValue ArrayPrototype::method_slice(const FunctionObject *b, const Value uint n = 0; for (uint i = start; i < end; ++i) { bool exists; - v = o->getIndexed(i, &exists); + v = o->get(i, &exists); CHECK_EXCEPTION(); if (exists) result->arraySet(n, v); @@ -548,60 +845,71 @@ 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) { bool exists; - v = instance->getIndexed(start + i, &exists); + v = instance->get(start + i, &exists); CHECK_EXCEPTION(); if (exists) newArray->arrayPut(i, v); } newArray->setArrayLengthUnchecked(deleteCount); - uint itemCount = argc < 2 ? 0 : argc - 2; if (itemCount < deleteCount) { for (uint k = start; k < len - deleteCount; ++k) { bool exists; - v = instance->getIndexed(k + deleteCount, &exists); + v = instance->get(k + deleteCount, &exists); CHECK_EXCEPTION(); bool ok; if (exists) - ok = instance->putIndexed(k + itemCount, v); + ok = instance->put(k + itemCount, v); else - ok = instance->deleteIndexedProperty(k + itemCount); + ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + itemCount)); if (!ok) return scope.engine->throwTypeError(); } for (uint k = len; k > len - deleteCount + itemCount; --k) { - if (!instance->deleteIndexedProperty(k - 1)) + if (!instance->deleteProperty(PropertyKey::fromArrayIndex(k - 1))) return scope.engine->throwTypeError(); } } else if (itemCount > deleteCount) { uint k = len - deleteCount; while (k > start) { bool exists; - v = instance->getIndexed(k + deleteCount - 1, &exists); + v = instance->get(k + deleteCount - 1, &exists); CHECK_EXCEPTION(); bool ok; if (exists) - ok = instance->putIndexed(k + itemCount - 1, v); + ok = instance->put(k + itemCount - 1, v); else - ok = instance->deleteIndexedProperty(k + itemCount - 1); + ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + itemCount - 1)); if (!ok) return scope.engine->throwTypeError(); --k; @@ -609,7 +917,7 @@ ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value } for (uint i = 0; i < itemCount; ++i) - instance->putIndexed(start + i, argv[i + 2]); + instance->put(start + i, argv[i + 2]); if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - deleteCount + itemCount)))) return scope.engine->throwTypeError(); @@ -636,17 +944,17 @@ ReturnedValue ArrayPrototype::method_unshift(const FunctionObject *b, const Valu ScopedValue v(scope); for (uint k = len; k > 0; --k) { bool exists; - v = instance->getIndexed(k - 1, &exists); + v = instance->get(k - 1, &exists); bool ok; if (exists) - ok = instance->putIndexed(k + argc - 1, v); + ok = instance->put(k + argc - 1, v); else - ok = instance->deleteIndexedProperty(k + argc - 1); + ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + argc - 1)); if (!ok) return scope.engine->throwTypeError(); } for (int i = 0, ei = argc; i < ei; ++i) { - bool ok = instance->putIndexed(i, argv[i]); + bool ok = instance->put(i, argv[i]); if (!ok) return scope.engine->throwTypeError(); } @@ -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->get(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); @@ -691,7 +1037,7 @@ ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Valu ScopedValue v(scope); for (uint k = fromIndex; k < len; ++k) { bool exists; - v = instance->getIndexed(k, &exists); + v = instance->get(k, &exists); if (exists && RuntimeHelpers::strictEqual(v, searchValue)) return Encode(k); } @@ -705,7 +1051,7 @@ ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Valu // lets be safe and slow for (uint i = fromIndex; i < len; ++i) { bool exists; - value = instance->getIndexed(i, &exists); + value = instance->get(i, &exists); CHECK_EXCEPTION(); if (exists && RuntimeHelpers::strictEqual(value, searchValue)) return Encode(i); @@ -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); @@ -765,7 +1123,7 @@ ReturnedValue ArrayPrototype::method_lastIndexOf(const FunctionObject *b, const for (uint k = fromIndex; k > 0;) { --k; bool exists; - v = instance->getIndexed(k, &exists); + v = instance->get(k, &exists); CHECK_EXCEPTION(); if (exists && RuntimeHelpers::strictEqual(v, searchValue)) return Encode(k); @@ -793,7 +1151,7 @@ ReturnedValue ArrayPrototype::method_every(const FunctionObject *b, const Value bool ok = true; for (uint k = 0; ok && k < len; ++k) { bool exists; - arguments[0] = instance->getIndexed(k, &exists); + arguments[0] = instance->get(k, &exists); if (!exists) continue; @@ -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); @@ -824,7 +1218,7 @@ ReturnedValue ArrayPrototype::method_some(const FunctionObject *b, const Value * for (uint k = 0; k < len; ++k) { bool exists; - arguments[0] = instance->getIndexed(k, &exists); + arguments[0] = instance->get(k, &exists); if (!exists) continue; @@ -855,7 +1249,7 @@ ReturnedValue ArrayPrototype::method_forEach(const FunctionObject *b, const Valu for (uint k = 0; k < len; ++k) { bool exists; - arguments[0] = instance->getIndexed(k, &exists); + arguments[0] = instance->get(k, &exists); if (!exists) continue; @@ -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); @@ -890,7 +1287,7 @@ ReturnedValue ArrayPrototype::method_map(const FunctionObject *b, const Value *t for (uint k = 0; k < len; ++k) { bool exists; - arguments[0] = instance->getIndexed(k, &exists); + arguments[0] = instance->get(k, &exists); if (!exists) continue; @@ -925,7 +1322,7 @@ ReturnedValue ArrayPrototype::method_filter(const FunctionObject *b, const Value uint to = 0; for (uint k = 0; k < len; ++k) { bool exists; - arguments[0] = instance->getIndexed(k, &exists); + arguments[0] = instance->get(k, &exists); if (!exists) continue; @@ -962,7 +1359,7 @@ ReturnedValue ArrayPrototype::method_reduce(const FunctionObject *b, const Value } else { bool kPresent = false; while (k < len && !kPresent) { - v = instance->getIndexed(k, &kPresent); + v = instance->get(k, &kPresent); if (kPresent) acc = v; ++k; @@ -975,7 +1372,7 @@ ReturnedValue ArrayPrototype::method_reduce(const FunctionObject *b, const Value while (k < len) { bool kPresent; - v = instance->getIndexed(k, &kPresent); + v = instance->get(k, &kPresent); if (kPresent) { arguments[0] = acc; arguments[1] = v; @@ -1015,7 +1412,7 @@ ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const } else { bool kPresent = false; while (k > 0 && !kPresent) { - v = instance->getIndexed(k - 1, &kPresent); + v = instance->get(k - 1, &kPresent); if (kPresent) acc = v; --k; @@ -1028,7 +1425,7 @@ ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const while (k > 0) { bool kPresent; - v = instance->getIndexed(k - 1, &kPresent); + v = instance->get(k - 1, &kPresent); if (kPresent) { arguments[0] = acc; arguments[1] = v; @@ -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..04ec7e1607 100644 --- a/src/qml/jsruntime/qv4arrayobject_p.h +++ b/src/qml/jsruntime/qv4arrayobject_p.h @@ -70,8 +70,8 @@ struct ArrayCtor: FunctionObject { V4_OBJECT2(ArrayCtor, 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); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct ArrayPrototype: ArrayObject @@ -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/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index eb83f902db..f00abad871 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -50,13 +50,13 @@ void Heap::BooleanCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Boolean")); } -ReturnedValue BooleanCtor::callAsConstructor(const FunctionObject *that, const Value *argv, int argc) +ReturnedValue BooleanCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *) { bool n = argc ? argv[0].toBoolean() : false; return Encode(that->engine()->newBooleanObject(n)); } -ReturnedValue BooleanCtor::call(const FunctionObject *, const Value *, const Value *argv, int argc) +ReturnedValue BooleanCtor::virtualCall(const FunctionObject *, const Value *, const Value *argv, int argc) { bool value = argc ? argv[0].toBoolean() : 0; return Encode(value); @@ -66,7 +66,7 @@ void BooleanPrototype::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)); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(engine->id_toString(), method_toString); diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h index 3cf09b2667..276ec8393b 100644 --- a/src/qml/jsruntime/qv4booleanobject_p.h +++ b/src/qml/jsruntime/qv4booleanobject_p.h @@ -70,8 +70,8 @@ struct BooleanCtor: FunctionObject { V4_OBJECT2(BooleanCtor, 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); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct BooleanPrototype: BooleanObject diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 020e519e74..bb08d2786d 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -48,12 +48,48 @@ #include "qv4errorobject_p.h" #include "qv4string_p.h" #include "qv4qmlcontext_p.h" +#include "qv4stackframe_p.h" 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 +111,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 +132,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,44 +171,32 @@ 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; } ctx = ctx->d()->outer; } - if (activation->hasOwnProperty(name)) + PropertyKey id = name->toPropertyKey(); + if (activation->getOwnProperty(id) != Attr_Invalid) return; 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(id, desc, attrs)) + scope.engine->throwTypeError(); } bool ExecutionContext::deleteProperty(String *name) { - name->makeIdentifier(); - Identifier *id = name->identifier(); + PropertyKey id = name->toPropertyKey(); 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); @@ -183,8 +210,8 @@ bool ExecutionContext::deleteProperty(String *name) if (ctx->activation) { Scope scope(this); ScopedObject object(scope, ctx->activation); - if (object && object->hasProperty(name)) - return object->deleteProperty(name); + if (object && object->hasProperty(name->toPropertyKey())) + return object->deleteProperty(name->toPropertyKey()); } break; } @@ -199,32 +226,24 @@ bool ExecutionContext::deleteProperty(String *name) ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value &value) { - name->makeIdentifier(); - Identifier *id = name->identifier(); + PropertyKey id = name->toPropertyKey(); 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); - if (w->hasProperty(name)) { + if (w->hasProperty(name->toPropertyKey())) { if (!w->put(name, value)) return TypeError; return NoError; } 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); @@ -262,20 +281,14 @@ ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value ReturnedValue ExecutionContext::getProperty(String *name) { - name->makeIdentifier(); + PropertyKey id = name->toPropertyKey(); 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(); uint index = c->internalClass->find(id); if (index < UINT_MAX) @@ -289,7 +302,7 @@ ReturnedValue ExecutionContext::getProperty(String *name) Scope scope(this); ScopedObject activation(scope, ctx->activation); bool hasProperty = false; - ReturnedValue v = activation->get(name, &hasProperty); + ReturnedValue v = activation->get(id, nullptr, &hasProperty); if (hasProperty) return v; } @@ -303,21 +316,14 @@ ReturnedValue ExecutionContext::getProperty(String *name) ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) { base->setM(nullptr); - name->makeIdentifier(); + PropertyKey id = name->toPropertyKey(); 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(); uint index = c->internalClass->find(id); if (index < UINT_MAX) @@ -340,7 +346,7 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) Scope scope(this); ScopedObject o(scope, ctx->activation); bool hasProperty = false; - ReturnedValue v = o->get(name, &hasProperty); + ReturnedValue v = o->get(id, nullptr, &hasProperty); if (hasProperty) { base->setM(o->d()); return v; diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index 512bfa06d8..fbb4168e9b 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -55,66 +55,11 @@ 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 { - Function = 0, - Context = 1, - Accumulator = 2, - This = 3, - Argc = 4 - }; - - Value function; - Value context; - Value accumulator; - Value thisObject; - Value _argc; - - int argc() const { - Q_ASSERT(_argc.isInteger()); - return _argc.int_32(); - } - - void setArgc(int argc) { - Q_ASSERT(argc >= 0); - _argc.setInt_32(argc); - } - - inline ReturnedValue argument(int i) const { - return i < argc() ? args[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue(); - } - - Value args[1]; - - static Q_DECL_CONSTEXPR int HeaderSize() { return offsetof(CallData, args) / sizeof(QV4::Value); } -}; - -Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value); -Q_STATIC_ASSERT(offsetof(CallData, thisObject) == CallData::This*sizeof(Value)); -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 +69,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 +82,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 +130,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 +143,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); @@ -224,6 +165,12 @@ struct Q_QML_EXPORT ExecutionContext : public Managed inline CallContext *asCallContext(); inline const CallContext *asCallContext() const; + +protected: + // vtable method required for compilation + static bool virtualDeleteProperty(Managed *, PropertyKey) { + Q_UNREACHABLE(); + } }; struct Q_QML_EXPORT CallContext : public ExecutionContext @@ -239,11 +186,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..d550e559d3 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" @@ -54,7 +55,7 @@ void Heap::DataViewCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("DataView")); } -ReturnedValue DataViewCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue DataViewCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Scope scope(f->engine()); Scoped<ArrayBuffer> buffer(scope, argc ? argv[0] : Primitive::undefinedValue()); @@ -69,54 +70,57 @@ 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; return a.asReturnedValue(); } -ReturnedValue DataViewCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) +ReturnedValue DataViewCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) { - return callAsConstructor(f, argv, argc); + return virtualCallAsConstructor(f, argv, argc, f); } 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/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h index 1e07d85118..6a9f865c0f 100644 --- a/src/qml/jsruntime/qv4dataview_p.h +++ b/src/qml/jsruntime/qv4dataview_p.h @@ -79,8 +79,8 @@ struct DataViewCtor: FunctionObject { V4_OBJECT2(DataViewCtor, 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); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct DataView : Object diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 5bbe312146..3efd626685 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,9 +531,9 @@ 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; + return TimeClip(t); } QDateTime dt = QDateTime::fromString(s, Qt::TextDate); @@ -605,7 +603,7 @@ static inline double ParseString(const QString &s) } if (!dt.isValid()) return qt_qnan(); - return dt.toMSecsSinceEpoch(); + return TimeClip(dt.toMSecsSinceEpoch()); } /*! @@ -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; @@ -705,7 +703,7 @@ DEFINE_OBJECT_VTABLE(DateObject); void Heap::DateObject::init(const QDateTime &date) { Object::init(); - this->date = date.isValid() ? date.toMSecsSinceEpoch() : qt_qnan(); + this->date = date.isValid() ? TimeClip(date.toMSecsSinceEpoch()) : qt_qnan(); } void Heap::DateObject::init(const QTime &time) @@ -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 @@ -745,15 +743,16 @@ void Heap::DateCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Date")); } -ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Value *argv, int argc) +ReturnedValue DateCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *) { + 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) +ReturnedValue DateCtor::virtualCall(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()); } @@ -1471,7 +1470,7 @@ ReturnedValue DatePrototype::method_toISOString(const FunctionObject *b, const V int year = (int)YearFromTime(t); if (year < 0 || year > 9999) { if (qAbs(year) >= 1000000) - RETURN_RESULT(v4->newString(QStringLiteral("Invalid Date"))); + RETURN_RESULT(v4->throwRangeError(*thisObject)); result += year < 0 ? QLatin1Char('-') : QLatin1Char('+'); year = qAbs(year); addZeroPrefixedInt(result, year, 6); @@ -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(); + PropertyKey id = hint->toPropertyKey(); + if (id == e->id_default()->propertyKey()) + hint = e->id_string(); + else if (id != e->id_string()->propertyKey() && id != e->id_number()->propertyKey()) + 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..5b9934282c 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -101,15 +101,15 @@ 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 { V4_OBJECT2(DateCtor, FunctionObject) - static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int); }; struct DatePrototype: Object @@ -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..f9ef6b45a1 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,17 @@ #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" +#include "qv4proxy_p.h" +#include "qv4stackframe_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 +92,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 +128,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 +139,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 +147,7 @@ ExecutionEngine::ExecutionEngine() , gcStack(new WTF::PageAllocation) , globalCode(nullptr) , v8Engine(nullptr) + , publicEngine(jsEngine) , argumentsAccessors(nullptr) , nArgumentsAccessors(0) , m_engineId(engineSerial.fetchAndAddOrdered(1)) @@ -181,27 +200,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 +247,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 +277,186 @@ 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")); + jsSymbols[Symbol_revokableProxy] = Symbol::create(this, QStringLiteral("@Proxy.revokableProxy")); 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()->propertyKey(), 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()->propertyKey(), Attr_NotEnumerable); + argsClass = argsClass->addMember(symbol_iterator()->propertyKey(), Attr_Data|Attr_NotEnumerable); + classes[Class_ArgumentsObject] = argsClass->addMember(id_callee()->propertyKey(), Attr_Data|Attr_NotEnumerable); 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()->propertyKey(), Attr_NotEnumerable); + argsClass = argsClass->addMember(symbol_iterator()->propertyKey(), Attr_Data|Attr_NotEnumerable); + classes[Class_StrictArgumentsObject] = argsClass->addMember(id_callee()->propertyKey(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); *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()->propertyKey(), Attr_ReadOnly); + classes[Class_StringObject] = ic->changePrototype(stringPrototype()->d()); + Q_ASSERT(classes[Class_StringObject]->find(id_length()->propertyKey()) == 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()->propertyKey(), 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()->propertyKey(), 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()->propertyKey(), 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()->propertyKey(), 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(ConstructorFunction::staticVTable()); + classes[Class_ConstructorFunction] = ic->d(); + ic = ic->changeVTable(MemberFunction::staticVTable()); + classes[Class_MemberFunction] = ic->d(); + ic = ic->changeVTable(GeneratorFunction::staticVTable()); + classes[Class_MemberFunction] = ic->d(); + ic = ic->changeVTable(GeneratorFunction::staticVTable()); + classes[Class_GeneratorFunction] = ic->d(); + ic = ic->changeVTable(MemberGeneratorFunction::staticVTable()); + classes[Class_MemberGeneratorFunction] = ic->d(); + classes[Class_ObjectProto] = classes[Class_Object]->addMember(id_constructor()->propertyKey(), 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()->propertyKey(), 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")))->propertyKey(), 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")))->propertyKey(), 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")))->propertyKey(), 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")))->propertyKey(), 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()->propertyKey(), 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()->propertyKey(), 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")))->propertyKey(), 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")))->propertyKey(), 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")))->propertyKey(), 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")))->propertyKey(), 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()->propertyKey(), 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")))->propertyKey(), 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()->propertyKey(), 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)); + classes[Class_ProxyObject] = classes[Class_Empty]->changeVTable(ProxyObject::staticVTable()); + + 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>(); - Q_ASSERT(variantPrototype()->prototype() == objectPrototype()->d()); + jsObjects[VariantProto] = memoryManager->allocate<VariantPrototype>(); + Q_ASSERT(variantPrototype()->getPrototypeOf() == objectPrototype()->d()); +#if QT_CONFIG(qml_sequence_object) ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this)); - jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic, 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 +466,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 +518,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 +536,23 @@ 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->defineDefaultProperty(QStringLiteral("Proxy"), (o = memoryManager->allocate<Proxy>(rootContext()))); 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 +564,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 +581,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 +603,6 @@ ExecutionEngine::~ExecutionEngine() while (!compilationUnits.isEmpty()) (*compilationUnits.begin())->unlink(); - internalClasses[Class_Empty]->destroy(); - delete classPool; delete bumperPointerAllocator; delete regExpCache; delete regExpAllocator; @@ -500,6 +614,11 @@ ExecutionEngine::~ExecutionEngine() delete [] argumentsAccessors; } +ExecutionContext *ExecutionEngine::currentContext() const +{ + return static_cast<ExecutionContext *>(¤tStackFrame->jsFrame->context); +} + #if QT_CONFIG(qml_debug) void ExecutionEngine::setDebugger(Debugging::Debugger *debugger) { @@ -521,59 +640,71 @@ 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) { - Scope scope(this); - return ScopedString(scope, memoryManager->allocWithStringData<String>(s.length() * sizeof(QChar), s))->d(); + return memoryManager->allocWithStringData<String>(s.length() * sizeof(QChar), s); } Heap::String *ExecutionEngine::newIdentifier(const QString &text) { - return identifierTable->insertString(text); + Scope scope(this); + ScopedString s(scope, memoryManager->allocWithStringData<String>(text.length() * sizeof(QChar), text)); + s->toPropertyKey(); + return s->d(); } 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 +717,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 +738,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 +789,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 +841,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) @@ -761,37 +903,6 @@ QQmlContextData *ExecutionEngine::callingQmlContext() const return ctx->qml()->context->contextData(); } -QString CppStackFrame::source() const -{ - return v4Function ? v4Function->sourceFile() : QString(); -} - -QString CppStackFrame::function() const -{ - return v4Function ? v4Function->name()->toQString() : QString(); -} - -int CppStackFrame::lineNumber() const -{ - if (!v4Function) - return -1; - - auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) { - return entry.codeOffset < offset; - }; - - const QV4::CompiledData::Function *cf = v4Function->compiledFunction; - uint offset = instructionPointer; - const CompiledData::CodeOffsetToLine *lineNumbers = cf->lineNumberTable(); - uint nLineNumbers = cf->nLineNumbers; - const CompiledData::CodeOffsetToLine *line = std::lower_bound(lineNumbers, lineNumbers + nLineNumbers, offset, findLine) - 1; - return line->line; -} - -ReturnedValue CppStackFrame::thisObject() const { - return jsFrame->thisObject.asReturnedValue(); -} - StackTrace ExecutionEngine::stackTrace(int frameLimit) const { Scope scope(const_cast<ExecutionEngine *>(this)); @@ -891,16 +1002,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 +1018,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 +1166,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 +1225,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>()) { @@ -1128,7 +1239,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int uint length = a->getLength(); QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope); for (uint ii = 0; ii < length; ++ii) { - qobjectWrapper = a->getIndexed(ii); + qobjectWrapper = a->get(ii); if (!!qobjectWrapper) { list << qobjectWrapper->object(); } else { @@ -1141,10 +1252,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 +1277,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>()) @@ -1211,7 +1326,7 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V int length = a->getLength(); for (int ii = 0; ii < length; ++ii) { - v = a->getIndexed(ii); + v = a->get(ii); list << ::toVariant(e, v, -1, /*createJSValueForObjects*/false, visitedObjects); } @@ -1260,9 +1375,6 @@ static QV4::ReturnedValue objectFromVariantMap(QV4::ExecutionEngine *e, const QV QV4::ScopedValue v(scope); for (QVariantMap::const_iterator iter = map.begin(), cend = map.end(); iter != cend; ++iter) { s = e->newString(iter.key()); - uint idx = s->asArrayIndex(); - if (idx > 16 && (!o->arrayData() || idx > o->arrayData()->length() * 2)) - o->initSparseArray(); o->put(s, (v = e->fromVariant(iter.value()))); } return o.asReturnedValue(); @@ -1321,6 +1433,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 +1443,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 +1454,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 +1497,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); @@ -1428,11 +1546,13 @@ static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVarian QV4::Scope scope(v4); QV4::ScopedObject o(scope, v4->newObject()); QV4::ScopedString s(scope); + QV4::ScopedPropertyKey key(scope); QV4::ScopedValue v(scope); for (QVariantMap::const_iterator it = vmap.constBegin(), cend = vmap.constEnd(); it != cend; ++it) { s = v4->newIdentifier(it.key()); + key = s->propertyKey(); v = variantToJS(v4, it.value()); - uint idx = s->asArrayIndex(); + uint idx = key->asArrayIndex(); if (idx < UINT_MAX) o->arraySet(idx, v); else @@ -1707,7 +1827,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) } else if (Object *o = value->objectValue()) { // Look in the prototype chain. QV4::Scope scope(this); - QV4::ScopedObject proto(scope, o->prototype()); + QV4::ScopedObject proto(scope, o->getPrototypeOf()); while (proto) { bool canCast = false; if (QV4::VariantObject *vo = proto->as<QV4::VariantObject>()) { @@ -1728,7 +1848,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) *reinterpret_cast<void* *>(data) = var.data(); return true; } - proto = proto->prototype(); + proto = proto->getPrototypeOf(); } } } else if (value->isNull() && name.endsWith('*')) { diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index c7fb743088..fc0c93eaad 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -56,7 +56,6 @@ #include <private/qintrusivelist_p.h> #include "qv4enginebase_p.h" - #ifndef V4_BOOTSTRAP # include "qv4function_p.h" # include <private/qv8engine_p.h> @@ -88,33 +87,6 @@ struct CompilationUnit; } struct Function; -struct InternalClass; -struct InternalClassPool; - -struct Q_QML_EXPORT CppStackFrame { - CppStackFrame *parent; - Function *v4Function; - CallData *jsFrame; - const Value *originalArguments; - int originalArgumentsCount; - int instructionPointer; - - QString source() const; - QString function() const; - inline QV4::ExecutionContext *context() const { - return static_cast<ExecutionContext *>(&jsFrame->context); - } - int lineNumber() const; - - inline QV4::Heap::CallContext *callContext() const { - Heap::ExecutionContext *ctx = static_cast<ExecutionContext &>(jsFrame->context).d();\ - while (ctx->type != Heap::ExecutionContext::Type_CallContext) - ctx = ctx->outer; - return static_cast<Heap::CallContext *>(ctx); - } - ReturnedValue thisObject() const; -}; - struct Q_QML_EXPORT ExecutionEngine : public EngineBase @@ -142,8 +114,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 +123,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 +153,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 +189,11 @@ public: URIError_Ctor, ArrayBuffer_Ctor, DataView_Ctor, + Set_Ctor, + Map_Ctor, + IntrinsicTypedArray_Ctor, + + GetSymbolSpecies, Eval_Function, GetStack_Function, @@ -211,12 +204,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 +225,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 +252,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 +288,8 @@ public: String_boolean, String_number, String_string, + String_default, + String_symbol, String_object, String_function, String_length, @@ -301,10 +318,31 @@ 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, + Symbol_revokableProxy, + 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 +351,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 +381,22 @@ 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); } + Symbol *symbol_revokableProxy() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_revokableProxy); } #ifndef V4_BOOTSTRAP QIntrusiveList<CompiledData::CompilationUnit, &CompiledData::CompilationUnit::nextCompilationUnit> compilationUnits; @@ -372,9 +428,9 @@ public: const bool m_canAllocateExecutableMemory; #endif - int internalClassIdCount = 0; + quintptr protoIdCount = 1; - ExecutionEngine(); + ExecutionEngine(QJSEngine *jsEngine = nullptr); ~ExecutionEngine(); #if !QT_CONFIG(qml_debug) @@ -391,29 +447,28 @@ public: void setProfiler(Profiling::Profiler *profiler); #endif // QT_CONFIG(qml_debug) - void setCurrentContext(Heap::ExecutionContext *context); - ExecutionContext *currentContext() const { - return static_cast<ExecutionContext *>(¤tStackFrame->jsFrame->context); - } + ExecutionContext *currentContext() const; - 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 +492,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 +511,7 @@ public: void initRootContext(); - InternalClass *newClass(const InternalClass &other); + Heap::InternalClass *newClass(Heap::InternalClass *other); StackTrace exceptionStackTrace; @@ -492,7 +550,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 +560,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; @@ -521,11 +580,6 @@ struct NoThrowEngine; #endif -inline void ExecutionEngine::setCurrentContext(Heap::ExecutionContext *context) -{ - currentStackFrame->jsFrame->context = context; -} - #define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \ ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4); diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index 59fb4a564a..3e89e57abb 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,11 +96,18 @@ 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_ConstructorFunction, + Class_MemberFunction, + Class_MemberGeneratorFunction, Class_ObjectProto, Class_RegExp, Class_RegExpObject, @@ -111,10 +118,12 @@ struct Q_QML_EXPORT EngineBase { Class_ErrorObjectWithMessage, Class_ErrorProto, Class_QmlContextWrapper, - Class_QmlContext, + Class_ProxyObject, + 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..e8b4e04e83 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()); @@ -233,13 +229,13 @@ void Heap::ErrorCtor::init(QV4::ExecutionContext *scope, const QString &name) Heap::FunctionObject::init(scope, name); } -ReturnedValue ErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue ErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Value v = argc ? *argv : Primitive::undefinedValue(); return ErrorObject::create<ErrorObject>(f->engine(), v)->asReturnedValue(); } -ReturnedValue ErrorCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) +ReturnedValue ErrorCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) { return f->callAsConstructor(argv, argc); } @@ -249,7 +245,7 @@ void Heap::EvalErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("EvalError")); } -ReturnedValue EvalErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue EvalErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Value v = argc ? *argv : Primitive::undefinedValue(); return ErrorObject::create<EvalErrorObject>(f->engine(), v)->asReturnedValue(); @@ -260,7 +256,7 @@ void Heap::RangeErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("RangeError")); } -ReturnedValue RangeErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue RangeErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Value v = argc ? *argv : Primitive::undefinedValue(); return ErrorObject::create<RangeErrorObject>(f->engine(), v)->asReturnedValue(); @@ -271,7 +267,7 @@ void Heap::ReferenceErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("ReferenceError")); } -ReturnedValue ReferenceErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue ReferenceErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Value v = argc ? *argv : Primitive::undefinedValue(); return ErrorObject::create<ReferenceErrorObject>(f->engine(), v)->asReturnedValue(); @@ -282,7 +278,7 @@ void Heap::SyntaxErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("SyntaxError")); } -ReturnedValue SyntaxErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue SyntaxErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Value v = argc ? *argv : Primitive::undefinedValue(); return ErrorObject::create<SyntaxErrorObject>(f->engine(), v)->asReturnedValue(); @@ -293,7 +289,7 @@ void Heap::TypeErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("TypeError")); } -ReturnedValue TypeErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue TypeErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Value v = argc ? *argv : Primitive::undefinedValue(); return ErrorObject::create<TypeErrorObject>(f->engine(), v)->asReturnedValue(); @@ -304,7 +300,7 @@ void Heap::URIErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("URIError")); } -ReturnedValue URIErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue URIErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Value v = argc ? *argv : Primitive::undefinedValue(); return ErrorObject::create<URIErrorObject>(f->engine(), v)->asReturnedValue(); @@ -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..90ef4dd842 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 { @@ -229,50 +229,50 @@ struct ErrorCtor: FunctionObject { V4_OBJECT2(ErrorCtor, 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); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct EvalErrorCtor: ErrorCtor { V4_OBJECT2(EvalErrorCtor, ErrorCtor) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); }; struct RangeErrorCtor: ErrorCtor { V4_OBJECT2(RangeErrorCtor, ErrorCtor) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); }; struct ReferenceErrorCtor: ErrorCtor { V4_OBJECT2(ReferenceErrorCtor, ErrorCtor) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); }; struct SyntaxErrorCtor: ErrorCtor { V4_OBJECT2(SyntaxErrorCtor, ErrorCtor) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); }; struct TypeErrorCtor: ErrorCtor { V4_OBJECT2(TypeErrorCtor, ErrorCtor) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); }; struct URIErrorCtor: ErrorCtor { V4_OBJECT2(URIErrorCtor, ErrorCtor) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); }; @@ -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/qv4estable.cpp b/src/qml/jsruntime/qv4estable.cpp new file mode 100644 index 0000000000..55b7407000 --- /dev/null +++ b/src/qml/jsruntime/qv4estable.cpp @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** 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 "qv4estable_p.h" + +using namespace QV4; + +// The ES spec requires that Map/Set be implemented using a data structure that +// is a little different from most; it requires nonlinear access, and must also +// preserve the order of insertion of items in a deterministic way. +// +// This class implements those requirements, except for fast access: that +// will be addressed in a followup patch. + +ESTable::ESTable() + : m_capacity(8) +{ + m_keys = (Value*)malloc(m_capacity * sizeof(Value)); + m_values = (Value*)malloc(m_capacity * sizeof(Value)); + memset(m_keys, 0, m_capacity); + memset(m_values, 0, m_capacity); +} + +ESTable::~ESTable() +{ + free(m_keys); + free(m_values); + m_size = 0; + m_capacity = 0; + m_keys = nullptr; + m_values = nullptr; +} + +void ESTable::markObjects(MarkStack *s) +{ + for (uint i = 0; i < m_size; ++i) { + m_keys[i].mark(s); + m_values[i].mark(s); + } +} + +// Pretends that there's nothing in the table. Doesn't actually free memory, as +// it will almost certainly be reused again anyway. +void ESTable::clear() +{ + m_size = 0; +} + +// Update the table to contain \a value for a given \a key. The key is +// normalized, as required by the ES spec. +void ESTable::set(const Value &key, const Value &value) +{ + for (uint i = 0; i < m_size; ++i) { + if (m_keys[i].sameValueZero(key)) { + m_values[i] = value; + return; + } + } + + if (m_capacity == m_size) { + uint oldCap = m_capacity; + m_capacity *= 2; + m_keys = (Value*)realloc(m_keys, m_capacity * sizeof(Value)); + m_values = (Value*)realloc(m_values, m_capacity * sizeof(Value)); + memset(m_keys + oldCap, 0, m_capacity - oldCap); + memset(m_values + oldCap, 0, m_capacity - oldCap); + } + + Value nk = key; + if (nk.isDouble()) { + if (nk.doubleValue() == 0 && std::signbit(nk.doubleValue())) + nk = Primitive::fromDouble(+0); + } + + m_keys[m_size] = nk; + m_values[m_size] = value; + + m_size++; +} + +// Returns true if the table contains \a key, false otherwise. +bool ESTable::has(const Value &key) const +{ + for (uint i = 0; i < m_size; ++i) { + if (m_keys[i].sameValueZero(key)) + return true; + } + + return false; +} + +// Fetches the value for the given \a key, and if \a hasValue is passed in, +// it is set depending on whether or not the given key was found. +ReturnedValue ESTable::get(const Value &key, bool *hasValue) const +{ + for (uint i = 0; i < m_size; ++i) { + if (m_keys[i].sameValueZero(key)) { + if (hasValue) + *hasValue = true; + return m_values[i].asReturnedValue(); + } + } + + if (hasValue) + *hasValue = false; + return Encode::undefined(); +} + +// Removes the given \a key from the table +bool ESTable::remove(const Value &key) +{ + bool found = false; + uint idx = 0; + for (; idx < m_size; ++idx) { + if (m_keys[idx].sameValueZero(key)) { + found = true; + break; + } + } + + if (found == true) { + memmove(m_keys + idx, m_keys + idx + 1, m_size - idx); + memmove(m_values + idx, m_values + idx + 1, m_size - idx); + m_size--; + } + return found; +} + +// Returns the size of the table. Note that the size may not match the underlying allocation. +uint ESTable::size() const +{ + return m_size; +} + +// Retrieves a key and value for a given \a idx, and places them in \a key and +// \a value. They must be valid pointers. +void ESTable::iterate(uint idx, Value *key, Value *value) +{ + Q_ASSERT(idx < m_size); + Q_ASSERT(key); + Q_ASSERT(value); + *key = m_keys[idx]; + *value = m_values[idx]; +} + diff --git a/src/qml/jsruntime/qv4estable_p.h b/src/qml/jsruntime/qv4estable_p.h new file mode 100644 index 0000000000..c665467760 --- /dev/null +++ b/src/qml/jsruntime/qv4estable_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +// +// 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. +// + +#ifndef QV4ESTABLE_P_H +#define QV4ESTABLE_P_H + +#include "qv4value_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 +{ + +class ESTable +{ +public: + ESTable(); + ~ESTable(); + + void markObjects(MarkStack *s); + void clear(); + void set(const Value &k, const Value &v); + bool has(const Value &k) const; + ReturnedValue get(const Value &k, bool *hasValue = nullptr) const; + bool remove(const Value &k); + uint size() const; + void iterate(uint idx, Value *k, Value *v); + +private: + Value *m_keys = nullptr; + Value *m_values = nullptr; + uint m_size = 0; + uint m_capacity = 0; +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 6fca9ecd45..5e3860a660 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -47,32 +47,51 @@ #include <private/qv4mm_p.h> #include <private/qv4identifiertable_p.h> #include <assembler/MacroAssemblerCodeRef.h> +#include <private/qv4vme_moth_p.h> +#include <private/qqmlglobal_p.h> QT_BEGIN_NAMESPACE using namespace QV4; -Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, Code codePtr) - : compiledFunction(function) - , compilationUnit(unit) - , code(codePtr) - , codeData(function->code()) +ReturnedValue Function::call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) { + ExecutionEngine *engine = context->engine(); + CppStackFrame frame; + frame.init(engine, this, argv, argc); + frame.setupJSFrame(engine->jsStackTop, Primitive::undefinedValue(), context->d(), + thisObject ? *thisObject : Primitive::undefinedValue(), + Primitive::undefinedValue()); + + frame.push(); + engine->jsStackTop += frame.requiredJSStackFrameSize(); + + ReturnedValue result = Moth::VME::exec(&frame, engine); + + frame.pop(); + + return result; +} + +Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function) + : compiledFunction(function) + , compilationUnit(unit) + , 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->asPropertyKey(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->asPropertyKey(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable); + internalClass = ic->d(); nFormals = compiledFunction->nFormals; } @@ -110,20 +129,25 @@ 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(); for (quint32 i = 0; i < compiledFunction->nLocals; ++i) - internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); + internalClass = internalClass->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable); 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->propertyKey(), Attr_NotConfigurable); } nFormals = parameters.size(); } +QQmlSourceLocation Function::sourceLocation() const +{ + return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column); +} + QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 59a94e5dde..d542ce752f 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -51,10 +51,8 @@ // #include "qv4global_p.h" -#include <private/qqmlglobal_p.h> #include <private/qv4compileddata_p.h> #include <private/qv4context_p.h> -#include <private/qv4vme_moth_p.h> namespace JSC { class MacroAssemblerCodeRef; @@ -62,31 +60,29 @@ class MacroAssemblerCodeRef; QT_BEGIN_NAMESPACE +struct QQmlSourceLocation; + namespace QV4 { struct Q_QML_EXPORT Function { const CompiledData::Function *compiledFunction; CompiledData::CompilationUnit *compilationUnit; - ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) { - return Moth::VME::exec(this, thisObject, argv, argc, context); - } + ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *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,13 +94,11 @@ 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 - { - return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column); - } + QQmlSourceLocation sourceLocation() const; Function *nestedFunction() const { diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 83608070ec..2aca7c2849 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" @@ -57,6 +58,7 @@ #include "private/qlocale_tools_p.h" #include "private/qqmlbuiltinfunctions_p.h" #include <private/qv4jscall_p.h> +#include <private/qv4vme_moth_p.h> #include <QtCore/QDebug> #include <algorithm> @@ -72,31 +74,38 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(const QV4::FunctionObject *, const Value *thisObject, const Value *argv, int argc)) { jsCall = code; - jsConstruct = QV4::FunctionObject::callAsConstructor; + jsConstruct = QV4::FunctionObject::virtualCallAsConstructor; Object::init(); this->scope.set(scope->engine(), scope->d()); Scope s(scope->engine()); ScopedFunctionObject f(s, this); - f->init(name, false); + if (name) + f->setName(name); } void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, bool createProto) { - jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call; - jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor; + jsCall = vtable()->call; + jsConstruct = vtable()->callAsConstructor; Object::init(); this->scope.set(scope->engine(), scope->d()); Scope s(scope->engine()); ScopedFunctionObject f(s, this); - f->init(name, createProto); + if (name) + f->setName(name); + + if (createProto) + f->createDefaultPrototypeProperty(Heap::FunctionObject::Index_Prototype, Heap::FunctionObject::Index_ProtoConstructor); } + + void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function, bool createProto) { - jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call; - jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor; + jsCall = vtable()->call; + jsConstruct = vtable()->callAsConstructor; Object::init(); setFunction(function); @@ -104,7 +113,11 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function Scope s(scope->engine()); ScopedString name(s, function->name()); ScopedFunctionObject f(s, this); - f->init(name, createProto); + if (name) + f->setName(name); + + if (createProto) + f->createDefaultPrototypeProperty(Heap::FunctionObject::Index_Prototype, Heap::FunctionObject::Index_ProtoConstructor); } void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const QString &name, bool createProto) @@ -116,12 +129,12 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const QString &nam void Heap::FunctionObject::init() { - jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call; - jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor; + jsCall = vtable()->call; + jsConstruct = vtable()->callAsConstructor; 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()->propertyKey()) == Index_Prototype); setProperty(internalClass->engine, Index_Prototype, Primitive::undefinedValue()); } @@ -139,23 +152,16 @@ void Heap::FunctionObject::destroy() Object::destroy(); } -void FunctionObject::init(String *n, bool createProto) +void FunctionObject::createDefaultPrototypeProperty(uint protoSlot, uint protoConstructorSlot) { - Scope s(internalClass()->engine); - ScopedValue protectThis(s, this); + Scope s(this); - Q_ASSERT(internalClass() && internalClass()->find(s.engine->id_prototype()) == 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); - proto->setProperty(Heap::FunctionObject::Index_ProtoConstructor, d()); - setProperty(Heap::FunctionObject::Index_Prototype, proto); - } else { - setProperty(Heap::FunctionObject::Index_Prototype, Primitive::undefinedValue()); - } + Q_ASSERT(internalClass() && internalClass()->find(s.engine->id_prototype()->propertyKey()) == protoSlot); + Q_ASSERT(s.engine->internalClasses(EngineBase::Class_ObjectProto)->find(s.engine->id_constructor()->propertyKey()) == protoConstructorSlot); - if (n) - defineReadonlyConfigurableProperty(s.engine->id_name(), *n); + ScopedObject proto(s, s.engine->newObject(s.engine->internalClasses(EngineBase::Class_ObjectProto))); + proto->setProperty(protoConstructorSlot, d()); + setProperty(protoSlot, proto); } ReturnedValue FunctionObject::name() const @@ -163,19 +169,48 @@ ReturnedValue FunctionObject::name() const return get(scope()->internalClass->engine->id_name()); } -ReturnedValue FunctionObject::callAsConstructor(const FunctionObject *f, const Value *, int) +ReturnedValue FunctionObject::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) { return f->engine()->throwTypeError(); } -ReturnedValue FunctionObject::call(const FunctionObject *, const Value *, const Value *, int) +ReturnedValue FunctionObject::virtualCall(const FunctionObject *, const Value *, const Value *, int) { return Encode::undefined(); } 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::createConstructorFunction(ExecutionContext *scope, Function *function, bool isDerivedConstructor) +{ + if (!function) { + Heap::DefaultClassConstructorFunction *c = scope->engine()->memoryManager->allocate<DefaultClassConstructorFunction>(scope); + c->isDerivedConstructor = isDerivedConstructor; + return c; + } + Heap::ConstructorFunction *c = scope->engine()->memoryManager->allocate<ConstructorFunction>(scope, function); + c->isDerivedConstructor = isDerivedConstructor; + return c; +} + +Heap::FunctionObject *FunctionObject::createMemberFunction(ExecutionContext *scope, Function *function) +{ + return scope->engine()->memoryManager->allocate<MemberFunction>(scope, function); +} + +Heap::FunctionObject *FunctionObject::createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, VTable::Call 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 +236,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,42 +248,58 @@ 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::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +{ + 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)); } // 15.3.1: This is equivalent to new Function(...) -ReturnedValue FunctionCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) +ReturnedValue FunctionCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) { - return callAsConstructor(f, argv, argc); + return virtualCallAsConstructor(f, argv, argc, f); } DEFINE_OBJECT_VTABLE(FunctionPrototype); @@ -268,13 +317,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 +334,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 +366,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>(); @@ -323,7 +385,7 @@ ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, cons arguments[i] = Primitive::undefinedValue(); } else { for (quint32 i = 0; i < len; ++i) - arguments[i] = arr->getIndexed(i); + arguments[i] = arr->get(i); } } @@ -383,18 +445,50 @@ 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::virtualInstanceOf(o, argv[0]); +} + DEFINE_OBJECT_VTABLE(ScriptFunction); -ReturnedValue ScriptFunction::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc) +ReturnedValue ScriptFunction::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *newTarget) { ExecutionEngine *v4 = fo->engine(); const ScriptFunction *f = static_cast<const ScriptFunction *>(fo); + Q_ASSERT(newTarget->isFunctionObject()); + const FunctionObject *nt = static_cast<const FunctionObject *>(newTarget); Scope scope(v4); - InternalClass *ic = f->classForConstructor(); + Scoped<InternalClass> ic(scope); + if (nt->d() == f->d()) { + ic = f->classForConstructor(); + } else { + const Object *o = nt->d()->protoProperty(); + ic = scope.engine->internalClasses(EngineBase::Class_Object); + if (o) + ic = ic->changePrototype(o->d()); + } ScopedValue thisObject(scope, v4->memoryManager->allocObject<Object>(ic)); - ReturnedValue result = Moth::VME::exec(fo, thisObject, argv, argc); + CppStackFrame frame; + frame.init(v4, f->function(), argv, argc); + frame.setupJSFrame(v4->jsStackTop, *f, f->scope(), + thisObject, + newTarget ? *newTarget : Primitive::undefinedValue()); + + frame.push(); + v4->jsStackTop += frame.requiredJSStackFrameSize(); + + ReturnedValue result = Moth::VME::exec(&frame, v4); + + frame.pop(); if (Q_UNLIKELY(v4->hasException)) return Encode::undefined(); @@ -403,9 +497,23 @@ ReturnedValue ScriptFunction::callAsConstructor(const FunctionObject *fo, const return result; } -ReturnedValue ScriptFunction::call(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc) +ReturnedValue ScriptFunction::virtualCall(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc) { - return Moth::VME::exec(fo, thisObject, argv, argc); + ExecutionEngine *engine = fo->engine(); + CppStackFrame frame; + frame.init(engine, fo->function(), argv, argc); + frame.setupJSFrame(engine->jsStackTop, *fo, fo->scope(), + thisObject ? *thisObject : Primitive::undefinedValue(), + Primitive::undefinedValue()); + + frame.push(); + engine->jsStackTop += frame.requiredJSStackFrameSize(); + + ReturnedValue result = Moth::VME::exec(&frame, engine); + + frame.pop(); + + return result; } void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function) @@ -415,38 +523,123 @@ 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); - } + if (name) + f->setName(name); + f->createDefaultPrototypeProperty(Heap::FunctionObject::Index_Prototype, Heap::FunctionObject::Index_ProtoConstructor); + + Q_ASSERT(internalClass && internalClass->find(s.engine->id_length()->propertyKey()) == 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->d(); +} + +DEFINE_OBJECT_VTABLE(ConstructorFunction); + +ReturnedValue ConstructorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) +{ + const ConstructorFunction *c = static_cast<const ConstructorFunction *>(f); + if (!c->d()->isDerivedConstructor) + return ScriptFunction::virtualCallAsConstructor(f, argv, argc, newTarget); + + ExecutionEngine *v4 = f->engine(); + + CppStackFrame frame; + frame.init(v4, f->function(), argv, argc); + frame.setupJSFrame(v4->jsStackTop, *f, f->scope(), + Primitive::undefinedValue(), + newTarget ? *newTarget : Primitive::undefinedValue()); + + frame.push(); + v4->jsStackTop += frame.requiredJSStackFrameSize(); + + ReturnedValue result = Moth::VME::exec(&frame, v4); + + frame.pop(); + + if (Q_UNLIKELY(v4->hasException)) + return Encode::undefined(); + else if (Value::fromReturnedValue(result).isObject()) + return result; + else if (!Value::fromReturnedValue(result).isUndefined()) + return v4->throwTypeError(); + return frame.jsFrame->thisObject.asReturnedValue(); +} + +ReturnedValue ConstructorFunction::virtualCall(const FunctionObject *f, const Value *, const Value *, int) +{ + return f->engine()->throwTypeError(QStringLiteral("Cannot call a class constructor without |new|")); +} - return ic; +DEFINE_OBJECT_VTABLE(MemberFunction); + +ReturnedValue MemberFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) +{ + return f->engine()->throwTypeError(QStringLiteral("Function is not a constructor.")); +} + +DEFINE_OBJECT_VTABLE(DefaultClassConstructorFunction); + +ReturnedValue DefaultClassConstructorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) +{ + const DefaultClassConstructorFunction *c = static_cast<const DefaultClassConstructorFunction *>(f); + ExecutionEngine *v4 = f->engine(); + + Scope scope(v4); + + if (!c->d()->isDerivedConstructor) { + ScopedObject proto(scope, static_cast<const Object *>(newTarget) ->get(scope.engine->id_prototype())); + ScopedObject c(scope, scope.engine->newObject()); + c->setPrototypeUnchecked(proto); + return c->asReturnedValue(); + } + + ScopedFunctionObject super(scope, f->getPrototypeOf()); + Q_ASSERT(super->isFunctionObject()); + + CppStackFrame frame; + frame.init(v4, nullptr, argv, argc); + frame.setupJSFrame(v4->jsStackTop, *f, f->scope(), + Primitive::undefinedValue(), + newTarget ? *newTarget : Primitive::undefinedValue(), argc, argc); + + frame.push(); + v4->jsStackTop += frame.requiredJSStackFrameSize(argc); + + // Do a super call + ReturnedValue result = super->callAsConstructor(argv, argc, newTarget); + + frame.pop(); + + if (Q_UNLIKELY(v4->hasException)) + return Encode::undefined(); + else if (Value::fromReturnedValue(result).isObject()) + return result; + else if (!Value::fromReturnedValue(result).isUndefined()) + return v4->throwTypeError(); + return frame.jsFrame->thisObject.asReturnedValue(); +} + +ReturnedValue DefaultClassConstructorFunction::virtualCall(const FunctionObject *f, const Value *, const Value *, int) +{ + return f->engine()->throwTypeError(QStringLiteral("Cannot call a class constructor without |new|")); } DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction); @@ -479,7 +672,7 @@ void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } -ReturnedValue BoundFunction::call(const FunctionObject *fo, const Value *, const Value *argv, int argc) +ReturnedValue BoundFunction::virtualCall(const FunctionObject *fo, const Value *, const Value *argv, int argc) { const BoundFunction *f = static_cast<const BoundFunction *>(fo); Scope scope(f->engine()); @@ -500,7 +693,7 @@ ReturnedValue BoundFunction::call(const FunctionObject *fo, const Value *, const return target->call(jsCallData); } -ReturnedValue BoundFunction::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc) +ReturnedValue BoundFunction::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *) { const BoundFunction *f = static_cast<const BoundFunction *>(fo); Scope scope(f->engine()); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 32e71a175b..1acb1df0b4 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -64,17 +64,14 @@ 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 { #define FunctionObjectMembers(class, Member) \ Member(class, Pointer, ExecutionContext *, scope) \ Member(class, NoMark, Function *, function) \ - Member(class, NoMark, jsCallFunction, jsCall) \ - Member(class, NoMark, jsConstructFunction, jsConstruct) + Member(class, NoMark, VTable::Call, jsCall) \ + Member(class, NoMark, VTable::CallAsConstructor, jsConstruct) DECLARE_HEAP_OBJECT(FunctionObject, Object) { DECLARE_MARKOBJECTS(FunctionObject); @@ -111,14 +108,30 @@ 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; +struct ConstructorFunction : ScriptFunction +{ + bool isDerivedConstructor; +}; + +struct MemberFunction : ScriptFunction +{ +}; + +struct DefaultClassConstructorFunction : FunctionObject +{ + bool isDerivedConstructor; }; #define BoundFunctionMembers(class, Member) \ @@ -152,25 +165,26 @@ struct Q_QML_EXPORT FunctionObject: Object { unsigned int formalParameterCount() const { return d()->formalParameterCount(); } unsigned int varCount() const { return d()->varCount(); } - void init(String *name, bool createProto); + void setName(String *name) { + defineReadonlyConfigurableProperty(engine()->id_name(), *name); + } + void createDefaultPrototypeProperty(uint protoSlot, uint protoConstructorSlot); inline ReturnedValue callAsConstructor(const JSCallData &data) const; - ReturnedValue callAsConstructor(const Value *argv, int argc) const { - return d()->jsConstruct(this, argv, argc); + ReturnedValue callAsConstructor(const Value *argv, int argc, const Value *newTarget = nullptr) const { + return d()->jsConstruct(this, argv, argc, newTarget ? newTarget : this); } inline ReturnedValue call(const JSCallData &data) const; ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const { return d()->jsCall(this, thisObject, argv, argc); } - 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 virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function); - 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 *createConstructorFunction(ExecutionContext *scope, Function *function, bool isDerivedConstructor); + static Heap::FunctionObject *createMemberFunction(ExecutionContext *scope, Function *function); + static Heap::FunctionObject *createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, VTable::Call code, int argumentCount); bool strictMode() const { return d()->function ? d()->function->isStrict() : false; } bool isBinding() const; @@ -181,7 +195,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; } @@ -189,8 +203,14 @@ struct FunctionCtor: FunctionObject { V4_OBJECT2(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); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +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 +223,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 @@ -224,27 +245,46 @@ struct ScriptFunction : FunctionObject { V4_INTERNALCLASS(ScriptFunction) enum { NInlineProperties = 3 }; - static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + + Heap::InternalClass *classForConstructor() const; +}; + +struct ConstructorFunction : ScriptFunction { + V4_OBJECT2(ConstructorFunction, ScriptFunction) + V4_INTERNALCLASS(ConstructorFunction) + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; - InternalClass *classForConstructor() const; +struct MemberFunction : ScriptFunction { + V4_OBJECT2(MemberFunction, ScriptFunction) + V4_INTERNALCLASS(MemberFunction) + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); }; +struct DefaultClassConstructorFunction : FunctionObject { + V4_OBJECT2(DefaultClassConstructorFunction, FunctionObject) + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + struct BoundFunction: FunctionObject { V4_OBJECT2(BoundFunction, FunctionObject) static Heap::BoundFunction *create(ExecutionContext *scope, FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs) { - return scope->engine()->memoryManager->allocObject<BoundFunction>(scope, target, boundThis, boundArgs); + return scope->engine()->memoryManager->allocate<BoundFunction>(scope, target, boundThis, boundArgs); } Heap::FunctionObject *target() const { return d()->target; } Value boundThis() const { return d()->boundThis; } Heap::MemberData *boundArgs() const { return d()->boundArgs; } - static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; } diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp new file mode 100644 index 0000000000..8c3cd8b863 --- /dev/null +++ b/src/qml/jsruntime/qv4generatorobject.cpp @@ -0,0 +1,252 @@ +/**************************************************************************** +** +** 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::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +{ + 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::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + return virtualCallAsConstructor(f, argv, argc, f); +} + +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->setPrototypeOf(scope.engine->generatorPrototype()); + g->defineDefaultProperty(scope.engine->id_prototype(), proto, Attr_NotConfigurable|Attr_NotEnumerable); + g->setPrototypeOf(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype()))); + return g->d(); +} + +ReturnedValue GeneratorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) +{ + return f->engine()->throwTypeError(); +} + +ReturnedValue GeneratorFunction::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +{ + const GeneratorFunction *gf = static_cast<const GeneratorFunction *>(f); + Function *function = gf->function(); + ExecutionEngine *engine = gf->engine(); + + // We need to set up a separate stack for the generator, as it's being re-entered + uint stackSize = argc // space for the original arguments + + CppStackFrame::requiredJSStackFrameSize(function); // space for the JS stack frame + + size_t requiredMemory = sizeof(GeneratorObject::Data) - sizeof(Value) + sizeof(Value) * stackSize; + + Scope scope(gf); + Scoped<GeneratorObject> g(scope, scope.engine->memoryManager->allocManaged<GeneratorObject>(requiredMemory, scope.engine->classes[EngineBase::Class_GeneratorObject])); + g->setPrototypeOf(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.init(engine, function, gp->stack.values, argc); + gp->cppFrame.setupJSFrame(&gp->stack.values[argc], *gf, gf->scope(), + thisObject ? *thisObject : Primitive::undefinedValue(), + Primitive::undefinedValue()); + + gp->cppFrame.push(); + + Moth::VME::interpret(&gp->cppFrame, engine, function->codeData); + gp->state = GeneratorState::SuspendedStart; + + gp->cppFrame.pop(); + 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); + + setPrototypeOf(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, engine, 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); +} + +DEFINE_OBJECT_VTABLE(MemberGeneratorFunction); + +Heap::FunctionObject *MemberGeneratorFunction::create(ExecutionContext *context, Function *function) +{ + Scope scope(context); + Scoped<GeneratorFunction> g(scope, context->engine()->memoryManager->allocate<MemberGeneratorFunction>(context, function)); + ScopedObject proto(scope, scope.engine->newObject()); + proto->setPrototypeOf(scope.engine->generatorPrototype()); + g->defineDefaultProperty(scope.engine->id_prototype(), proto, Attr_NotConfigurable|Attr_NotEnumerable); + g->setPrototypeOf(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype()))); + return g->d(); +} + +ReturnedValue MemberGeneratorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) +{ + return f->engine()->throwTypeError(QStringLiteral("Function is not a constructor.")); +} diff --git a/src/qml/jsruntime/qv4generatorobject_p.h b/src/qml/jsruntime/qv4generatorobject_p.h new file mode 100644 index 0000000000..a97050473c --- /dev/null +++ b/src/qml/jsruntime/qv4generatorobject_p.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** 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" +#include "qv4stackframe_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 MemberGeneratorFunction : 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 virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct GeneratorFunction : ScriptFunction +{ + V4_OBJECT2(GeneratorFunction, ScriptFunction) + V4_INTERNALCLASS(GeneratorFunction) + + static Heap::FunctionObject *create(ExecutionContext *scope, Function *function); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct MemberGeneratorFunction : GeneratorFunction +{ + V4_OBJECT2(MemberGeneratorFunction, GeneratorFunction) + V4_INTERNALCLASS(MemberGeneratorFunction) + + static Heap::FunctionObject *create(ExecutionContext *scope, Function *function); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); +}; + +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..e3b3423a9d 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,19 @@ namespace Heap { template <typename T, size_t> struct Pointer; } +struct CppStackFrame; class MemoryManager; class ExecutableAllocator; +struct PropertyKey; +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 +250,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 +259,7 @@ typedef Scoped<ExecutionContext> ScopedContext; struct PersistentValueStorage; class PersistentValue; class WeakValue; +struct MarkStack; struct IdentifierTable; class RegExpCache; @@ -357,6 +373,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..43895011f3 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -335,7 +335,7 @@ void Heap::EvalFunction::init(QV4::ExecutionContext *scope) Scope s(scope); Heap::FunctionObject::init(scope, s.engine->id_eval()); ScopedFunctionObject f(s, this); - f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(1)); + f->defineReadonlyConfigurableProperty(s.engine->id_length(), Primitive::fromInt32(1)); } ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc, bool directCall) const @@ -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(); @@ -384,7 +384,7 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc, } -ReturnedValue EvalFunction::call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +ReturnedValue EvalFunction::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) { // indirect call return static_cast<const EvalFunction *>(f)->evalCall(thisObject, argv, argc, false); diff --git a/src/qml/jsruntime/qv4globalobject_p.h b/src/qml/jsruntime/qv4globalobject_p.h index fd1820c23c..021b445955 100644 --- a/src/qml/jsruntime/qv4globalobject_p.h +++ b/src/qml/jsruntime/qv4globalobject_p.h @@ -71,7 +71,7 @@ struct Q_QML_EXPORT EvalFunction : FunctionObject ReturnedValue evalCall(const Value *thisObject, const Value *argv, int argc, bool directCall) const; - static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct GlobalFunctions diff --git a/src/qml/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp index c122bcb51a..5db5bd46ec 100644 --- a/src/qml/jsruntime/qv4identifier.cpp +++ b/src/qml/jsruntime/qv4identifier.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qv4identifier_p.h" #include "qv4identifiertable_p.h" +#include "qv4string_p.h" QT_BEGIN_NAMESPACE @@ -54,14 +55,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 +76,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 +101,10 @@ void IdentifierHash::detach() } -IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier) +IdentifierHashEntry *IdentifierHash::addEntry(PropertyKey identifier) { + Q_ASSERT(identifier.isStringOrSymbol()); + // fill up to max 50% bool grow = (d->alloc <= d->size*2); @@ -104,10 +115,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 +129,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 +140,15 @@ IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier) return d->entries + idx; } -const IdentifierHashEntry *IdentifierHash::lookup(const Identifier *identifier) const +const IdentifierHashEntry *IdentifierHash::lookup(PropertyKey identifier) const { - if (!d) + if (!d || !identifier.isStringOrSymbol()) 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,39 +161,54 @@ 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; - } + PropertyKey id = d->identifierTable->asPropertyKey(str); + return lookup(id); } const IdentifierHashEntry *IdentifierHash::lookup(String *str) const { if (!d) return nullptr; - if (str->d()->identifier) - return lookup(str->d()->identifier); + PropertyKey id = d->identifierTable->asPropertyKey(str); + if (id.isValid()) + return lookup(id); return lookup(str->toQString()); } -const Identifier *IdentifierHash::toIdentifier(const QString &str) const +const PropertyKey IdentifierHash::toIdentifier(const QString &str) const { Q_ASSERT(d); - return d->identifierTable->identifier(str); + return d->identifierTable->asPropertyKey(str); } -const Identifier *IdentifierHash::toIdentifier(Heap::String *str) const +const PropertyKey IdentifierHash::toIdentifier(Heap::String *str) const { Q_ASSERT(d); - return d->identifierTable->identifier(str); + return d->identifierTable->asPropertyKey(str); +} + +QString QV4::IdentifierHash::findId(int value) const +{ + IdentifierHashEntry *e = d->entries; + IdentifierHashEntry *end = e + d->alloc; + while (e < end) { + if (e->identifier.isValid() && e->value == value) + return e->identifier.toQString(); + ++e; + } + return QString(); +} + +void IdentifierHashData::markObjects(MarkStack *markStack) const +{ + IdentifierHashEntry *e = entries; + IdentifierHashEntry *end = e + alloc; + while (e < end) { + if (Heap::Base *o = e->identifier.asStringOrSymbol()) + o->mark(markStack); + ++e; + } } diff --git a/src/qml/jsruntime/qv4identifier_p.h b/src/qml/jsruntime/qv4identifier_p.h index 82346d5f68..32de8b7c8d 100644 --- a/src/qml/jsruntime/qv4identifier_p.h +++ b/src/qml/jsruntime/qv4identifier_p.h @@ -51,38 +51,24 @@ // #include <qstring.h> +#include <private/qv4global_p.h> +#include <private/qv4propertykey_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -namespace Heap { - struct String; -} - -struct String; -struct IdentifierTable; -struct ExecutionEngine; - -struct Identifier -{ - QString string; - uint hashValue; -}; - - struct IdentifierHashEntry { - const Identifier *identifier; + PropertyKey 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 +103,12 @@ struct IdentifierHash QString findId(int value) const; protected: - IdentifierHashEntry *addEntry(const Identifier *i); - const IdentifierHashEntry *lookup(const Identifier *identifier) const; + IdentifierHashEntry *addEntry(PropertyKey i); + const IdentifierHashEntry *lookup(PropertyKey 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 PropertyKey toIdentifier(const QString &str) const; + const PropertyKey toIdentifier(Heap::String *str) const; }; @@ -180,20 +166,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..0412695404 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 @@ -53,44 +54,44 @@ static inline int primeForNumBits(int numBits) } -IdentifierTable::IdentifierTable(ExecutionEngine *engine) +IdentifierTable::IdentifierTable(ExecutionEngine *engine, int numBits) : engine(engine) , size(0) - , numBits(8) + , numBits(numBits) { 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 = PropertyKey::fromStringOrSymbol(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; } @@ -120,10 +146,16 @@ Heap::String *IdentifierTable::insertString(const QString &s) { uint subtype; uint hash = String::createHashValue(s.constData(), s.length(), &subtype); + if (subtype == Heap::String::StringType_ArrayIndex) { + Heap::String *str = engine->newString(s); + str->stringHash = hash; + str->subtype = subtype; + return str; + } 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 +167,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) +} + + +PropertyKey IdentifierTable::asPropertyKeyImpl(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 = PropertyKey::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,37 +214,117 @@ Identifier *IdentifierTable::identifierImpl(const Heap::String *str) return str->identifier; } -Heap::String *IdentifierTable::stringFromIdentifier(Identifier *i) +Heap::StringOrSymbol *IdentifierTable::resolveId(PropertyKey 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(PropertyKey i) const +{ + Heap::StringOrSymbol *s = resolveId(i); + Q_ASSERT(s && s->internalClass->vtable->isString); + return static_cast<Heap::String *>(s); +} + +Heap::Symbol *IdentifierTable::symbolForId(PropertyKey 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; + uint 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) % alloc)) { + Q_ASSERT(table[lastEntry] == nullptr); + table[lastEntry] = entry; + table[idx] = nullptr; + + // find next free slot just like in addEntry() + do { + lastEntry = (lastEntry + 1) % alloc; + } while (table[lastEntry] != nullptr); + } + continue; + } + if (lastEntry == -1) { + lastEntry = idx; + lastKey = f(entry) % alloc; + } + 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<uint>(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; +} + +PropertyKey IdentifierTable::asPropertyKey(const QString &s) { return insertString(s)->identifier; } -Identifier *IdentifierTable::identifier(const char *s, int len) +PropertyKey IdentifierTable::asPropertyKey(const char *s, int len) { uint subtype; uint hash = String::createHashValue(s, len, &subtype); if (hash == UINT_MAX) - return identifier(QString::fromUtf8(s, len)); + return asPropertyKey(QString::fromUtf8(s, 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..3674ece84f 100644 --- a/src/qml/jsruntime/qv4identifiertable_p.h +++ b/src/qml/jsruntime/qv4identifiertable_p.h @@ -53,55 +53,61 @@ #include "qv4identifier_p.h" #include "qv4string_p.h" #include "qv4engine_p.h" +#include <qset.h> #include <limits.h> QT_BEGIN_NAMESPACE namespace QV4 { -struct IdentifierTable +struct Q_QML_PRIVATE_EXPORT IdentifierTable { ExecutionEngine *engine; 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: - IdentifierTable(ExecutionEngine *engine); + IdentifierTable(ExecutionEngine *engine, int numBits = 8); ~IdentifierTable(); Heap::String *insertString(const QString &s); + Heap::Symbol *insertSymbol(const QString &s); - Identifier *identifier(const Heap::String *str) { - if (str->identifier) + PropertyKey asPropertyKey(const Heap::String *str) { + if (str->identifier.isValid()) return str->identifier; - return identifierImpl(str); + return asPropertyKeyImpl(str); } - Identifier *identifier(const QV4::String *str) { - return identifier(str->d()); + PropertyKey asPropertyKey(const QV4::String *str) { + return asPropertyKey(str->d()); } - Identifier *identifier(const QString &s); - Identifier *identifier(const char *s, int len); + PropertyKey asPropertyKey(const QString &s); + PropertyKey asPropertyKey(const char *s, int len); + + PropertyKey asPropertyKeyImpl(const Heap::String *str); - Identifier *identifierImpl(const Heap::String *str); + Heap::StringOrSymbol *resolveId(PropertyKey i) const; + Heap::String *stringForId(PropertyKey i) const; + Heap::Symbol *symbolForId(PropertyKey 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..c890dc0550 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(PropertyKey identifier, int classSize) +{ + int val = -1; + PropertyHashData *dd = new PropertyHashData(d->numBits); + for (int i = 0; i < d->alloc; ++i) { + const Entry &e = d->entries[i]; + if (!e.identifier.isValid() || e.index >= static_cast<unsigned>(classSize)) + continue; + if (e.identifier == identifier) { + val = e.index; + continue; + } + uint idx = e.identifier.id() % dd->alloc; + while (dd->entries[idx].identifier.isValid()) { + ++idx; + idx %= dd->alloc; + } + dd->entries[idx] = e; + } + dd->size = classSize; + if (!--d->refCount) + delete d; + d = dd; + + Q_ASSERT(val != -1); + return val; +} + +void PropertyHash::detach(bool grow, int classSize) +{ + if (d->refCount == 1 && !grow) + 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<PropertyKey>(); + 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<PropertyKey>(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<PropertyKey>(); + 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, PropertyKey id, PropertyAttributes data, uint *index) { + Q_ASSERT(id.isStringOrSymbol()); 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, PropertyKey::invalid()); + newClass->propertyData.add(newClass->size, PropertyAttributes()); + ++newClass->size; +} + +Heap::InternalClass *InternalClass::changeMember(PropertyKey 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<PropertyKey>(); + newClass->propertyData = SharedInternalClassData<PropertyAttributes>(); + newClass->size = 0; + for (uint i = 0; i < size; ++i) { + PropertyKey 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 = { { PropertyKey::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 = { { PropertyKey::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 = { { PropertyKey::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, PropertyKey id, PropertyAttributes data, uint *index) { + Q_ASSERT(id.isStringOrSymbol()); 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(PropertyKey identifier, PropertyAttributes data, uint *index) { + Q_ASSERT(identifier.isStringOrSymbol()); 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(PropertyKey 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, PropertyKey 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, PropertyKey::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 = { { PropertyKey::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 = { { PropertyKey::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 = { { PropertyKey::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) { + PropertyKey id = ic->nameMap.at(i); + if (Heap::Base *b = id.asStringOrSymbol()) + 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..888eedcaba 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/qv4propertykey_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; + PropertyKey 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(PropertyKey identifier) const; + int removeIdentifier(PropertyKey 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(PropertyKey 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; + PropertyKey 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<PropertyKey> 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, PropertyKey id, PropertyAttributes data, uint *index); + Q_REQUIRED_RESULT InternalClass *addMember(PropertyKey identifier, PropertyAttributes data, uint *index = nullptr); + Q_REQUIRED_RESULT InternalClass *changeMember(PropertyKey identifier, PropertyAttributes data, uint *index = nullptr); + static void changeMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, uint *index = nullptr); + static void removeMember(QV4::Object *object, PropertyKey identifier); + uint find(const PropertyKey id) { + Q_ASSERT(id.isStringOrSymbol()); + 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(PropertyKey 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..307eec9111 100644 --- a/src/qml/jsruntime/qv4jscall_p.h +++ b/src/qml/jsruntime/qv4jscall_p.h @@ -55,6 +55,7 @@ #include "qv4functionobject_p.h" #include "qv4context_p.h" #include "qv4scopedvalue_p.h" +#include "qv4stackframe_p.h" QT_BEGIN_NAMESPACE @@ -67,7 +68,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 +81,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(); @@ -102,7 +102,9 @@ struct JSCallData { inline ReturnedValue FunctionObject::callAsConstructor(const JSCallData &data) const { - return d()->jsConstruct(this, data.args, data.argc); + if (!d()->jsConstruct) + return engine()->throwTypeError(QStringLiteral("Object is not a constructor.")); + return d()->jsConstruct(this, data.args, data.argc, this); } inline diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index c3569c29d2..2e2314cafe 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> @@ -260,11 +261,12 @@ bool JsonParser::parseMember(Object *o) if (!parseValue(val)) return false; - ScopedString s(scope, engine->newIdentifier(key)); - uint idx = s->asArrayIndex(); - if (idx < UINT_MAX) { - o->putIndexed(idx, val); + ScopedString s(scope, engine->newString(key)); + PropertyKey skey = s->toPropertyKey(); + if (skey.isArrayIndex()) { + o->put(skey.asArrayIndex(), val); } else { + // avoid trouble with properties named __proto__ o->insertMember(s, val); } @@ -743,7 +745,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); @@ -846,7 +848,7 @@ QString Stringify::JA(Object *a) ScopedValue v(scope); for (uint i = 0; i < len; ++i) { bool exists; - v = a->getIndexed(i, &exists); + v = a->get(i, &exists); if (!exists) { partial += QStringLiteral("null"); continue; @@ -881,6 +883,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); } @@ -916,7 +920,7 @@ ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value stringify.propertyList = static_cast<QV4::String *>(scope.alloc(arrayLen)); for (uint i = 0; i < arrayLen; ++i) { Value *v = stringify.propertyList + i; - *v = o->getIndexed(i); + *v = o->get(i); if (v->as<NumberObject>() || v->as<StringObject>() || v->isNumber()) *v = v->toString(scope.engine); if (!v->isString()) { @@ -1078,7 +1082,7 @@ QJsonArray JsonObject::toJsonArray(const ArrayObject *a, V4ObjectSet &visitedObj ScopedValue v(scope); quint32 length = a->getLength(); for (quint32 i = 0; i < length; ++i) { - v = a->getIndexed(i); + v = a->get(i); if (v->as<FunctionObject>()) v = Encode::null(); result.append(toJsonValue(v, visitedObjects)); diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 52ab03cd94..daa5b2dfbd 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(PropertyKey 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]); + PropertyKey name = engine->identifierTable->asPropertyKey(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; + PropertyKey name = engine->identifierTable->asPropertyKey(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; + PropertyKey name = engine->identifierTable->asPropertyKey(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->get(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(); + PropertyKey key = name->toPropertyKey(); + uint idx = c->find(key); if (idx != UINT_MAX) { if (object->isArrayObject() && idx == Heap::ArrayObject::LengthPropertyIndex) { setter = arrayLengthSetter; @@ -460,8 +484,8 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value return setter(this, engine, *object, value); } - insertionLookup.icIdentifier = c->id; - if (!object->put(name, value)) { + insertionLookup.protoId = c->protoId; + if (!object->put(key, value)) { setter = Lookup::setterFallback; return false; } @@ -471,7 +495,7 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value setter = setterFallback; return true; } - idx = object->internalClass()->find(name); + idx = object->internalClass()->find(key); if (idx == UINT_MAX) { // ### can this even happen? setter = setterFallback; return false; @@ -574,7 +598,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..c7555539b4 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(PropertyKey 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..bb7b8086e4 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -43,34 +43,23 @@ using namespace QV4; +DEFINE_MANAGED_VTABLE(Managed); -const VTable Managed::static_vtbl = -{ - nullptr, - 0, - 0, - Managed::IsExecutionContext, - Managed::IsString, - Managed::IsObject, - Managed::IsFunctionObject, - Managed::IsErrorObject, - Managed::IsArrayData, - 0, - Managed::MyType, - "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 +69,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 +81,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 +91,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"; @@ -104,6 +99,9 @@ QString Managed::className() const case Type_JsonObject: s = "JSON"; break; + case Type_ProxyObject: + s = "ProxyObject"; + break; case Type_MathObject: s = "Math"; break; @@ -111,8 +109,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"; @@ -125,7 +138,7 @@ QString Managed::className() const return QString::fromLatin1(s); } -bool Managed::isEqualTo(Managed *, Managed *) +bool Managed::virtualIsEqualTo(Managed *, Managed *) { return false; } diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index 092c61b81c..cacb262ba7 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -55,6 +55,7 @@ #include "qv4enginebase_p.h" #include <private/qv4heap_p.h> #include <private/qv4writebarrier_p.h> +#include <private/qv4vtable_p.h> QT_BEGIN_NAMESPACE @@ -70,13 +71,9 @@ inline int qYouForgotTheQ_MANAGED_Macro(T, T) { return 0; } template <typename T1, typename T2> inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} -#ifdef Q_COMPILER_STATIC_ASSERT -#define V4_MANAGED_SIZE_TEST void __dataTest() { Q_STATIC_ASSERT(sizeof(*this) == sizeof(Managed)); } -#else -#define V4_MANAGED_SIZE_TEST -#endif +#define V4_MANAGED_SIZE_TEST void __dataTest() { static_assert (sizeof(*this) == sizeof(Managed), "Classes derived from Managed can't have own data members."); } -#define V4_NEEDS_DESTROY static void destroy(QV4::Heap::Base *b) { static_cast<Data *>(b)->destroy(); } +#define V4_NEEDS_DESTROY static void virtualDestroy(QV4::Heap::Base *b) { static_cast<Data *>(b)->destroy(); } #define V4_MANAGED_ITSELF(DataClass, superClass) \ @@ -92,77 +89,30 @@ 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: \ enum { MyType = Type_##type }; -#define Q_VTABLE_FUNCTION(classname, func) \ - (classname::func == QV4::Managed::func ? 0 : classname::func) - -// Q_VTABLE_FUNCTION triggers a bogus tautological-compare warning in GCC6+ -#if (defined(Q_CC_GNU) && Q_CC_GNU >= 600) -#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \ - QT_WARNING_PUSH; \ - QT_WARNING_DISABLE_GCC("-Wtautological-compare") - -#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF \ - ;QT_WARNING_POP -#elif defined(Q_CC_CLANG) && Q_CC_CLANG >= 306 -#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \ - QT_WARNING_PUSH; \ - QT_WARNING_DISABLE_CLANG("-Wtautological-compare") - -#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF \ - ;QT_WARNING_POP -#else -#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON -#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF -#endif - -#define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \ -{ \ - parentVTable, \ - (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ - (sizeof(classname::Data) + (classname::NInlineProperties*sizeof(QV4::Value)) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \ - - (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ - classname::IsExecutionContext, \ - classname::IsString, \ - classname::IsObject, \ - classname::IsFunctionObject, \ - classname::IsErrorObject, \ - classname::IsArrayData, \ - 0, \ - classname::MyType, \ - #classname, \ - Q_VTABLE_FUNCTION(classname, destroy), \ - classname::Data::markObjects, \ - isEqualTo \ -} \ - -#define DEFINE_MANAGED_VTABLE(classname) \ -QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \ -const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, 0) \ -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 +struct Q_QML_PRIVATE_EXPORT Managed : Value, VTableBase { V4_MANAGED_ITSELF(Base, Managed) enum { IsExecutionContext = false, IsString = false, + IsStringOrSymbol = false, IsObject = false, IsFunctionObject = false, IsErrorObject = false, @@ -180,47 +130,55 @@ 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, Type_ArgumentsObject, Type_JsonObject, Type_MathObject, + Type_ProxyObject, 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)); } - - static bool isEqualTo(Managed *m, Managed *other); + { return d()->internalClass->vtable->isEqualTo(const_cast<Managed *>(this), const_cast<Managed *>(other)); } bool inUse() const { return d()->inUse(); } bool markBit() const { return d()->isMarked(); } inline void mark(MarkStack *markStack); - static void destroy(Heap::Base *) {} - Q_ALWAYS_INLINE Heap::Base *heapObject() const { return m(); } @@ -232,6 +190,9 @@ public: return static_cast<const T *>(this); } +protected: + static bool virtualIsEqualTo(Managed *m, Managed *other); + private: friend class MemoryManager; friend struct Identifiers; @@ -254,6 +215,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(PropertyKey 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..7be7416e4a --- /dev/null +++ b/src/qml/jsruntime/qv4mapiterator.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** 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/qv4estable_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); + uint index = thisObject->d()->mapNextIndex; + IteratorKind itemKind = thisObject->d()->iterationKind; + + if (!s) { + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); + } + + Value *arguments = scope.alloc(2); + + while (index < s->d()->esTable->size()) { + s->d()->esTable->iterate(index, &arguments[0], &arguments[1]); + thisObject->d()->mapNextIndex = index + 1; + + ScopedValue result(scope); + + if (itemKind == KeyIteratorKind) { + result = arguments[0]; + } else if (itemKind == ValueIteratorKind) { + result = arguments[1]; + } else { + Q_ASSERT(itemKind == KeyValueIteratorKind); + + result = scope.engine->newArrayObject(); + + Scoped<ArrayObject> resultArray(scope, result); + resultArray->arrayReserve(2); + resultArray->arrayPut(0, arguments[0]); + resultArray->arrayPut(1, arguments[1]); + 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..ca9e1723f9 --- /dev/null +++ b/src/qml/jsruntime/qv4mapobject.cpp @@ -0,0 +1,279 @@ +/**************************************************************************** +** +** 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 "qv4estable_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::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +{ + Scope scope(f); + Scoped<MapObject> a(scope, scope.engine->memoryManager->allocate<MapObject>()); + + 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::virtualCall(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); +} + +void Heap::MapObject::init() +{ + Object::init(); + esTable = new ESTable(); +} + +void Heap::MapObject::destroy() +{ + delete esTable; + esTable = 0; +} + +void Heap::MapObject::markObjects(Heap::Base *that, MarkStack *markStack) +{ + MapObject *m = static_cast<MapObject *>(that); + m->esTable->markObjects(markStack); + Object::markObjects(that, markStack); +} + +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()->esTable->clear(); + return Encode::undefined(); +} + +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(); + + return Encode(that->d()->esTable->remove(argv[0])); +} + +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]); + + Value *arguments = scope.alloc(3); + for (uint i = 0; i < that->d()->esTable->size(); ++i) { + that->d()->esTable->iterate(i, &arguments[0], &arguments[1]); // fill in key (0), value (1) + + 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(); + + return that->d()->esTable->get(argv[0]); +} + +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(); + + return Encode(that->d()->esTable->has(argv[0])); +} + +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(); + + that->d()->esTable->set(argv[0], 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(); + + return Encode(that->d()->esTable->size()); +} + +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..6793612bcb --- /dev/null +++ b/src/qml/jsruntime/qv4mapobject_p.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** 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 { + +class ESTable; + +namespace Heap { + +struct MapCtor : FunctionObject { + void init(QV4::ExecutionContext *scope); +}; + +struct MapObject : Object { + static void markObjects(Heap::Base *that, MarkStack *markStack); + void init(); + void destroy(); + ESTable *esTable; +}; + +} + +struct MapCtor: FunctionObject +{ + V4_OBJECT2(MapCtor, FunctionObject) + + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct MapObject : Object +{ + V4_OBJECT2(MapObject, Object) + V4_PROTOTYPE(mapPrototype) + V4_NEEDS_DESTROY +}; + +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..e176235786 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(); @@ -209,7 +399,7 @@ ReturnedValue MathObject::method_max(const FunctionObject *, const Value *, cons if (x > mx || std::isnan(x)) mx = x; } - RETURN_RESULT(Encode(mx)); + RETURN_RESULT(Encode::smallestNumber(mx)); } ReturnedValue MathObject::method_min(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -222,7 +412,7 @@ ReturnedValue MathObject::method_min(const FunctionObject *, const Value *, cons mx = x; } } - RETURN_RESULT(Encode(mx)); + RETURN_RESULT(Encode::smallestNumber(mx)); } ReturnedValue MathObject::method_pow(const FunctionObject *, const Value *, const Value *argv, int argc) @@ -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/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index f58ff45801..d103f65d47 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -78,13 +78,13 @@ void Heap::NumberCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Number")); } -ReturnedValue NumberCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue NumberCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { double dbl = argc ? argv[0].toNumber() : 0.; return Encode(f->engine()->newNumberObject(dbl)); } -ReturnedValue NumberCtor::call(const FunctionObject *, const Value *, const Value *argv, int argc) +ReturnedValue NumberCtor::virtualCall(const FunctionObject *, const Value *, const Value *argv, int argc) { double dbl = argc ? argv[0].toNumber() : 0.; return Encode(dbl); @@ -95,7 +95,7 @@ void NumberPrototype::init(ExecutionEngine *engine, Object *ctor) Scope scope(engine); ScopedObject o(scope); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(qt_qnan())); ctor->defineReadonlyProperty(QStringLiteral("NEGATIVE_INFINITY"), Primitive::fromDouble(-qInf())); diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h index cfdcf9bc1d..576817cf36 100644 --- a/src/qml/jsruntime/qv4numberobject_p.h +++ b/src/qml/jsruntime/qv4numberobject_p.h @@ -79,8 +79,8 @@ struct NumberCtor: FunctionObject { V4_OBJECT2(NumberCtor, 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 virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; struct NumberPrototype: NumberObject diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 0c6cde84ad..e4d670d4f3 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,20 +90,7 @@ void Object::setProperty(uint index, const Property *p) void Heap::Object::setUsedAsProto() { - internalClass = internalClass->asProtoClass(); -} - -bool Object::setPrototype(Object *proto) -{ - Heap::Object *p = proto ? proto->d() : nullptr; - Heap::Object *pp = p; - while (pp) { - if (pp == d()) - return false; - pp = pp->prototype(); - } - setInternalClass(internalClass()->changePrototype(p)); - return true; + internalClass.set(internalClass->engine, internalClass->asProtoClass()); } ReturnedValue Object::getValue(const Value &thisObject, const Value &v, PropertyAttributes attrs) @@ -121,7 +109,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 +136,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, VTable::Call 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, VTable::Call 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, VTable::Call getter, VTable::Call setter) { ExecutionEngine *e = engine(); Scope scope(e); @@ -186,16 +171,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, VTable::Call getter, VTable::Call 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 +217,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 +248,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); + PropertyKey key = s->toPropertyKey(); + Heap::InternalClass::addMember(this, key, attributes, &idx); if (attributes.isAccessor()) { setProperty(idx + GetterOffset, p->value); @@ -255,230 +262,81 @@ 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::setPrototypeUnchecked(const Object *p) { - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return getOwnProperty(idx, attrs, p); - - name->makeIdentifier(); - Identifier *id = name->identifier(); - - uint member = internalClass()->find(id); - if (member < UINT_MAX) { - *attrs = internalClass()->propertyData[member]; - if (p) { - p->value = *propertyData(member); - if (attrs->isAccessor()) - p->set = *propertyData(member + SetterOffset); - } - return; - } - - if (attrs) - *attrs = Attr_Invalid; - return; -} - -void Object::getOwnProperty(uint index, PropertyAttributes *attrs, Property *p) -{ - if (arrayData()) { - if (arrayData()->getProperty(index, p, attrs)) - return; - } - if (isStringObject()) { - *attrs = Attr_NotConfigurable|Attr_NotWritable; - if (p) - p->value = static_cast<StringObject *>(this)->getIndex(index); - return; - } - - if (attrs) - *attrs = Attr_Invalid; - return; + setInternalClass(internalClass()->changePrototype(p ? p->d() : nullptr)); } // Section 8.12.2 -MemberData::Index Object::getValueOrSetter(String *name, PropertyAttributes *attrs) -{ - Q_ASSERT(name->asArrayIndex() == UINT_MAX); - - name->makeIdentifier(); - Identifier *id = name->identifier(); - - Heap::Object *o = d(); - while (o) { - uint idx = o->internalClass->find(id); - if (idx < UINT_MAX) { - *attrs = o->internalClass->propertyData[idx]; - return o->writablePropertyData(attrs->isAccessor() ? idx + SetterOffset : idx ); - } - - o = o->prototype(); - } - *attrs = Attr_Invalid; - return { nullptr, nullptr }; -} - -ArrayData::Index Object::getValueOrSetter(uint index, PropertyAttributes *attrs) +PropertyIndex Object::getValueOrSetter(PropertyKey id, PropertyAttributes *attrs) { - Heap::Object *o = d(); - while (o) { - if (o->arrayData) { - uint idx = o->arrayData->mappedIndex(index); - if (idx != UINT_MAX) { - *attrs = o->arrayData->attributes(index); - return { o->arrayData , attrs->isAccessor() ? idx + SetterOffset : idx }; + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + Heap::Object *o = d(); + while (o) { + if (o->arrayData) { + uint idx = o->arrayData->mappedIndex(index); + if (idx != UINT_MAX) { + *attrs = o->arrayData->attributes(index); + return { o->arrayData , o->arrayData->values.values + (attrs->isAccessor() ? idx + SetterOffset : idx) }; + } } + if (o->vtable()->type == Type_StringObject) { + if (index < static_cast<const Heap::StringObject *>(o)->length()) { + // this is an evil hack, but it works, as the method is only ever called from put, + // where we don't use the returned pointer there for non writable attributes + *attrs = (Attr_NotWritable|Attr_NotConfigurable); + return { reinterpret_cast<Heap::ArrayData *>(0x1), nullptr }; + } + } + o = o->prototype(); } - if (o->vtable()->type == Type_StringObject) { - if (index < static_cast<const Heap::StringObject *>(o)->length()) { - // this is an evil hack, but it works, as the method is only ever called from putIndexed, - // where we don't use the returned pointer there for non writable attributes - *attrs = (Attr_NotWritable|Attr_NotConfigurable); - return { reinterpret_cast<Heap::ArrayData *>(0x1), 0 }; + } else { + Heap::Object *o = d(); + while (o) { + uint idx = o->internalClass->find(id); + if (idx < UINT_MAX) { + *attrs = o->internalClass->propertyData[idx]; + return o->writablePropertyData(attrs->isAccessor() ? idx + SetterOffset : idx ); } + + o = o->prototype(); } - o = o->prototype(); } *attrs = Attr_Invalid; - return { nullptr, 0 }; -} - -bool Object::hasProperty(String *name) const -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return hasProperty(idx); - - Scope scope(engine()); - ScopedObject o(scope, d()); - while (o) { - if (o->hasOwnProperty(name)) - return true; - - o = o->prototype(); - } - - return false; -} - -bool Object::hasProperty(uint index) const -{ - Scope scope(engine()); - ScopedObject o(scope, d()); - while (o) { - if (o->hasOwnProperty(index)) - return true; - - o = o->prototype(); - } - - return false; -} - -bool Object::hasOwnProperty(String *name) const -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return hasOwnProperty(idx); - - name->makeIdentifier(); - Identifier *id = name->identifier(); - - if (internalClass()->find(id) < UINT_MAX) - return true; - if (!query(name).isEmpty()) - return true; - return false; -} - -bool Object::hasOwnProperty(uint index) const -{ - if (arrayData() && !arrayData()->isEmpty(index)) - return true; - - if (isStringObject()) { - if (index < static_cast<const StringObject *>(this)->length()) - return true; - } - if (!queryIndexed(index).isEmpty()) - return true; - return false; + return { nullptr, nullptr }; } -ReturnedValue Object::callAsConstructor(const FunctionObject *f, const Value *, int) +ReturnedValue Object::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) { return f->engine()->throwTypeError(); } -ReturnedValue Object::call(const FunctionObject *f, const Value *, const Value *, int) +ReturnedValue Object::virtualCall(const FunctionObject *f, const Value *, const Value *, int) { return f->engine()->throwTypeError(); } -ReturnedValue Object::get(const Managed *m, String *name, bool *hasProperty) +ReturnedValue Object::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { - return static_cast<const Object *>(m)->internalGet(name, hasProperty); + if (id.isArrayIndex()) + return static_cast<const Object *>(m)->internalGetIndexed(id.asArrayIndex(), receiver, hasProperty); + Scope scope(m); + Scoped<StringOrSymbol> name(scope, id.asStringOrSymbol()); + return static_cast<const Object *>(m)->internalGet(name, receiver, hasProperty); } -ReturnedValue Object::getIndexed(const Managed *m, uint index, bool *hasProperty) +bool Object::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { - return static_cast<const Object *>(m)->internalGetIndexed(index, hasProperty); + return static_cast<Object *>(m)->internalPut(id, value, receiver); } -bool Object::put(Managed *m, String *name, const Value &value) +bool Object::virtualDeleteProperty(Managed *m, PropertyKey id) { - return static_cast<Object *>(m)->internalPut(name, value); + return static_cast<Object *>(m)->internalDeleteProperty(id); } -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) -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return queryIndexed(m, idx); - - name->makeIdentifier(); - Identifier *id = name->identifier(); - - const Object *o = static_cast<const Object *>(m); - idx = o->internalClass()->find(id); - if (idx < UINT_MAX) - return o->internalClass()->propertyData[idx]; - - return Attr_Invalid; -} - -PropertyAttributes Object::queryIndexed(const Managed *m, uint index) -{ - const Object *o = static_cast<const Object *>(m); - if (o->arrayData() && !o->arrayData()->isEmpty(index)) - return o->arrayData()->attributes(index); - - if (o->isStringObject()) { - if (index < static_cast<const StringObject *>(o)->length()) - return (Attr_NotWritable|Attr_NotConfigurable); - } - return Attr_Invalid; -} - -bool Object::deleteProperty(Managed *m, String *name) -{ - return static_cast<Object *>(m)->internalDeleteProperty(name); -} - -bool Object::deleteIndexedProperty(Managed *m, uint index) -{ - return static_cast<Object *>(m)->internalDeleteIndexedProperty(index); -} - -void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *pd, PropertyAttributes *attrs) +void Object::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *pd, PropertyAttributes *attrs) { Object *o = static_cast<Object *>(m); name->setM(nullptr); @@ -525,9 +383,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) { + PropertyKey n = o->internalClass()->nameMap.at(it->memberIndex); + if (!n.isStringOrSymbol() || !n.asStringOrSymbol()->internalClass->vtable->isString) { // accessor properties have a dummy entry with n == 0 + // symbol entries are supposed to be skipped ++it->memberIndex; continue; } @@ -536,7 +395,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.asStringOrSymbol()); *attrs = a; pd->value = *o->propertyData(idx); if (a.isAccessor()) @@ -549,14 +408,11 @@ 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, const Value *receiver, bool *hasProperty) const { - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return getIndexed(idx, hasProperty); + PropertyKey id = name->toPropertyKey(); - name->makeIdentifier(); - Identifier *id = name->identifier(); + Q_ASSERT(!id.isArrayIndex()); Heap::Object *o = d(); while (o) { @@ -564,7 +420,7 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) const if (idx < UINT_MAX) { if (hasProperty) *hasProperty = true; - return getValue(*o->propertyData(idx), o->internalClass->propertyData.at(idx)); + return Object::getValue(*receiver, *o->propertyData(idx), o->internalClass->propertyData.at(idx)); } o = o->prototype(); @@ -575,7 +431,7 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) const return Encode::undefined(); } -ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const +ReturnedValue Object::internalGetIndexed(uint index, const Value *receiver, bool *hasProperty) const { PropertyAttributes attrs; Scope scope(engine()); @@ -596,13 +452,13 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const return str.asReturnedValue(); } } - o = o->prototype(); + o = o->getPrototypeOf(); } if (exists) { if (hasProperty) *hasProperty = true; - return getValue(pd->value, attrs); + return Object::getValue(*receiver, pd->value, attrs); } if (hasProperty) @@ -612,36 +468,44 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const // Section 8.12.5 -bool Object::internalPut(String *name, const Value &value) +bool Object::internalPut(PropertyKey id, const Value &value, Value *receiver) { ExecutionEngine *engine = this->engine(); if (engine->hasException) return false; - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return putIndexed(idx, value); - - name->makeIdentifier(); - Identifier *id = name->identifier(); + uint index = id.asArrayIndex(); + Scope scope(engine); - MemberData::Index memberIndex{nullptr, nullptr}; - uint member = internalClass()->find(id); PropertyAttributes attrs; - if (member < UINT_MAX) { - attrs = internalClass()->propertyData[member]; - memberIndex = d()->writablePropertyData(attrs.isAccessor() ? member + SetterOffset : member); + PropertyIndex propertyIndex{nullptr, nullptr}; + + if (index != UINT_MAX) { + if (arrayData()) + propertyIndex = arrayData()->getValueOrSetter(index, &attrs); + + if (propertyIndex.isNull() && isStringObject()) { + if (index < static_cast<StringObject *>(this)->length()) + // not writable + return false; + } + } else { + uint member = internalClass()->find(id); + if (member < UINT_MAX) { + attrs = internalClass()->propertyData[member]; + propertyIndex = d()->writablePropertyData(attrs.isAccessor() ? member + SetterOffset : member); + } } // clause 1 - if (!memberIndex.isNull()) { + if (!propertyIndex.isNull()) { if (attrs.isAccessor()) { - if (memberIndex->as<FunctionObject>()) + if (propertyIndex->as<FunctionObject>()) goto cont; return false; } else if (!attrs.isWritable()) return false; - else if (isArrayObject() && name->equals(engine->id_length())) { + else if (isArrayObject() && id == engine->id_length()->propertyKey()) { bool ok; uint l = value.asArrayLength(&ok); if (!ok) { @@ -652,19 +516,18 @@ bool Object::internalPut(String *name, const Value &value) if (!ok) return false; } else { - memberIndex.set(engine, value); + propertyIndex.set(engine, value); } return true; - } else if (!prototype()) { + } else if (!getPrototypeOf()) { if (!isExtensible()) return false; } else { // clause 4 - Scope scope(engine); - memberIndex = ScopedObject(scope, prototype())->getValueOrSetter(name, &attrs); - if (!memberIndex.isNull()) { + propertyIndex = ScopedObject(scope, getPrototypeOf())->getValueOrSetter(id, &attrs); + if (!propertyIndex.isNull()) { if (attrs.isAccessor()) { - if (!memberIndex->as<FunctionObject>()) + if (!propertyIndex->as<FunctionObject>()) return false; } else if (!isExtensible() || !attrs.isWritable()) { return false; @@ -677,228 +540,59 @@ bool Object::internalPut(String *name, const Value &value) cont: // Clause 5 - if (!memberIndex.isNull() && attrs.isAccessor()) { - Q_ASSERT(memberIndex->as<FunctionObject>()); + if (!propertyIndex.isNull() && attrs.isAccessor()) { + Q_ASSERT(propertyIndex->as<FunctionObject>()); Scope scope(engine); - ScopedFunctionObject setter(scope, *memberIndex); + ScopedFunctionObject setter(scope, *propertyIndex); JSCallData jsCallData(scope, 1); jsCallData->args[0] = value; - *jsCallData->thisObject = this; + *jsCallData->thisObject = *receiver; setter->call(jsCallData); return !engine->hasException; } - insertMember(name, value); - return true; -} - -bool Object::internalPutIndexed(uint index, const Value &value) -{ - ExecutionEngine *engine = this->engine(); - if (engine->hasException) - return false; - - PropertyAttributes attrs; - - ArrayData::Index arrayIndex = arrayData() ? arrayData()->getValueOrSetter(index, &attrs) : ArrayData::Index{ nullptr, 0 }; - - if (arrayIndex.isNull() && isStringObject()) { - if (index < static_cast<StringObject *>(this)->length()) - // not writable - return false; - } - - // clause 1 - if (!arrayIndex.isNull()) { - if (attrs.isAccessor()) { - if (arrayIndex->as<FunctionObject>()) - goto cont; - return false; - } else if (!attrs.isWritable()) - return false; - - arrayIndex.set(engine, value); - return true; - } else if (!prototype()) { - if (!isExtensible()) - return false; + if (index != UINT_MAX) { + arraySet(index, value); } else { - // clause 4 - Scope scope(engine); - arrayIndex = ScopedObject(scope, prototype())->getValueOrSetter(index, &attrs); - if (!arrayIndex.isNull()) { - if (attrs.isAccessor()) { - if (!arrayIndex->as<FunctionObject>()) - return false; - } else if (!isExtensible() || !attrs.isWritable()) { - return false; - } - } else if (!isExtensible()) { - return false; - } - } - - cont: - - // Clause 5 - if (!arrayIndex.isNull() && attrs.isAccessor()) { - Q_ASSERT(arrayIndex->as<FunctionObject>()); - - Scope scope(engine); - ScopedFunctionObject setter(scope, *arrayIndex); - JSCallData jsCallData(scope, 1); - jsCallData->args[0] = value; - *jsCallData->thisObject = this; - setter->call(jsCallData); - return !engine->hasException; + Scoped<StringOrSymbol> name(scope, id.asStringOrSymbol()); + insertMember(name, value); } - - arraySet(index, value); return true; } // Section 8.12.7 -bool Object::internalDeleteProperty(String *name) +bool Object::internalDeleteProperty(PropertyKey id) { if (internalClass()->engine->hasException) return false; - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return deleteIndexedProperty(idx); - - name->makeIdentifier(); + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + Scope scope(engine()); + if (scope.engine->hasException) + return false; - uint memberIdx = internalClass()->find(name->identifier()); - if (memberIdx != UINT_MAX) { - if (internalClass()->propertyData[memberIdx].isConfigurable()) { - InternalClass::removeMember(this, name->identifier()); + Scoped<ArrayData> ad(scope, arrayData()); + if (!ad || ad->vtable()->del(this, index)) return true; - } - return false; - } - return true; -} - -bool Object::internalDeleteIndexedProperty(uint index) -{ - Scope scope(engine()); - if (scope.engine->hasException) return false; + } - Scoped<ArrayData> ad(scope, arrayData()); - if (!ad || ad->vtable()->del(this, index)) - return true; - - return false; -} - -// Section 8.12.9 -bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const Property *p, PropertyAttributes attrs) -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return __defineOwnProperty__(engine, idx, p, attrs); - - Scope scope(engine); - name->makeIdentifier(); - - uint memberIndex; - - if (isArrayObject() && name->equals(engine->id_length())) { - Q_ASSERT(Heap::ArrayObject::LengthPropertyIndex == internalClass()->find(engine->id_length())); - ScopedProperty lp(scope); - PropertyAttributes cattrs; - getProperty(Heap::ArrayObject::LengthPropertyIndex, lp, &cattrs); - if (attrs.isEmpty() || p->isSubset(attrs, lp, cattrs)) + uint memberIdx = internalClass()->find(id); + if (memberIdx != UINT_MAX) { + if (internalClass()->propertyData[memberIdx].isConfigurable()) { + Heap::InternalClass::removeMember(this, id); return true; - if (!cattrs.isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable()) - return false; - bool succeeded = true; - if (attrs.type() == PropertyAttributes::Data) { - bool ok; - uint l = p->value.asArrayLength(&ok); - if (!ok) { - ScopedValue v(scope, p->value); - engine->throwRangeError(v); - return false; - } - succeeded = setArrayLength(l); } - if (attrs.hasWritable() && !attrs.isWritable()) { - cattrs.setWritable(false); - InternalClass::changeMember(this, engine->id_length(), cattrs); - } - if (!succeeded) - return false; - return true; - } - - // Clause 1 - memberIndex = internalClass()->find(name->identifier()); - - if (memberIndex == UINT_MAX) { - // clause 3 - if (!isExtensible()) - return false; - // clause 4 - ScopedProperty pd(scope); - pd->copy(p, attrs); - pd->fullyPopulated(&attrs); - insertMember(name, pd, attrs); - return true; - } - - return __defineOwnProperty__(engine, memberIndex, name, p, attrs); -} - -bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs) -{ - // 15.4.5.1, 4b - if (isArrayObject() && index >= getLength() && !internalClass()->propertyData[Heap::ArrayObject::LengthPropertyIndex].isWritable()) return false; - - if (ArgumentsObject::isNonStrictArgumentsObject(this)) - return static_cast<ArgumentsObject *>(this)->defineOwnProperty(engine, index, p, attrs); - - return defineOwnProperty2(engine, index, p, attrs); -} - -bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs) -{ - bool hasProperty = 0; - - // Clause 1 - if (arrayData()) { - hasProperty = arrayData()->mappedIndex(index) != UINT_MAX; - if (!hasProperty && isStringObject()) - hasProperty = (index < static_cast<StringObject *>(this)->length()); } - if (!hasProperty) { - // clause 3 - if (!isExtensible()) - return false; - // clause 4 - Scope scope(engine); - ScopedProperty pp(scope); - pp->copy(p, attrs); - pp->fullyPopulated(&attrs); - if (attrs == Attr_Data) { - ScopedValue v(scope, pp->value); - arraySet(index, v); - } else { - arraySet(index, pp, attrs); - } - return true; - } - - return __defineOwnProperty__(engine, index, nullptr, p, attrs); + return true; } -bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *member, const Property *p, PropertyAttributes attrs) +bool Object::internalDefineOwnProperty(ExecutionEngine *engine, uint index, StringOrSymbol *member, const Property *p, PropertyAttributes attrs) { // clause 5 if (attrs.isEmpty()) @@ -977,7 +671,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->propertyKey(), cattrs); setProperty(index, current); } else { setArrayAttributes(index, cattrs); @@ -986,15 +680,6 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * return true; } - -bool Object::__defineOwnProperty__(ExecutionEngine *engine, const QString &name, const Property *p, PropertyAttributes attrs) -{ - Scope scope(engine); - ScopedString s(scope, engine->newString(name)); - return __defineOwnProperty__(engine, s, p, attrs); -} - - void Object::copyArrayData(Object *other) { Q_ASSERT(isArrayObject()); @@ -1007,7 +692,7 @@ void Object::copyArrayData(Object *other) ScopedValue v(scope); for (uint i = 0; i < len; ++i) { - arraySet(i, (v = other->getIndexed(i))); + arraySet(i, (v = other->get(i))); } } else if (!other->arrayData()) { ; @@ -1030,15 +715,15 @@ void Object::copyArrayData(Object *other) setArrayLengthUnchecked(other->getLength()); } -uint Object::getLength(const Managed *m) +qint64 Object::virtualGetLength(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. -ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var) +ReturnedValue Object::virtualInstanceOf(const Object *typeObject, const Value &var) { QV4::ExecutionEngine *engine = typeObject->internalClass()->engine; @@ -1080,6 +765,141 @@ ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var) return Encode(false); } +bool Object::virtualHasProperty(const Managed *m, PropertyKey id) +{ + Scope scope(m->engine()); + ScopedObject o(scope, m); + ScopedProperty p(scope); + while (o) { + if (o->getOwnProperty(id, p) != Attr_Invalid) + return true; + + o = o->getPrototypeOf(); + } + + return false; +} + +PropertyAttributes Object::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p) +{ + PropertyAttributes attrs; + Object *o = static_cast<Object *>(m); + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + if (o->arrayData()) { + if (o->arrayData()->getProperty(index, p, &attrs)) + return attrs; + } + } else { + Q_ASSERT(id.asStringOrSymbol()); + + uint member = o->internalClass()->find(id); + if (member < UINT_MAX) { + attrs = o->internalClass()->propertyData[member]; + if (p) { + p->value = *o->propertyData(member); + if (attrs.isAccessor()) + p->set = *o->propertyData(member + SetterOffset); + } + return attrs; + } + } + + return Attr_Invalid; +} + +bool Object::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs) +{ + Object *o = static_cast<Object *>(m); + Scope scope(o); + + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + + bool hasProperty = false; + + if (o->arrayData()) { + hasProperty = o->arrayData()->mappedIndex(index) != UINT_MAX; + if (!hasProperty && o->isStringObject()) + hasProperty = (index < static_cast<StringObject *>(o)->length()); + } + + if (!hasProperty) { + if (!o->isExtensible()) + return false; + + ScopedProperty pp(scope); + pp->copy(p, attrs); + pp->fullyPopulated(&attrs); + if (attrs == Attr_Data) { + ScopedValue v(scope, pp->value); + o->arraySet(index, v); + } else { + o->arraySet(index, pp, attrs); + } + return true; + } + + return o->internalDefineOwnProperty(scope.engine, index, nullptr, p, attrs); + } + + uint memberIndex = o->internalClass()->find(id); + Scoped<StringOrSymbol> name(scope, id.asStringOrSymbol()); + + if (memberIndex == UINT_MAX) { + if (!o->isExtensible()) + return false; + + ScopedProperty pd(scope); + pd->copy(p, attrs); + pd->fullyPopulated(&attrs); + o->insertMember(name, pd, attrs); + return true; + } + + return o->internalDefineOwnProperty(scope.engine, memberIndex, name, p, attrs); +} + +bool Object::virtualIsExtensible(const Managed *m) +{ + return m->d()->internalClass->extensible; +} + +bool Object::virtualPreventExtensions(Managed *m) +{ + Q_ASSERT(m->isObject()); + Object *o = static_cast<Object *>(m); + o->setInternalClass(o->internalClass()->nonExtensible()); + return true; +} + +Heap::Object *Object::virtualGetPrototypeOf(const Managed *m) +{ + return m->internalClass()->prototype; +} + +bool Object::virtualSetPrototypeOf(Managed *m, const Object *proto) +{ + Q_ASSERT(m->isObject()); + Object *o = static_cast<Object *>(m); + Heap::Object *current = o->internalClass()->prototype; + Heap::Object *protod = proto ? proto->d() : nullptr; + if (current == protod) + return true; + if (!o->internalClass()->extensible) + return false; + Heap::Object *p = protod; + while (p) { + if (p == o->d()) + return false; + if (p->vtable()->getPrototypeOf != Object::staticVTable()->getPrototypeOf) + break; + p = p->prototype(); + } + o->setInternalClass(o->internalClass()->changePrototype(protod)); + return true; +} + bool Object::setArrayLength(uint newLen) { Q_ASSERT(isArrayObject()); @@ -1132,12 +952,10 @@ void Heap::ArrayObject::init(const QStringList &list) a->setArrayLengthUnchecked(len); } -uint ArrayObject::getLength(const Managed *m) +qint64 ArrayObject::virtualGetLength(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 @@ -1150,8 +968,62 @@ QStringList ArrayObject::toQStringList() const uint length = getLength(); for (uint i = 0; i < length; ++i) { - v = const_cast<ArrayObject *>(this)->getIndexed(i); + v = const_cast<ArrayObject *>(this)->get(i); result.append(v->toQStringNoThrow()); } return result; } + +bool ArrayObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs) +{ + Q_ASSERT(m->isArrayObject()); + ArrayObject *a = static_cast<ArrayObject *>(m); + + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + uint len = a->getLength(); + if (index >= len && !a->internalClass()->propertyData[Heap::ArrayObject::LengthPropertyIndex].isWritable()) + return false; + + bool succeeded = Object::virtualDefineOwnProperty(m, id, p, attrs); + if (!succeeded) + return false; + + if (index >= len) + a->setArrayLengthUnchecked(index + 1); + + return true; + } + + ExecutionEngine *engine = m->engine(); + if (id == engine->id_length()->propertyKey()) { + Scope scope(engine); + Q_ASSERT(Heap::ArrayObject::LengthPropertyIndex == a->internalClass()->find(engine->id_length()->propertyKey())); + ScopedProperty lp(scope); + PropertyAttributes cattrs; + a->getProperty(Heap::ArrayObject::LengthPropertyIndex, lp, &cattrs); + if (attrs.isEmpty() || p->isSubset(attrs, lp, cattrs)) + return true; + if (!cattrs.isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable()) + return false; + bool succeeded = true; + if (attrs.type() == PropertyAttributes::Data) { + bool ok; + uint l = p->value.asArrayLength(&ok); + if (!ok) { + ScopedValue v(scope, p->value); + engine->throwRangeError(v); + return false; + } + succeeded = a->setArrayLength(l); + } + if (attrs.hasWritable() && !attrs.isWritable()) { + cattrs.setWritable(false); + Heap::InternalClass::changeMember(a, engine->id_length()->propertyKey(), cattrs); + } + if (!succeeded) + return false; + return true; + } + return Object::virtualDefineOwnProperty(m, id, p, attrs); +} diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 1731ae3c76..dea080f98a 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -74,6 +74,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 +94,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 { @@ -134,76 +138,6 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) { } -#define V4_OBJECT2(DataClass, superClass) \ - private: \ - DataClass() Q_DECL_EQ_DELETE; \ - Q_DISABLE_COPY(DataClass) \ - public: \ - Q_MANAGED_CHECK \ - typedef QV4::Heap::DataClass Data; \ - typedef superClass SuperClass; \ - static const QV4::ObjectVTable static_vtbl; \ - static inline const QV4::VTable *staticVTable() { return &static_vtbl.vTable; } \ - V4_MANAGED_SIZE_TEST \ - QV4::Heap::DataClass *d_unchecked() const { return static_cast<QV4::Heap::DataClass *>(m()); } \ - QV4::Heap::DataClass *d() const { \ - QV4::Heap::DataClass *dptr = d_unchecked(); \ - dptr->_checkIsInitialized(); \ - return dptr; \ - } \ - Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value); - -#define V4_PROTOTYPE(p) \ - static QV4::Object *defaultPrototype(QV4::ExecutionEngine *e) \ - { return e->p(); } - -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 (*getIndexed)(const Managed *, uint index, bool *hasProperty); - bool (*put)(Managed *, String *name, const Value &value); - bool (*putIndexed)(Managed *, uint index, const Value &value); - PropertyAttributes (*query)(const Managed *, String *name); - PropertyAttributes (*queryIndexed)(const Managed *, uint index); - bool (*deleteProperty)(Managed *m, String *name); - bool (*deleteIndexedProperty)(Managed *m, uint index); - uint (*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); -}; - -#define DEFINE_OBJECT_VTABLE_BASE(classname) \ -const QV4::ObjectVTable classname::static_vtbl = \ -{ \ - DEFINE_MANAGED_VTABLE_INT(classname, (std::is_same<classname::SuperClass, Object>::value) ? nullptr : &classname::SuperClass::static_vtbl.vTable), \ - call, \ - callAsConstructor, \ - get, \ - getIndexed, \ - put, \ - putIndexed, \ - query, \ - queryIndexed, \ - deleteProperty, \ - deleteIndexedProperty, \ - getLength, \ - advanceIterator, \ - instanceOf \ -} - -#define DEFINE_OBJECT_VTABLE(classname) \ -QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \ -DEFINE_OBJECT_VTABLE_BASE(classname) \ -QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF - -#define DEFINE_OBJECT_TEMPLATE_VTABLE(classname) \ -QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \ -template<> DEFINE_OBJECT_VTABLE_BASE(classname) \ -QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF - struct Q_QML_EXPORT Object: Managed { V4_OBJECT2(Object, Object) Q_MANAGED_TYPE(Object) @@ -218,7 +152,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); } @@ -232,27 +166,21 @@ struct Q_QML_EXPORT Object: Managed { void setProperty(ExecutionEngine *engine, uint index, Value v) const { d()->setProperty(engine, index, v); } void setProperty(ExecutionEngine *engine, uint index, Heap::Base *b) const { d()->setProperty(engine, index, b); } - const ObjectVTable *vtable() const { return reinterpret_cast<const ObjectVTable *>(d()->vtable()); } - Heap::Object *prototype() const { return d()->prototype(); } - bool setPrototype(Object *proto); + const VTable *vtable() const { return d()->vtable(); } - void getOwnProperty(String *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); + PropertyAttributes getOwnProperty(PropertyKey id, Property *p = nullptr) { + return vtable()->getOwnProperty(this, id, p); + } - bool hasProperty(String *name) const; - bool hasProperty(uint index) const; + PropertyIndex getValueOrSetter(PropertyKey id, PropertyAttributes *attrs); - bool hasOwnProperty(String *name) const; - bool hasOwnProperty(uint index) const; + bool hasProperty(PropertyKey id) const { + return vtable()->hasProperty(this, id); + } - 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, 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); + bool defineOwnProperty(PropertyKey id, const Property *p, PropertyAttributes attrs) { + return vtable()->defineOwnProperty(this, id, p, attrs); + } // // helpers @@ -267,33 +195,39 @@ 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, VTable::Call code, + int argumentCount = 0, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable); + void defineDefaultProperty(StringOrSymbol *name, VTable::Call code, + int argumentCount = 0, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable); + void defineAccessorProperty(const QString &name, VTable::Call getter, VTable::Call setter); + void defineAccessorProperty(StringOrSymbol *name, VTable::Call getter, VTable::Call 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; } + bool isExtensible() const { return vtable()->isExtensible(this); } + bool preventExtensions() { return vtable()->preventExtensions(this); } + Heap::Object *getPrototypeOf() const { return vtable()->getPrototypeOf(this); } + bool setPrototypeOf(const Object *p) { return vtable()->setPrototypeOf(this, p); } + void setPrototypeUnchecked(const Object *p); // Array handling @@ -353,33 +287,58 @@ public: Scope scope(engine()); ScopedObject p(scope, this); - while ((p = p->prototype())) + while ((p = p->getPrototypeOf())) if (p->arrayData()) return true; return false; } - inline ReturnedValue get(String *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); } + inline ReturnedValue get(StringOrSymbol *name, bool *hasProperty = nullptr, const Value *receiver = nullptr) const + { if (!receiver) receiver = this; return vtable()->get(this, name->toPropertyKey(), receiver, hasProperty); } + inline ReturnedValue get(uint idx, bool *hasProperty = nullptr, const Value *receiver = nullptr) const + { if (!receiver) receiver = this; return vtable()->get(this, PropertyKey::fromArrayIndex(idx), receiver, hasProperty); } + QT_DEPRECATED inline ReturnedValue getIndexed(uint idx, bool *hasProperty = nullptr) const + { return get(idx, hasProperty); } + inline ReturnedValue get(PropertyKey id, const Value *receiver = nullptr, bool *hasProperty = nullptr) const + { if (!receiver) receiver = this; return vtable()->get(this, id, receiver, hasProperty); } // use the set variants instead, to customize throw behavior - inline bool put(String *name, const Value &v) - { return vtable()->put(this, name, v); } - inline bool putIndexed(uint idx, const Value &v) - { return vtable()->putIndexed(this, idx, v); } + inline bool put(StringOrSymbol *name, const Value &v, Value *receiver = nullptr) + { if (!receiver) receiver = this; return vtable()->put(this, name->toPropertyKey(), v, receiver); } + inline bool put(uint idx, const Value &v, Value *receiver = nullptr) + { if (!receiver) receiver = this; return vtable()->put(this, PropertyKey::fromArrayIndex(idx), v, receiver); } + QT_DEPRECATED inline bool putIndexed(uint idx, const Value &v) + { return put(idx, v); } + inline bool put(PropertyKey id, const Value &v, Value *receiver = nullptr) + { if (!receiver) receiver = this; return vtable()->put(this, id, v, receiver); } enum ThrowOnFailure { DoThrowOnRejection, 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()->put(this, PropertyKey::fromArrayIndex(idx), v, this); + // 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); + bool ret = vtable()->put(this, name->toPropertyKey(), v, this); // 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(); @@ -392,42 +351,37 @@ public: return ret; } - PropertyAttributes query(String *name) const - { return vtable()->query(this, name); } - PropertyAttributes queryIndexed(uint index) const - { return vtable()->queryIndexed(this, index); } - bool deleteProperty(String *name) - { return vtable()->deleteProperty(this, name); } - bool deleteIndexedProperty(uint index) - { return vtable()->deleteIndexedProperty(this, index); } + bool deleteProperty(PropertyKey id) + { return vtable()->deleteProperty(this, id); } 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 getIndexed(const Managed *m, uint index, bool *hasProperty); - static bool put(Managed *m, String *name, const Value &value); - static bool putIndexed(Managed *m, uint index, const Value &value); - static PropertyAttributes query(const Managed *m, String *name); - static PropertyAttributes queryIndexed(const Managed *m, uint index); - static bool deleteProperty(Managed *m, String *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 ReturnedValue instanceOf(const Object *typeObject, const Value &var); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver,bool *hasProperty); + static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); + static bool virtualDeleteProperty(Managed *m, PropertyKey id); + static bool virtualHasProperty(const Managed *m, PropertyKey id); + static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p); + static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs); + static bool virtualIsExtensible(const Managed *m); + static bool virtualPreventExtensions(Managed *); + static Heap::Object *virtualGetPrototypeOf(const Managed *); + static bool virtualSetPrototypeOf(Managed *, const Object *); + static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); + static qint64 virtualGetLength(const Managed *m); + static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var); private: - ReturnedValue internalGet(String *name, bool *hasProperty) const; - ReturnedValue internalGetIndexed(uint index, bool *hasProperty) const; - bool internalPut(String *name, const Value &value); - bool internalPutIndexed(uint index, const Value &value); - bool internalDeleteProperty(String *name); - bool internalDeleteIndexedProperty(uint index); + bool internalDefineOwnProperty(ExecutionEngine *engine, uint index, StringOrSymbol *member, const Property *p, PropertyAttributes attrs); + ReturnedValue internalGet(StringOrSymbol *name, const Value *receiver, bool *hasProperty) const; + ReturnedValue internalGetIndexed(uint index, const Value *receiver, bool *hasProperty) const; + bool internalPut(PropertyKey id, const Value &value, Value *receiver); + bool internalDeleteProperty(PropertyKey id); friend struct ObjectIterator; friend struct ObjectPrototype; @@ -499,10 +453,12 @@ struct ArrayObject: Object { void init(ExecutionEngine *engine); - using Object::getLength; - static uint getLength(const Managed *m); + static qint64 virtualGetLength(const Managed *m); QStringList toQStringList() const; +protected: + static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs); + }; inline void Object::setArrayLengthUnchecked(uint l) @@ -551,7 +507,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..73c09a864a 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); @@ -84,12 +104,12 @@ void ObjectIterator::next(Value *name, uint *index, Property *pd, PropertyAttrib n = *name; bool shadowed = false; while (o->d() != current->heapObject()) { - if ((!!n && o->hasOwnProperty(n)) || - (*index != UINT_MAX && o->hasOwnProperty(*index))) { + PropertyKey id = n ? (n->toPropertyKey()) : PropertyKey::fromArrayIndex(*index); + if (id.isValid() && o->getOwnProperty(id) != Attr_Invalid) { shadowed = true; break; } - o = o->prototype(); + o = o->getPrototypeOf(); } if (shadowed) continue; @@ -98,7 +118,7 @@ void ObjectIterator::next(Value *name, uint *index, Property *pd, PropertyAttrib } if (flags & WithProtoChain) - current->setM(co->prototype()); + current->setM(co->getPrototypeOf()); else current->setM(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..65e9b836d1 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -47,6 +47,8 @@ #include "qv4objectiterator_p.h" #include "qv4string_p.h" #include "qv4jscall_p.h" +#include "qv4symbol_p.h" +#include "qv4propertykey_p.h" #include <QtCore/QDateTime> #include <QtCore/QStringList> @@ -61,23 +63,23 @@ void Heap::ObjectCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Object")); } -ReturnedValue ObjectCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue ObjectCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { ExecutionEngine *v4 = f->engine(); - const ObjectCtor *ctor = static_cast<const ObjectCtor *>(f); + const ObjectCtor *nt = static_cast<const ObjectCtor *>(newTarget); if (!argc || argv[0].isUndefined() || argv[0].isNull()) { Scope scope(v4); ScopedObject obj(scope, scope.engine->newObject()); - ScopedObject proto(scope, ctor->get(scope.engine->id_prototype())); + ScopedObject proto(scope, nt->get(scope.engine->id_prototype())); if (!!proto) - obj->setPrototype(proto); + obj->setPrototypeOf(proto); return obj.asReturnedValue(); } else { return argv[0].toObject(v4)->asReturnedValue(); } } -ReturnedValue ObjectCtor::call(const FunctionObject *m, const Value *, const Value *argv, int argc) +ReturnedValue ObjectCtor::virtualCall(const FunctionObject *m, const Value *, const Value *argv, int argc) { ExecutionEngine *v4 = m->engine(); if (!argc || argv[0].isUndefined() || argv[0].isNull()) { @@ -97,6 +99,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 +107,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 +124,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) @@ -136,10 +137,19 @@ ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, co if (scope.engine->hasException) return QV4::Encode::undefined(); - ScopedObject p(scope, o->prototype()); + ScopedObject p(scope, o->getPrototypeOf()); 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,13 +164,12 @@ 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)); + ScopedPropertyKey name(scope, v->toPropertyKey(scope.engine)); if (scope.engine->hasException) return QV4::Encode::undefined(); - PropertyAttributes attrs; ScopedProperty desc(scope); - O->getOwnProperty(name, &attrs, desc); + PropertyAttributes attrs = O->getOwnProperty(name, desc); return fromPropertyDescriptor(scope.engine, desc, attrs); } @@ -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) { + PropertyKey id = ic->nameMap.at(i); + n = id.asStringOrSymbol(); + 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) { @@ -204,11 +235,10 @@ ReturnedValue ObjectPrototype::method_assign(const FunctionObject *b, const Valu ScopedString nextKey(scope); ScopedValue propValue(scope); for (quint32 i = 0; i < length; ++i) { - nextKey = Value::fromReturnedValue(keys->getIndexed(i)).toString(scope.engine); + nextKey = Value::fromReturnedValue(keys->get(i)).toString(scope.engine); - PropertyAttributes attrs; ScopedProperty prop(scope); - from->getOwnProperty(nextKey, &attrs, prop); + PropertyAttributes attrs = from->getOwnProperty(nextKey->toPropertyKey(), prop); if (attrs == PropertyFlag::Attr_Invalid) continue; @@ -235,7 +265,7 @@ ReturnedValue ObjectPrototype::method_create(const FunctionObject *builtin, cons ScopedObject O(scope, argv[0]); ScopedObject newObject(scope, scope.engine->newObject()); - newObject->setPrototype(O); + newObject->setPrototypeOf(O); if (argc > 1 && !argv[1].isUndefined()) { @@ -255,7 +285,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); + ScopedPropertyKey name(scope, (argc > 1 ? argv[1] : Primitive::undefinedValue()).toPropertyKey(scope.engine)); if (scope.engine->hasException) return QV4::Encode::undefined(); @@ -266,7 +296,7 @@ ReturnedValue ObjectPrototype::method_defineProperty(const FunctionObject *b, co if (scope.engine->hasException) return QV4::Encode::undefined(); - if (!O->__defineOwnProperty__(scope.engine, name, pd, attrs)) + if (!O->defineOwnProperty(name, pd, attrs)) THROW_TYPE_ERROR(); return O.asReturnedValue(); @@ -287,7 +317,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) { @@ -303,9 +333,9 @@ ReturnedValue ObjectPrototype::method_defineProperties(const FunctionObject *b, return QV4::Encode::undefined(); bool ok; if (name) - ok = O->__defineOwnProperty__(scope.engine, name, n, nattrs); + ok = O->defineOwnProperty(name->toPropertyKey(), n, nattrs); else - ok = O->__defineOwnProperty__(scope.engine, index, n, nattrs); + ok = O->defineOwnProperty(PropertyKey::fromArrayIndex(index), n, nattrs); if (!ok) THROW_TYPE_ERROR(); } @@ -372,7 +402,7 @@ ReturnedValue ObjectPrototype::method_preventExtensions(const FunctionObject *b, if (!o) return argv[0].asReturnedValue(); - o->setInternalClass(o->internalClass()->nonExtensible()); + o->preventExtensions(); return o.asReturnedValue(); } @@ -477,19 +507,53 @@ 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]); + const Object *p = argv[1].isNull() ? nullptr : static_cast<const Object *>(argv + 1); + bool ok = o->setPrototypeOf(p); + if (!ok) + return scope.engine->throwTypeError(QStringLiteral("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,15 +578,13 @@ 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); + ScopedPropertyKey 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)); if (scope.engine->hasException) return QV4::Encode::undefined(); - bool r = O->hasOwnProperty(P); - if (!r) - r = !O->query(P).isEmpty(); + bool r = O->getOwnProperty(P) != Attr_Invalid; return Encode(r); } @@ -536,11 +598,11 @@ ReturnedValue ObjectPrototype::method_isPrototypeOf(const FunctionObject *b, con ScopedObject O(scope, thisObject->toObject(scope.engine)); if (scope.engine->hasException) return QV4::Encode::undefined(); - ScopedObject proto(scope, V->prototype()); + ScopedObject proto(scope, V->getPrototypeOf()); while (proto) { if (O->d() == proto->d()) return Encode(true); - proto = proto->prototype(); + proto = proto->getPrototypeOf(); } return Encode(false); } @@ -548,15 +610,14 @@ 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); + ScopedPropertyKey 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)); if (scope.engine->hasException) return QV4::Encode::undefined(); - PropertyAttributes attrs; - o->getOwnProperty(p, &attrs); + PropertyAttributes attrs = o->getOwnProperty(p); return Encode(attrs.isEnumerable()); } @@ -584,7 +645,7 @@ ReturnedValue ObjectPrototype::method_defineGetter(const FunctionObject *b, cons ScopedProperty pd(scope); pd->value = f; pd->set = Primitive::emptyValue(); - bool ok = o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor); + bool ok = o->defineOwnProperty(prop->toPropertyKey(), pd, Attr_Accessor); if (!ok) THROW_TYPE_ERROR(); RETURN_UNDEFINED(); @@ -614,7 +675,7 @@ ReturnedValue ObjectPrototype::method_defineSetter(const FunctionObject *b, cons ScopedProperty pd(scope); pd->value = Primitive::emptyValue(); pd->set = f; - bool ok = o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor); + bool ok = o->defineOwnProperty(prop->toPropertyKey(), pd, Attr_Accessor); if (!ok) THROW_TYPE_ERROR(); RETURN_UNDEFINED(); @@ -627,32 +688,21 @@ ReturnedValue ObjectPrototype::method_get_proto(const FunctionObject *b, const V if (!o) THROW_TYPE_ERROR(); - return Encode(o->prototype()); + return Encode(o->getPrototypeOf()); } ReturnedValue ObjectPrototype::method_set_proto(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); ScopedObject o(scope, thisObject); - if (!o || !argc) + if (!o || !argc || (!argv[0].isObject() && !argv[0].isNull())) THROW_TYPE_ERROR(); - if (argv[0].isNull()) { - o->setPrototype(nullptr); - RETURN_UNDEFINED(); - } - - ScopedObject p(scope, argv[0]); - bool ok = false; - if (!!p) { - if (o->prototype() == p->d()) { - ok = true; - } else if (o->isExtensible()) { - ok = o->setPrototype(p); - } - } + const Object *p = argv[0].isNull() ? nullptr : static_cast<const Object *>(argv); + bool ok = o->setPrototypeOf(p); if (!ok) - return scope.engine->throwTypeError(QStringLiteral("Cyclic __proto__ value")); + return scope.engine->throwTypeError(QStringLiteral("Could not change prototype.")); + return Encode::undefined(); RETURN_UNDEFINED(); } @@ -670,13 +720,13 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value desc->set = Primitive::emptyValue(); ScopedValue tmp(scope); - if (o->hasProperty(engine->id_enumerable())) + if (o->hasProperty(engine->id_enumerable()->toPropertyKey())) attrs->setEnumerable((tmp = o->get(engine->id_enumerable()))->toBoolean()); - if (o->hasProperty(engine->id_configurable())) + if (o->hasProperty(engine->id_configurable()->toPropertyKey())) attrs->setConfigurable((tmp = o->get(engine->id_configurable()))->toBoolean()); - if (o->hasProperty(engine->id_get())) { + if (o->hasProperty(engine->id_get()->toPropertyKey())) { ScopedValue get(scope, o->get(engine->id_get())); FunctionObject *f = get->as<FunctionObject>(); if (f || get->isUndefined()) { @@ -688,7 +738,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value attrs->setType(PropertyAttributes::Accessor); } - if (o->hasProperty(engine->id_set())) { + if (o->hasProperty(engine->id_set()->toPropertyKey())) { ScopedValue set(scope, o->get(engine->id_set())); FunctionObject *f = set->as<FunctionObject>(); if (f || set->isUndefined()) { @@ -700,7 +750,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value attrs->setType(PropertyAttributes::Accessor); } - if (o->hasProperty(engine->id_writable())) { + if (o->hasProperty(engine->id_writable()->toPropertyKey())) { if (attrs->isAccessor()) { engine->throwTypeError(); return; @@ -710,7 +760,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value desc->value = Primitive::undefinedValue(); } - if (o->hasProperty(engine->id_value())) { + if (o->hasProperty(engine->id_value()->toPropertyKey())) { if (attrs->isAccessor()) { engine->throwTypeError(); return; @@ -734,29 +784,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..0314c05766 100644 --- a/src/qml/jsruntime/qv4objectproto_p.h +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -70,28 +70,31 @@ struct ObjectCtor: FunctionObject { V4_OBJECT2(ObjectCtor, FunctionObject) - static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); - static ReturnedValue call(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc); }; 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/qv4propertykey.cpp b/src/qml/jsruntime/qv4propertykey.cpp new file mode 100644 index 0000000000..e5e96bedb8 --- /dev/null +++ b/src/qml/jsruntime/qv4propertykey.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** 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 "qv4propertykey_p.h" + +#include <QtCore/qstring.h> +#include <qv4string_p.h> + +QV4::Heap::StringOrSymbol *QV4::PropertyKey::toStringOrSymbol(QV4::ExecutionEngine *e) +{ + if (isArrayIndex()) + return Primitive::fromUInt32(asArrayIndex()).toString(e); + return static_cast<Heap::StringOrSymbol *>(asStringOrSymbol()); +} + +bool QV4::PropertyKey::isString() const { + Heap::StringOrSymbol *s = asStringOrSymbol(); + return s && s->internalClass->vtable->isString; +} + +bool QV4::PropertyKey::isSymbol() const { + Heap::Base *s = asStringOrSymbol(); + return s && !s->internalClass->vtable->isString && s->internalClass->vtable->isStringOrSymbol; +} + +QString QV4::PropertyKey::toQString() const +{ + if (isArrayIndex()) + return QString::number(asArrayIndex()); + Heap::Base *b = asStringOrSymbol(); + Q_ASSERT(b->internalClass->vtable->isStringOrSymbol); + Heap::StringOrSymbol *s = static_cast<Heap::StringOrSymbol *>(b); + return s->toQString(); +} diff --git a/src/qml/jsruntime/qv4propertykey_p.h b/src/qml/jsruntime/qv4propertykey_p.h new file mode 100644 index 0000000000..00bf8fb195 --- /dev/null +++ b/src/qml/jsruntime/qv4propertykey_p.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** 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 QV4PROPERTYKEY_H +#define QV4PROPERTYKEY_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qv4global_p.h> + +QT_BEGIN_NAMESPACE + +class QString; + +namespace QV4 { + +struct PropertyKey +{ +private: + // Property keys are Strings, Symbols or unsigned integers. + // For convenience we derive them from Values, allowing us to store them + // on the JS stack + // + // They do however behave somewhat different than a Value: + // * If the key is a String, the pointer to the string is stored in the identifier + // table and thus unique. + // * If the key is a Symbol it simply points to the referenced symbol object + // * if the key is an array index (a uint < UINT_MAX), it's encoded as an + // integer value + quint64 val; + + // Important: Always keep this in sync with the definitions for Integers and heap objects in Value + static const quint64 ArrayIndexMask = 0x3800000000000ull; + enum { + IsManagedOrUndefined_Shift = 64-15, + }; + inline bool isManaged() const { return (val >> IsManagedOrUndefined_Shift) == 0; } + inline quint32 value() const { return val & quint64(~quint32(0)); } + +#if QT_POINTER_SIZE == 8 + QML_NEARLY_ALWAYS_INLINE Heap::StringOrSymbol *m() const + { + Heap::StringOrSymbol *b; + memcpy(&b, &val, 8); + return b; + } + QML_NEARLY_ALWAYS_INLINE void setM(Heap::StringOrSymbol *b) + { + memcpy(&val, &b, 8); + } +#elif QT_POINTER_SIZE == 4 + QML_NEARLY_ALWAYS_INLINE Heap::StringOrSymbol *m() const + { + Q_STATIC_ASSERT(sizeof(Heap::StringOrSymbol*) == sizeof(quint32)); + Heap::StringOrSymbol *b; + quint32 v = value(); + memcpy(&b, &v, 4); + return b; + } + QML_NEARLY_ALWAYS_INLINE void setM(Heap::StringOrSymbol *b) + { + quint32 v; + memcpy(&v, &b, 4); + val = v; + } +#endif + +public: + static PropertyKey invalid() { PropertyKey key; key.val = 0; return key; } + static PropertyKey fromArrayIndex(uint idx) { PropertyKey key; key.val = ArrayIndexMask | static_cast<quint64>(idx); return key; } + bool isStringOrSymbol() const { return isManaged() && val != 0; } + uint asArrayIndex() const { return (isManaged() || val == 0) ? std::numeric_limits<uint>::max() : static_cast<uint>(val & 0xffffffff); } + uint isArrayIndex() const { return !isManaged() && val != 0; } + bool isValid() const { return val != 0; } + static PropertyKey fromStringOrSymbol(Heap::StringOrSymbol *b) + { PropertyKey key; key.setM(b); return key; } + Heap::StringOrSymbol *asStringOrSymbol() const { + if (!isManaged()) + return nullptr; + return m(); + } + + bool isString() const; + bool isSymbol() const; + + Q_QML_EXPORT QString toQString() const; + Heap::StringOrSymbol *toStringOrSymbol(ExecutionEngine *e); + quint64 id() const { return val; } + + bool operator ==(const PropertyKey &other) const { return val == other.val; } + bool operator !=(const PropertyKey &other) const { return val != other.val; } + bool operator <(const PropertyKey &other) const { return val < other.val; } +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4proxy.cpp b/src/qml/jsruntime/qv4proxy.cpp new file mode 100644 index 0000000000..794aee8c24 --- /dev/null +++ b/src/qml/jsruntime/qv4proxy.cpp @@ -0,0 +1,553 @@ +/**************************************************************************** +** +** 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 "qv4proxy_p.h" +#include "qv4symbol_p.h" +#include "qv4jscall_p.h" +#include "qv4objectproto_p.h" + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(ProxyObject); + +void Heap::ProxyObject::init(const QV4::Object *target, const QV4::Object *handler) +{ + Object::init(); + ExecutionEngine *e = internalClass->engine; + this->target.set(e, target->d()); + this->handler.set(e, handler->d()); +} + +ReturnedValue ProxyObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedValue trap(scope, handler->get(scope.engine->id_get())); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->get(id, receiver, hasProperty); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + if (hasProperty) + *hasProperty = true; + + JSCallData cdata(scope, 3, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = id.toStringOrSymbol(scope.engine); + cdata.args[2] = *receiver; + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + ScopedProperty targetDesc(scope); + PropertyAttributes attributes = target->getOwnProperty(id, targetDesc); + if (attributes != Attr_Invalid && !attributes.isConfigurable()) { + if (attributes.isData() && !attributes.isWritable()) { + if (!trapResult->sameValue(targetDesc->value)) + return scope.engine->throwTypeError(); + } + if (attributes.isAccessor() && targetDesc->value.isUndefined()) { + if (!trapResult->isUndefined()) + return scope.engine->throwTypeError(); + } + } + return trapResult->asReturnedValue(); +} + +bool ProxyObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedValue trap(scope, handler->get(scope.engine->id_set())); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->put(id, value, receiver); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + JSCallData cdata(scope, 4, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = id.toStringOrSymbol(scope.engine); + cdata.args[2] = value; + cdata.args[3] = *receiver; + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (!trapResult->toBoolean()) + return false; + ScopedProperty targetDesc(scope); + PropertyAttributes attributes = target->getOwnProperty(id, targetDesc); + if (attributes != Attr_Invalid && !attributes.isConfigurable()) { + if (attributes.isData() && !attributes.isWritable()) { + if (!value.sameValue(targetDesc->value)) + return scope.engine->throwTypeError(); + } + if (attributes.isAccessor() && targetDesc->set.isUndefined()) + return scope.engine->throwTypeError(); + } + return true; +} + +bool ProxyObject::virtualDeleteProperty(Managed *m, PropertyKey id) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString deleteProp(scope, scope.engine->newString(QStringLiteral("deleteProperty"))); + ScopedValue trap(scope, handler->get(deleteProp)); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->deleteProperty(id); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + JSCallData cdata(scope, 3, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = id.toStringOrSymbol(scope.engine); + cdata.args[2] = o->d(); // ### fix receiver handling + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (!trapResult->toBoolean()) + return false; + ScopedProperty targetDesc(scope); + PropertyAttributes attributes = target->getOwnProperty(id, targetDesc); + if (attributes == Attr_Invalid) + return true; + if (!attributes.isConfigurable()) + return scope.engine->throwTypeError(); + return true; +} + +bool ProxyObject::virtualHasProperty(const Managed *m, PropertyKey id) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("has"))); + ScopedValue trap(scope, handler->get(hasProp)); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->hasProperty(id); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + JSCallData cdata(scope, 2, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = id.isArrayIndex() ? Primitive::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol(); + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + bool result = trapResult->toBoolean(); + if (!result) { + ScopedProperty targetDesc(scope); + PropertyAttributes attributes = target->getOwnProperty(id, targetDesc); + if (attributes != Attr_Invalid) { + if (!attributes.isConfigurable() || !target->isExtensible()) + return scope.engine->throwTypeError(); + } + } + return result; +} + +PropertyAttributes ProxyObject::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) { + scope.engine->throwTypeError(); + return Attr_Invalid; + } + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString deleteProp(scope, scope.engine->newString(QStringLiteral("getOwnPropertyDescriptor"))); + ScopedValue trap(scope, handler->get(deleteProp)); + if (scope.hasException()) + return Attr_Invalid; + if (trap->isNullOrUndefined()) + return target->getOwnProperty(id, p); + if (!trap->isFunctionObject()) { + scope.engine->throwTypeError(); + return Attr_Invalid; + } + + JSCallData cdata(scope, 2, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = id.isArrayIndex() ? Primitive::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol(); + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (!trapResult->isObject() && !trapResult->isUndefined()) { + scope.engine->throwTypeError(); + return Attr_Invalid; + } + + ScopedProperty targetDesc(scope); + PropertyAttributes targetAttributes = target->getOwnProperty(id, targetDesc); + if (trapResult->isUndefined()) { + p->value = Encode::undefined(); + if (targetAttributes == Attr_Invalid) { + p->value = Encode::undefined(); + return Attr_Invalid; + } + if (!targetAttributes.isConfigurable() || !target->isExtensible()) { + scope.engine->throwTypeError(); + return Attr_Invalid; + } + return Attr_Invalid; + } + + //bool extensibleTarget = target->isExtensible(); + ScopedProperty resultDesc(scope); + PropertyAttributes resultAttributes; + ObjectPrototype::toPropertyDescriptor(scope.engine, trapResult, resultDesc, &resultAttributes); + resultDesc->fullyPopulated(&resultAttributes); + + // ### + //Let valid be IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc). + //If valid is false, throw a TypeError exception. + + if (!resultAttributes.isConfigurable()) { + if (targetAttributes == Attr_Invalid || !targetAttributes.isConfigurable()) { + scope.engine->throwTypeError(); + return Attr_Invalid; + } + } + + p->value = resultDesc->value; + p->set = resultDesc->set; + return resultAttributes; +} + +bool ProxyObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) { + scope.engine->throwTypeError(); + return false; + } + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString prop(scope, scope.engine->newString(QStringLiteral("defineProperty"))); + ScopedValue trap(scope, handler->get(prop)); + if (scope.hasException()) + return false; + if (trap->isNullOrUndefined()) + return target->defineOwnProperty(id, p, attrs); + if (!trap->isFunctionObject()) { + scope.engine->throwTypeError(); + return false; + } + + JSCallData cdata(scope, 3, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = id.isArrayIndex() ? Primitive::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol(); + cdata.args[2] = ObjectPrototype::fromPropertyDescriptor(scope.engine, p, attrs); + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + bool result = trapResult->toBoolean(); + if (!result) + return false; + + ScopedProperty targetDesc(scope); + PropertyAttributes targetAttributes = target->getOwnProperty(id, targetDesc); + bool extensibleTarget = target->isExtensible(); + bool settingConfigFalse = attrs.hasConfigurable() && !attrs.isConfigurable(); + if (targetAttributes == Attr_Invalid) { + if (!extensibleTarget || settingConfigFalse) { + scope.engine->throwTypeError(); + return false; + } + } else { + // ### + // if IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) is false throw a type error. + if (settingConfigFalse && targetAttributes.isConfigurable()) { + scope.engine->throwTypeError(); + return false; + } + } + + return true; +} + +bool ProxyObject::virtualIsExtensible(const Managed *m) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("isExtensible"))); + ScopedValue trap(scope, handler->get(hasProp)); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->isExtensible(); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + JSCallData cdata(scope, 1, nullptr, handler); + cdata.args[0] = target; + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + bool result = trapResult->toBoolean(); + if (result != target->isExtensible()) { + scope.engine->throwTypeError(); + return false; + } + return result; +} + +bool ProxyObject::virtualPreventExtensions(Managed *m) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("preventExtensions"))); + ScopedValue trap(scope, handler->get(hasProp)); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->preventExtensions(); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + JSCallData cdata(scope, 1, nullptr, handler); + cdata.args[0] = target; + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + bool result = trapResult->toBoolean(); + if (result && target->isExtensible()) { + scope.engine->throwTypeError(); + return false; + } + return result; +} + +Heap::Object *ProxyObject::virtualGetPrototypeOf(const Managed *m) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) { + scope.engine->throwTypeError(); + return nullptr; + } + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString name(scope, scope.engine->newString(QStringLiteral("getPrototypeOf"))); + ScopedValue trap(scope, handler->get(name)); + if (scope.hasException()) + return nullptr; + if (trap->isNullOrUndefined()) + return target->getPrototypeOf(); + if (!trap->isFunctionObject()) { + scope.engine->throwTypeError(); + return nullptr; + } + + JSCallData cdata(scope, 1, nullptr, handler); + cdata.args[0] = target; + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (!trapResult->isNull() && !trapResult->isObject()) { + scope.engine->throwTypeError(); + return nullptr; + } + Heap::Object *proto = trapResult->isNull() ? nullptr : static_cast<Heap::Object *>(trapResult->heapObject()); + if (!target->isExtensible()) { + Heap::Object *targetProto = target->getPrototypeOf(); + if (proto != targetProto) { + scope.engine->throwTypeError(); + return nullptr; + } + } + return proto; +} + +bool ProxyObject::virtualSetPrototypeOf(Managed *m, const Object *p) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) { + scope.engine->throwTypeError(); + return false; + } + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString name(scope, scope.engine->newString(QStringLiteral("setPrototypeOf"))); + ScopedValue trap(scope, handler->get(name)); + if (scope.hasException()) + return false; + if (trap->isNullOrUndefined()) + return target->setPrototypeOf(p); + if (!trap->isFunctionObject()) { + scope.engine->throwTypeError(); + return false; + } + + JSCallData cdata(scope, 2, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = p ? p->asReturnedValue() : Encode::null(); + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + bool result = trapResult->toBoolean(); + if (!result) + return false; + if (!target->isExtensible()) { + Heap::Object *targetProto = target->getPrototypeOf(); + if (p->d() != targetProto) { + scope.engine->throwTypeError(); + return false; + } + } + return true; +} + +//ReturnedValue ProxyObject::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +//{ + +//} + +//ReturnedValue ProxyObject::call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +//{ + +//} + +DEFINE_OBJECT_VTABLE(Proxy); + +void Heap::Proxy::init(QV4::ExecutionContext *ctx) +{ + Heap::FunctionObject::init(ctx, QStringLiteral("Proxy")); + + Scope scope(ctx); + Scoped<QV4::Proxy> ctor(scope, this); + ctor->defineDefaultProperty(QStringLiteral("revocable"), QV4::Proxy::method_revocable, 2); + ctor->defineReadonlyConfigurableProperty(scope.engine->id_length(), Primitive::fromInt32(2)); +} + +ReturnedValue Proxy::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +{ + Scope scope(f); + if (argc < 2 || !argv[0].isObject() || !argv[1].isObject()) + return scope.engine->throwTypeError(); + + const Object *target = static_cast<const Object *>(argv); + const Object *handler = static_cast<const Object *>(argv + 1); + if (const ProxyObject *ptarget = target->as<ProxyObject>()) + if (!ptarget->d()->handler) + return scope.engine->throwTypeError(); + if (const ProxyObject *phandler = handler->as<ProxyObject>()) + if (!phandler->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, scope.engine->memoryManager->allocate<ProxyObject>(target, handler)); + return o->asReturnedValue(); +} + +ReturnedValue Proxy::virtualCall(const FunctionObject *f, const Value *, const Value *, int) +{ + return f->engine()->throwTypeError(); +} + +ReturnedValue Proxy::method_revocable(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + ScopedObject proxy(scope, Proxy::virtualCallAsConstructor(f, argv, argc, f)); + if (scope.hasException()) + return Encode::undefined(); + + ScopedString revoke(scope, scope.engine->newString(QStringLiteral("revoke"))); + ScopedFunctionObject revoker(scope, createBuiltinFunction(scope.engine, revoke, method_revoke, 0)); + revoker->defineDefaultProperty(scope.engine->symbol_revokableProxy(), proxy); + + ScopedObject o(scope, scope.engine->newObject()); + ScopedString p(scope, scope.engine->newString(QStringLiteral("proxy"))); + o->defineDefaultProperty(p, proxy); + o->defineDefaultProperty(revoke, revoker); + return o->asReturnedValue(); +} + +ReturnedValue Proxy::method_revoke(const FunctionObject *f, const Value *, const Value *, int) +{ + Scope scope(f); + Scoped<ProxyObject> proxy(scope, f->get(scope.engine->symbol_revokableProxy())); + Q_ASSERT(proxy); + + proxy->d()->target.set(scope.engine, nullptr); + proxy->d()->handler.set(scope.engine, nullptr); + return Encode::undefined(); +} diff --git a/src/qml/jsruntime/qv4proxy_p.h b/src/qml/jsruntime/qv4proxy_p.h new file mode 100644 index 0000000000..4d631e882c --- /dev/null +++ b/src/qml/jsruntime/qv4proxy_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** 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 QV4PROXY_P_H +#define QV4PROXY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Heap { + +#define ProxyObjectMembers(class, Member) \ + Member(class, Pointer, Object *, target) \ + Member(class, Pointer, Object *, handler) + +DECLARE_HEAP_OBJECT(ProxyObject, Object) { + DECLARE_MARKOBJECTS(ProxyObject) + + void init(const QV4::Object *target, const QV4::Object *handler); +}; + +#define ProxyMembers(class, Member) \ + Member(class, Pointer, Symbol *, revokableProxySymbol) \ + +DECLARE_HEAP_OBJECT(Proxy, FunctionObject) { + DECLARE_MARKOBJECTS(Proxy) + + void init(QV4::ExecutionContext *ctx); +}; + +} + +struct ProxyObject: Object { + V4_OBJECT2(ProxyObject, Object) + Q_MANAGED_TYPE(ProxyObject) + V4_INTERNALCLASS(ProxyObject) + + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); + static bool virtualDeleteProperty(Managed *m, PropertyKey id); + static bool virtualHasProperty(const Managed *m, PropertyKey id); + static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p); + static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs); + static bool virtualIsExtensible(const Managed *m); + static bool virtualPreventExtensions(Managed *); + static Heap::Object *virtualGetPrototypeOf(const Managed *); + static bool virtualSetPrototypeOf(Managed *, const Object *); + + // those might require a second proxy object that derives from FunctionObject... +// static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); +// static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct Proxy : FunctionObject +{ + V4_OBJECT2(Proxy, FunctionObject) + + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue method_revocable(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue method_revoke(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 040f060476..dc69016559 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -78,30 +78,25 @@ void Heap::QQmlContextWrapper::destroy() Object::destroy(); } -ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasProperty) +ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { Q_ASSERT(m->as<QQmlContextWrapper>()); + + if (!id.isString()) + return Object::virtualGet(m, id, receiver, hasProperty); + 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); + return Object::virtualGet(m, id, receiver, hasProperty); if (v4->callingQmlContext() != *resource->d()->context) - return Object::get(m, name, hasProperty); + return Object::virtualGet(m, id, receiver, hasProperty); - result = Object::get(m, name, &hasProp); + bool hasProp = false; + ScopedValue result(scope, Object::virtualGet(m, id, receiver, &hasProp)); if (hasProp) { if (hasProperty) *hasProperty = hasProp; @@ -129,6 +124,7 @@ ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasP QObject *scopeObject = resource->getScopeObject(); + ScopedString name(scope, id.asStringOrSymbol()); if (context->imports && name->startsWithUpper()) { // Search for attached properties, enums and imported scripts QQmlTypeNameCache::Result r = context->imports->query(name, QQmlImport::AllowRecursion); @@ -139,7 +135,7 @@ ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasP if (r.scriptIndex != -1) { QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); if (scripts) - return scripts->getIndexed(r.scriptIndex); + return scripts->get(r.scriptIndex); return QV4::Encode::null(); } else if (r.type.isValid()) { return QQmlTypeWrapper::create(v4, scopeObject, r.type); @@ -219,14 +215,27 @@ 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::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { Q_ASSERT(m->as<QQmlContextWrapper>()); + + if (id.isSymbol() || id.isArrayIndex()) + return Object::virtualPut(m, id, value, receiver); + QQmlContextWrapper *resource = static_cast<QQmlContextWrapper *>(m); ExecutionEngine *v4 = resource->engine(); QV4::Scope scope(v4); @@ -234,20 +243,20 @@ bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value) return false; QV4::Scoped<QQmlContextWrapper> wrapper(scope, resource); - uint member = wrapper->internalClass()->find(name); + uint member = wrapper->internalClass()->find(id); if (member < UINT_MAX) return wrapper->putValue(member, value); if (wrapper->d()->isNullWrapper) { if (wrapper && wrapper->d()->readOnly) { - QString error = QLatin1String("Invalid write to global property \"") + name->toQString() + + QString error = QLatin1String("Invalid write to global property \"") + id.toQString() + QLatin1Char('"'); ScopedString e(scope, v4->newString(error)); v4->throwError(e); return false; } - return Object::put(m, name, value); + return Object::virtualPut(m, id, value, receiver); } // It's possible we could delay the calculation of the "actual" context (in the case @@ -261,6 +270,7 @@ bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value) // See QV8ContextWrapper::Getter for resolution order QObject *scopeObject = wrapper->getScopeObject(); + ScopedString name(scope, id.asStringOrSymbol()); while (context) { const QV4::IdentifierHash &properties = context->propertyNames(); @@ -291,7 +301,7 @@ bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value) return false; } - return Object::put(m, name, value); + return Object::virtualPut(m, id, value, receiver); } void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml) @@ -312,7 +322,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 +340,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..b9061a3f58 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 virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); }; struct Q_QML_EXPORT QmlContext : public ExecutionContext diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index d63d42478a..a17de5d94d 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()); @@ -308,7 +314,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String } } } - return QV4::Object::get(this, name, hasProperty); + return QV4::Object::virtualGet(this, name->propertyKey(), this, hasProperty); } QQmlData *ddata = QQmlData::get(d()->object(), false); @@ -661,7 +667,7 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int p return setProperty(engine, object, property, value); } -bool QObjectWrapper::isEqualTo(Managed *a, Managed *b) +bool QObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b) { Q_ASSERT(a->as<QV4::QObjectWrapper>()); QV4::QObjectWrapper *qobjectWrapper = static_cast<QV4::QObjectWrapper *>(a); @@ -684,60 +690,77 @@ 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::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { + if (!id.isString()) + return Object::virtualGet(m, id, receiver, hasProperty); + const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m); + Scope scope(that); + ScopedString n(scope, id.asStringOrSymbol()); QQmlContextData *qmlContext = that->engine()->callingQmlContext(); - return that->getQmlProperty(qmlContext, 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::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { + if (!id.isString()) + return Object::virtualPut(m, id, value, receiver); + + Scope scope(m); QObjectWrapper *that = static_cast<QObjectWrapper*>(m); - ExecutionEngine *v4 = that->engine(); + ScopedString name(scope, id.asStringOrSymbol()); - if (v4->hasException || QQmlData::wasDeleted(that->d()->object())) + if (scope.engine->hasException || QQmlData::wasDeleted(that->d()->object())) return false; - QQmlContextData *qmlContext = v4->callingQmlContext(); - if (!setQmlProperty(v4, qmlContext, that->d()->object(), name, QV4::QObjectWrapper::IgnoreRevision, value)) { + QQmlContextData *qmlContext = scope.engine->callingQmlContext(); + if (!setQmlProperty(scope.engine, qmlContext, that->d()->object(), name, QV4::QObjectWrapper::IgnoreRevision, value)) { QQmlData *ddata = QQmlData::get(that->d()->object()); // Types created by QML are not extensible at run-time, but for other QObjects we can store them // as regular JavaScript properties, like on JavaScript objects. if (ddata && ddata->context) { QString error = QLatin1String("Cannot assign to non-existent property \"") + name->toQString() + QLatin1Char('\"'); - v4->throwError(error); + scope.engine->throwError(error); return false; } else { - return QV4::Object::put(m, name, value); + return QV4::Object::virtualPut(m, id, value, receiver); } } return true; } -PropertyAttributes QObjectWrapper::query(const Managed *m, String *name) +PropertyAttributes QObjectWrapper::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p) { - const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m); - const QObject *thatObject = that->d()->object(); - if (QQmlData::wasDeleted(thatObject)) - return QV4::Object::query(m, name); + if (id.isString()) { + QObjectWrapper *that = static_cast<QObjectWrapper*>(m); + const QObject *thatObject = that->d()->object(); + if (!QQmlData::wasDeleted(thatObject)) { + Scope scope(m); + ScopedString n(scope, id.asStringOrSymbol()); + QQmlContextData *qmlContext = scope.engine->callingQmlContext(); + QQmlPropertyData local; + if (that->findProperty(scope.engine, qmlContext, n, IgnoreRevision, &local) + || n->equals(scope.engine->id_destroy()) || n->equals(scope.engine->id_toString())) { + if (p) { + // ### probably not the fastest implementation + bool hasProperty; + p->value = that->getQmlProperty(qmlContext, n, IgnoreRevision, &hasProperty, /*includeImports*/ true); + } + return QV4::Attr_Data; + } + } + } - 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())) - return QV4::Attr_Data; - else - return QV4::Object::query(m, name); + return QV4::Object::virtualGetOwnProperty(m, id, p); } -void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) +void QObjectWrapper::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) { // Used to block access to QObject::destroyed() and QObject::deleteLater() from QML static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)"); @@ -788,7 +811,7 @@ void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name return; } } - QV4::Object::advanceIterator(m, it, name, index, p, attributes); + QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes); } namespace QV4 { @@ -1619,6 +1642,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 +1655,7 @@ void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M } } } +#endif void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const QV4::Value &value) { @@ -1685,7 +1710,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q uint length = array->getLength(); for (uint ii = 0; ii < length; ++ii) { QObject *o = nullptr; - qobjectWrapper = array->getIndexed(ii); + qobjectWrapper = array->get(ii); if (!!qobjectWrapper) o = qobjectWrapper->object(); qlistPtr->append(o); @@ -1713,6 +1738,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 +1766,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 +1860,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 +1873,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()); @@ -1906,7 +1933,7 @@ QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionEngine *engine, c return Encode::undefined(); } -ReturnedValue QObjectMethod::call(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc) +ReturnedValue QObjectMethod::virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc) { const QObjectMethod *This = static_cast<const QObjectMethod*>(m); return This->callInternal(thisObject, argv, argc); @@ -2019,7 +2046,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(); } @@ -2037,7 +2064,7 @@ void QMetaObjectWrapper::init(ExecutionEngine *) { } } -ReturnedValue QMetaObjectWrapper::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue QMetaObjectWrapper::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(f); return This->constructInternal(argv, argc); @@ -2068,7 +2095,7 @@ ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc) } Scoped<QMetaObjectWrapper> metaObject(scope, this); object->defineDefaultProperty(v4->id_constructor(), metaObject); - object->setPrototype(const_cast<QMetaObjectWrapper*>(this)); + object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this)); return object.asReturnedValue(); } @@ -2143,7 +2170,7 @@ ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine } } -bool QMetaObjectWrapper::isEqualTo(Managed *a, Managed *b) +bool QMetaObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b) { Q_ASSERT(a->as<QMetaObjectWrapper>()); QMetaObjectWrapper *aMetaObject = a->as<QMetaObjectWrapper>(); diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 1455acc1b3..7843289d33 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -183,7 +183,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object void destroyObject(bool lastCall); protected: - static bool isEqualTo(Managed *that, Managed *o); + static bool virtualIsEqualTo(Managed *that, Managed *o); static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired = true); static void setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value); @@ -193,10 +193,10 @@ 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 void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); + static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p); + static void virtualAdvanceIterator(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); static ReturnedValue method_disconnect(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); @@ -237,7 +237,7 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject QV4::ReturnedValue method_toString(QV4::ExecutionEngine *engine) const; QV4::ReturnedValue method_destroy(QV4::ExecutionEngine *ctx, const Value *args, int argc) const; - static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); ReturnedValue callInternal(const Value *thisObject, const Value *argv, int argc) const; @@ -251,11 +251,12 @@ struct Q_QML_EXPORT QMetaObjectWrapper : public QV4::FunctionObject V4_NEEDS_DESTROY static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject); - static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); - static bool isEqualTo(Managed *a, Managed *b); - const QMetaObject *metaObject() const { return d()->metaObject; } +protected: + static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); + static bool virtualIsEqualTo(Managed *a, Managed *b); + private: void init(ExecutionEngine *engine); ReturnedValue constructInternal(const Value *argv, int argc) const; diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp new file mode 100644 index 0000000000..20ea39d4e5 --- /dev/null +++ b/src/qml/jsruntime/qv4reflect.cpp @@ -0,0 +1,260 @@ +/**************************************************************************** +** +** 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" +#include "qv4propertykey_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->get(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]); + ScopedPropertyKey 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(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) +{ + 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; + ScopedPropertyKey name(scope, index->toPropertyKey(scope.engine)); + if (scope.hasException()) + return Encode::undefined(); + ScopedValue receiver(scope, argc > 2 ? argv[2] : *o); + + return Encode(o->get(name, receiver)); +} + +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 *, const Value *argv, int argc) +{ + if (!argc || !argv[0].isObject()) + return f->engine()->throwTypeError(); + + const Object *o = static_cast<const Object *>(argv); + Heap::Object *p = o->getPrototypeOf(); + return (p ? p->asReturnedValue() : Encode::null()); +} + +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; + + ScopedPropertyKey name(scope, index->toPropertyKey(scope.engine)); + if (scope.engine->hasException) + return false; + + bool hasProperty = false; + (void) o->get(name, nullptr, &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)); + return Encode(o->preventExtensions()); +} + +ReturnedValue Reflect::method_set(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; + const Value &val = argc > 2 ? argv[2] : undef; + ScopedValue receiver(scope, argc >3 ? argv[3] : argv[0]); + + ScopedPropertyKey propertyKey(scope, index->toPropertyKey(scope.engine)); + if (scope.engine->hasException) + return false; + bool result = o->put(propertyKey, val, receiver); + return Encode(result); +} + +ReturnedValue Reflect::method_setPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + if (argc < 2 || !argv[0].isObject() || (!argv[1].isNull() && !argv[1].isObject())) + return f->engine()->throwTypeError(); + + Scope scope(f); + ScopedObject o(scope, static_cast<const Object *>(argv)); + const Object *proto = argv[1].isNull() ? nullptr : static_cast<const Object *>(argv + 1); + return Encode(o->setPrototypeOf(proto)); +} diff --git a/src/qml/jsruntime/qv4reflect_p.h b/src/qml/jsruntime/qv4reflect_p.h new file mode 100644 index 0000000000..d480e1d914 --- /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 *, 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..8429b96baa 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> @@ -214,7 +210,7 @@ void Heap::RegExpCtor::clearLastMatch() lastMatchEnd = 0; } -ReturnedValue RegExpCtor::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc) +ReturnedValue RegExpCtor::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *) { Scope scope(fo->engine()); ScopedValue r(scope, argc ? argv[0] : Primitive::undefinedValue()); @@ -263,14 +259,14 @@ ReturnedValue RegExpCtor::callAsConstructor(const FunctionObject *fo, const Valu return Encode(scope.engine->newRegExpObject(regexp)); } -ReturnedValue RegExpCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) +ReturnedValue RegExpCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) { if (argc > 0 && argv[0].as<RegExpObject>()) { if (argc == 1 || argv[1].isUndefined()) return Encode(argv[0]); } - return callAsConstructor(f, argv, argc); + return virtualCallAsConstructor(f, argv, argc, f); } void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor) @@ -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); @@ -442,12 +439,12 @@ ReturnedValue RegExpPrototype::method_compile(const FunctionObject *b, const Val return Encode::undefined(); } -template <int index> +template <uint index> ReturnedValue RegExpPrototype::method_get_lastMatch_n(const FunctionObject *b, const Value *, const Value *, int) { Scope scope(b); ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastMatch()); - ScopedValue res(scope, lastMatch ? lastMatch->getIndexed(index) : Encode::undefined()); + ScopedValue res(scope, lastMatch ? lastMatch->get(index) : Encode::undefined()); if (res->isUndefined()) res = scope.engine->newString(); return res->asReturnedValue(); @@ -457,7 +454,7 @@ ReturnedValue RegExpPrototype::method_get_lastParen(const FunctionObject *b, con { Scope scope(b); ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastMatch()); - ScopedValue res(scope, lastMatch ? lastMatch->getIndexed(lastMatch->getLength() - 1) : Encode::undefined()); + ScopedValue res(scope, lastMatch ? lastMatch->get(lastMatch->getLength() - 1) : Encode::undefined()); if (res->isUndefined()) res = scope.engine->newString(); return res->asReturnedValue(); diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index 181628241b..0d4fe760eb 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()->propertyKey())); 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()->propertyKey())); return setProperty(Index_LastIndex, Primitive::fromInt32(index)); } @@ -152,8 +152,8 @@ struct RegExpCtor: FunctionObject int lastMatchStart() { return d()->lastMatchStart; } int lastMatchEnd() { return d()->lastMatchEnd; } - 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 virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct RegExpPrototype: RegExpObject @@ -165,7 +165,7 @@ struct RegExpPrototype: RegExpObject static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_compile(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - template <int index> + template <uint index> static ReturnedValue method_get_lastMatch_n(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_get_lastParen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_get_input(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 9729228511..f387285e37 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,23 @@ 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); + + ScopedPropertyKey key(scope, index.toPropertyKey(engine)); + if (engine->hasException) + return false; + return o->deleteProperty(key); } bool Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex) @@ -361,8 +349,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 +367,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)); + ScopedPropertyKey s(scope, left.toPropertyKey(engine)); if (scope.hasException()) return Encode::undefined(); bool r = ro->hasProperty(s); @@ -408,27 +404,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->propertyKey() == engine->id_number()->propertyKey()) { qSwap(meth1, meth2); + } else { + Q_ASSERT(typeHint->propertyKey() == engine->id_string()->propertyKey()); + } 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,19 +477,22 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH } - Heap::Object *RuntimeHelpers::convertToObject(ExecutionEngine *engine, const Value &value) { Q_ASSERT(!value.isObject()); switch (value.type()) { case Value::Undefined_Type: + engine->throwTypeError(QLatin1String("Value is undefined and could not be converted to an object")); + return nullptr; case Value::Null_Type: - engine->throwTypeError(); + engine->throwTypeError(QLatin1String("Value is null and could not be converted to an object")); return nullptr; 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 +519,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()) @@ -539,14 +574,14 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu return Encode(x + y); } -bool Runtime::method_storeProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) +void Runtime::method_storeProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) { Scope scope(engine); - ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + QV4::Function *v4Function = engine->currentStackFrame->v4Function; + ScopedString name(scope, v4Function->compilationUnit->runtimeStrings[nameIndex]); ScopedObject o(scope, object.toObject(engine)); - if (!o) - return false; - return o->put(name, value); + if ((!o || !o->put(name, value)) && v4Function->isStrict()) + engine->throwTypeError(); } static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engine, const Value &object, uint idx) @@ -579,12 +614,13 @@ static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engin return v->asReturnedValue(); } - return o->getIndexed(idx); + return o->get(idx); } static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine, const Value &object, const Value &index) { - Q_ASSERT(index.asArrayIndex() == UINT_MAX); + Q_ASSERT(!index.isPositiveInt()); + Scope scope(engine); ScopedObject o(scope, object); @@ -598,26 +634,18 @@ 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)); + ScopedPropertyKey name(scope, index.toPropertyKey(engine)); if (scope.hasException()) return Encode::undefined(); return o->get(name); } -/* load element: - - Managed *m = object.heapObject(); - if (m) - return m->internalClass->getIndexed(m, index); - return getIndexedFallback(object, index); -*/ - ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value &object, const Value &index) { - uint idx = 0; - if (index.asArrayIndex(idx)) { + if (index.isPositiveInt()) { + uint idx = static_cast<uint>(index.int_32()); 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>(); @@ -640,8 +668,8 @@ static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Val if (engine->hasException) return false; - uint idx = 0; - if (index.asArrayIndex(idx)) { + if (index.isPositiveInt()) { + uint idx = static_cast<uint>(index.int_32()); if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) { Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (idx < s->values.size) { @@ -649,53 +677,136 @@ static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Val return true; } } - return o->putIndexed(idx, value); + return o->put(idx, value); } - ScopedString name(scope, index.toString(engine)); + ScopedPropertyKey name(scope, index.toPropertyKey(engine)); + if (engine->hasException) + return false; return o->put(name, value); } -bool Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) +void Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { - uint idx = 0; - if (index.asArrayIndex(idx)) { + if (index.isPositiveInt()) { + uint idx = static_cast<uint>(index.int_32()); 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>(); if (idx < s->values.size) { s->setData(engine, idx, value); - return true; + return; } } } } } - return setElementFallback(engine, object, index, value); + if (!setElementFallback(engine, object, index, value) && engine->currentStackFrame->v4Function->isStrict()) + engine->throwTypeError(); } -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()); - ForEachIteratorObject *it = static_cast<ForEachIteratorObject *>(foreach_iterator.objectValue()); - Q_ASSERT(it->as<ForEachIteratorObject>()); + 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); +} - return it->nextPropertyName(); +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(); + + 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) { @@ -745,6 +856,42 @@ ReturnedValue Runtime::method_loadName(ExecutionEngine *engine, int nameIndex) return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name); } +ReturnedValue Runtime::method_loadSuperProperty(ExecutionEngine *engine, const Value &property) +{ + Scope scope(engine); + ScopedObject base(scope, engine->currentStackFrame->thisObject()); + if (!base) + return engine->throwTypeError(); + ScopedObject proto(scope, base->getPrototypeOf()); + if (!proto) + return engine->throwTypeError(); + ScopedPropertyKey key(scope, property.toPropertyKey(engine)); + if (engine->hasException) + return Encode::undefined(); + return proto->get(key, base); +} + +void Runtime::method_storeSuperProperty(ExecutionEngine *engine, const Value &property, const Value &value) +{ + Scope scope(engine); + ScopedObject base(scope, engine->currentStackFrame->thisObject()); + if (!base) { + engine->throwTypeError(); + return; + } + ScopedObject proto(scope, base->getPrototypeOf()); + if (!proto) { + engine->throwTypeError(); + return; + } + ScopedPropertyKey key(scope, property.toPropertyKey(engine)); + if (engine->hasException) + return; + bool result = proto->put(key, value, base); + if (!result && engine->currentStackFrame->v4Function->isStrict()) + engine->throwTypeError(); +} + #endif // V4_BOOTSTRAP uint RuntimeHelpers::equalHelper(const Value &x, const Value &y) @@ -1033,24 +1180,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 +1234,7 @@ ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, Value *base, ScopedValue thisObject(scope, base->toObject(engine)); base = thisObject; - ScopedString str(scope, index.toString(engine)); + ScopedPropertyKey str(scope, index.toPropertyKey(engine)); if (engine->hasException) return Encode::undefined(); @@ -1130,12 +1284,80 @@ ReturnedValue Runtime::method_callQmlContextObjectProperty(ExecutionEngine *engi return fo->call(qmlContextValue, argv, argc); } -ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &function, Value *argv, int 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, const Value &newTarget, Value *argv, int argc) { if (!function.isFunctionObject()) return engine->throwTypeError(); - return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc); + return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc, &newTarget); +} + +ReturnedValue Runtime::method_constructWithSpread(ExecutionEngine *engine, const Value &function, const Value &newTarget, 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, &newTarget); } void Runtime::method_throwException(ExecutionEngine *engine, const Value &value) @@ -1161,6 +1383,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 +1407,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 exceptionVarNameIndex) +ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex) { ExecutionEngine *e = parent->engine(); - return parent->newCatchContext(e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex], - e->catchException(nullptr))->asReturnedValue(); + return parent->newCatchContext(e->currentStackFrame, blockIndex, + e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex])->asReturnedValue(); } +ReturnedValue Runtime::method_createBlockContext(ExecutionContext *parent, int index) +{ + ExecutionEngine *e = parent->engine(); + 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 +1467,160 @@ 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(); + + ScopedPropertyKey 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(name, pd, (arg == ObjectLiteralArgument::Value ? Attr_Data : Attr_Accessor)); + if (!ok) + return engine->throwTypeError(); + + args += 3; } + return o.asReturnedValue(); +} + +ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classIndex, const Value &superClass, const Value *computedNames) +{ + const CompiledData::CompilationUnit *unit = engine->currentStackFrame->v4Function->compilationUnit; + const QV4::CompiledData::Class *cls = unit->data->classAt(classIndex); - 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); + Scope scope(engine); + ScopedObject protoParent(scope, engine->objectPrototype()); + ScopedObject constructorParent(scope, engine->functionPrototype()); + if (!superClass.isEmpty()) { + if (superClass.isNull()) { + protoParent = Encode::null(); + } else { + // ### check that the heritage object is a constructor + if (!superClass.isFunctionObject()) + return engine->throwTypeError(QStringLiteral("The superclass is not a function object.")); + const FunctionObject *s = static_cast<const FunctionObject *>(&superClass); + ScopedValue result(scope, s->get(scope.engine->id_prototype())); + if (!result->isObject() && !result->isNull()) + return engine->throwTypeError(QStringLiteral("The value of the superclass's prototype property is not an object.")); + protoParent = *result; + constructorParent = superClass; } } - return o.asReturnedValue(); + ScopedObject proto(scope, engine->newObject()); + proto->setPrototypeUnchecked(protoParent); + ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context); + + ScopedFunctionObject constructor(scope); + QV4::Function *f = cls->constructorFunction != UINT_MAX ? unit->runtimeFunctions[cls->constructorFunction] : nullptr; + constructor = FunctionObject::createConstructorFunction(current, f, !superClass.isEmpty())->asReturnedValue(); + constructor->setPrototypeUnchecked(constructorParent); + constructor->defineDefaultProperty(engine->id_prototype(), proto); + proto->defineDefaultProperty(engine->id_constructor(), constructor); + + ScopedString name(scope); + if (cls->nameIndex != UINT_MAX) { + name = unit->runtimeStrings[cls->nameIndex]; + constructor->defineReadonlyConfigurableProperty(engine->id_name(), name); + } + + ScopedObject receiver(scope, *constructor); + ScopedPropertyKey propertyName(scope); + ScopedFunctionObject function(scope); + ScopedProperty property(scope); + const CompiledData::Method *methods = cls->methodTable(); + for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) { + if (i == cls->nStaticMethods) + receiver = proto; + if (methods[i].name == UINT_MAX) { + propertyName = computedNames->toPropertyKey(engine); + if (engine->hasException) + return Encode::undefined(); + ++computedNames; + } else { + name = unit->runtimeStrings[methods[i].name]; + propertyName = name->toPropertyKey(); + } + QV4::Function *f = unit->runtimeFunctions[methods[i].function]; + Q_ASSERT(f); + if (f->isGenerator()) + function = MemberGeneratorFunction::create(current, f); + else + function = FunctionObject::createMemberFunction(current, f); + Q_ASSERT(function); + PropertyAttributes attributes; + switch (methods[i].type) { + case CompiledData::Method::Getter: + property->setGetter(function); + property->set = Primitive::emptyValue(); + attributes = Attr_Accessor|Attr_NotEnumerable; + break; + case CompiledData::Method::Setter: + property->value = Primitive::emptyValue(); + property->setSetter(function); + attributes = Attr_Accessor|Attr_NotEnumerable; + break; + default: // Regular + property->value = function; + property->set = Primitive::emptyValue(); + attributes = Attr_Data|Attr_NotEnumerable; + break; + } + receiver->defineOwnProperty(propertyName, property, attributes); + } + + return constructor->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 +1837,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..3f65e6c6d6 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -99,33 +99,39 @@ 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, construct, (ExecutionEngine *engine, const Value &func, const Value &newTarget, Value *argv, int argc)) \ + F(ReturnedValue, constructWithSpread, (ExecutionEngine *engine, const Value &func, const Value &newTarget, Value *argv, int argc)) \ \ /* load & store */ \ F(void, storeNameStrict, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ F(void, storeNameSloppy, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ - F(bool, storeProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \ - F(bool, storeElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \ + F(void, storeProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \ + F(void, storeElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \ F(ReturnedValue, loadProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)) \ F(ReturnedValue, loadName, (ExecutionEngine *engine, int nameIndex)) \ F(ReturnedValue, loadElement, (ExecutionEngine *engine, const Value &object, const Value &index)) \ + F(ReturnedValue, loadSuperProperty, (ExecutionEngine *engine, const Value &property)) \ + F(void, storeSuperProperty, (ExecutionEngine *engine, const Value &property, const Value &value)) \ \ /* typeof */ \ F(ReturnedValue, typeofValue, (ExecutionEngine *engine, const Value &val)) \ 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 +140,18 @@ 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)) \ - \ - /* foreach */ \ - F(ReturnedValue, foreachIterator, (ExecutionEngine *engine, const Value &in)) \ - F(ReturnedValue, foreachNextPropertyName, (const Value &foreach_iterator)) \ + F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, int classId, int argc, const Value *args)) \ + F(ReturnedValue, createClass, (ExecutionEngine *engine, int classIndex, const Value &heritage, const Value *computedNames)) \ + \ + /* 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..1f66c4a47e 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -53,6 +53,7 @@ #include "qv4engine_p.h" #include "qv4value_p.h" #include "qv4property_p.h" +#include "qv4propertykey_p.h" #ifdef V4_USE_VALGRIND #include <valgrind/memcheck.h> @@ -117,8 +118,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 +174,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); } @@ -203,6 +241,37 @@ struct ScopedValue Value *ptr; }; + +struct ScopedPropertyKey +{ + ScopedPropertyKey(const Scope &scope) + { + ptr = reinterpret_cast<PropertyKey *>(scope.alloc<Scope::Uninitialized>()); + *ptr = PropertyKey::invalid(); + } + + ScopedPropertyKey(const Scope &scope, const PropertyKey &v) + { + ptr = reinterpret_cast<PropertyKey *>(scope.alloc<Scope::Uninitialized>()); + *ptr = v; + } + + ScopedPropertyKey &operator=(const PropertyKey &other) { + *ptr = other; + return *this; + } + + PropertyKey *operator->() { + return ptr; + } + operator PropertyKey() const { + return *ptr; + } + + PropertyKey *ptr; +}; + + template<typename T> struct Scoped { @@ -214,66 +283,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..37c4f27ca9 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -57,10 +57,11 @@ #include <QtCore/QDebug> #include <QtCore/QString> +#include <QScopedValueRollback> 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 +129,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; @@ -154,7 +155,7 @@ ReturnedValue Script::run(const QV4::Value *thisObject) QV4::Scope valueScope(engine); if (qmlContext.isUndefined()) { - TemporaryAssignment<Function*> savedGlobalCode(engine->globalCode, vmFunction); + QScopedValueRollback<Function*> savedGlobalCode(engine->globalCode, vmFunction); return vmFunction->call(thisObject ? thisObject : engine->globalObject, nullptr, 0, context); @@ -171,19 +172,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 +217,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..1ba889ee82 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; @@ -345,7 +349,7 @@ public: if (d()->isReference) { if (!d()->object) { - QV4::Object::advanceIterator(this, it, name, index, p, attrs); + QV4::Object::virtualAdvanceIterator(this, it, name, index, p, attrs); return; } loadReference(); @@ -358,7 +362,7 @@ public: p->value = convertElementToValue(engine(), d()->container->at(*index)); return; } - QV4::Object::advanceIterator(this, it, name, index, p, attrs); + QV4::Object::virtualAdvanceIterator(this, it, name, index, p, attrs); } bool containerDeleteIndexedProperty(uint index) @@ -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) @@ -524,7 +538,7 @@ public: quint32 length = array->getLength(); QV4::ScopedValue v(scope); for (quint32 i = 0; i < length; ++i) - result.push_back(convertValueToElement<typename Container::value_type>((v = array->getIndexed(i)))); + result.push_back(convertValueToElement<typename Container::value_type>((v = array->get(i)))); return QVariant::fromValue(result); } @@ -549,17 +563,31 @@ public: QMetaObject::metacall(d()->object, QMetaObject::WriteProperty, d()->propertyIndex, a); } - static QV4::ReturnedValue getIndexed(const QV4::Managed *that, uint index, bool *hasProperty) - { return static_cast<const QQmlSequence<Container> *>(that)->containerGetIndexed(index, hasProperty); } - static bool putIndexed(Managed *that, uint index, const QV4::Value &value) - { return static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(index, value); } + static QV4::ReturnedValue virtualGet(const QV4::Managed *that, PropertyKey id, const Value *receiver, bool *hasProperty) + { + if (!id.isArrayIndex()) + return Object::virtualGet(that, id, receiver, hasProperty); + return static_cast<const QQmlSequence<Container> *>(that)->containerGetIndexed(id.asArrayIndex(), hasProperty); + } + static bool virtualPut(Managed *that, PropertyKey id, const QV4::Value &value, Value *receiver) + { + if (id.isArrayIndex()) + return static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(id.asArrayIndex(), value); + return Object::virtualPut(that, id, value, receiver); + } static QV4::PropertyAttributes queryIndexed(const QV4::Managed *that, uint index) { return static_cast<const QQmlSequence<Container> *>(that)->containerQueryIndexed(index); } - static bool deleteIndexedProperty(QV4::Managed *that, uint index) - { return static_cast<QQmlSequence<Container> *>(that)->containerDeleteIndexedProperty(index); } - static bool isEqualTo(Managed *that, Managed *other) + static bool virtualDeleteProperty(QV4::Managed *that, PropertyKey id) + { + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + return static_cast<QQmlSequence<Container> *>(that)->containerDeleteIndexedProperty(index); + } + return Object::virtualDeleteProperty(that, id); + } + static bool virtualIsEqualTo(Managed *that, Managed *other) { return static_cast<QQmlSequence<Container> *>(that)->containerIsEqualTo(other); } - static void advanceIterator(Managed *that, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) + static void virtualAdvanceIterator(Managed *that, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) { return static_cast<QQmlSequence<Container> *>(that)->containerAdvanceIterator(it, name, index, p, attrs); } }; @@ -572,6 +600,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 +610,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 +698,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 +722,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 +740,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..e151966306 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) @@ -189,7 +197,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine push(data, valueheader(WorkerArray, length)); ScopedValue val(scope); for (uint ii = 0; ii < length; ++ii) - serialize(data, (val = array->getIndexed(ii)), engine); + serialize(data, (val = array->get(ii)), engine); } else if (v.isInteger()) { reserve(data, 2 * sizeof(quint32)); push(data, valueheader(WorkerInt32)); @@ -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(); @@ -252,10 +265,11 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine serialize(data, QV4::Primitive::fromInt32(QV4::SequencePrototype::metaTypeForSequence(o)), engine); // sequence type ScopedValue val(scope); for (uint ii = 0; ii < seqLength; ++ii) - serialize(data, (val = o->getIndexed(ii)), engine); // sequence elements + serialize(data, (val = o->get(ii)), engine); // sequence elements return; } +#endif // regular object QV4::ScopedValue val(scope, v); @@ -269,7 +283,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine QV4::ScopedValue s(scope); for (quint32 ii = 0; ii < length; ++ii) { - s = properties->getIndexed(ii); + s = properties->get(ii); serialize(data, s, engine); QV4::String *str = s->as<String>(); @@ -318,7 +332,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) ScopedValue v(scope); for (quint32 ii = 0; ii < size; ++ii) { v = deserialize(data, engine); - a->putIndexed(ii, v); + a->put(ii, v); } return a.asReturnedValue(); } @@ -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..4681a49bd6 --- /dev/null +++ b/src/qml/jsruntime/qv4setiterator.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** 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/qv4estable_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); + uint index = thisObject->d()->setNextIndex; + IteratorKind itemKind = thisObject->d()->iterationKind; + + if (!s) { + QV4::Value undefined = Primitive::undefinedValue(); + return IteratorPrototype::createIterResultObject(scope.engine, undefined, true); + } + + Value *arguments = scope.alloc(2); + + while (index < s->d()->esTable->size()) { + s->d()->esTable->iterate(index, &arguments[0], &arguments[1]); + thisObject->d()->setNextIndex = index + 1; + + if (itemKind == KeyValueIteratorKind) { + ScopedArrayObject resultArray(scope, scope.engine->newArrayObject()); + resultArray->arrayReserve(2); + resultArray->arrayPut(0, arguments[0]); + resultArray->arrayPut(1, arguments[0]); // yes, the key is repeated. + resultArray->setArrayLengthUnchecked(2); + + return IteratorPrototype::createIterResultObject(scope.engine, resultArray, false); + } + + return IteratorPrototype::createIterResultObject(scope.engine, arguments[0], 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..30e849bfed --- /dev/null +++ b/src/qml/jsruntime/qv4setobject.cpp @@ -0,0 +1,248 @@ +/**************************************************************************** +** +** 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 "qv4estable_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::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +{ + Scope scope(f); + Scoped<SetObject> a(scope, scope.engine->memoryManager->allocate<SetObject>()); + + 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::virtualCall(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); +} + +void Heap::SetObject::init() +{ + Object::init(); + esTable = new ESTable(); +} + +void Heap::SetObject::destroy() +{ + delete esTable; + esTable = 0; +} + +void Heap::SetObject::markObjects(Heap::Base *that, MarkStack *markStack) +{ + SetObject *s = static_cast<SetObject *>(that); + s->esTable->markObjects(markStack); + Object::markObjects(that, markStack); +} + +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(); + + that->d()->esTable->set(argv[0], Primitive::undefinedValue()); + 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(); + + that->d()->esTable->clear(); + return Encode::undefined(); +} + +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(); + + return Encode(that->d()->esTable->remove(argv[0])); +} + +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]); + + Value *arguments = scope.alloc(3); + for (uint i = 0; i < that->d()->esTable->size(); ++i) { + that->d()->esTable->iterate(i, &arguments[0], &arguments[1]); // fill in key (0), value (1) + arguments[1] = arguments[0]; // but for set, we want to return the key twice; value is always undefined. + + 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(); + + return Encode(that->d()->esTable->has(argv[0])); +} + +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(); + + return Encode(that->d()->esTable->size()); +} + +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..34649c3f01 --- /dev/null +++ b/src/qml/jsruntime/qv4setobject_p.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** 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 { + +class ESTable; + +namespace Heap { + +struct SetCtor : FunctionObject { + void init(QV4::ExecutionContext *scope); +}; + +struct SetObject : Object { + static void markObjects(Heap::Base *that, MarkStack *markStack); + void init(); + void destroy(); + ESTable *esTable; +}; + +} + +struct SetCtor: FunctionObject +{ + V4_OBJECT2(SetCtor, FunctionObject) + + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct SetObject : Object +{ + V4_OBJECT2(SetObject, Object) + V4_PROTOTYPE(setPrototype) + V4_NEEDS_DESTROY +}; + +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/qv4stackframe.cpp b/src/qml/jsruntime/qv4stackframe.cpp new file mode 100644 index 0000000000..a716c53aea --- /dev/null +++ b/src/qml/jsruntime/qv4stackframe.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** 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 "qv4stackframe_p.h" +#include <QtCore/qstring.h> + +using namespace QV4; + +QString CppStackFrame::source() const +{ + return v4Function ? v4Function->sourceFile() : QString(); +} + +QString CppStackFrame::function() const +{ + return v4Function ? v4Function->name()->toQString() : QString(); +} + +int CppStackFrame::lineNumber() const +{ + if (!v4Function) + return -1; + + auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) { + return entry.codeOffset < offset; + }; + + const QV4::CompiledData::Function *cf = v4Function->compiledFunction; + uint offset = instructionPointer; + const CompiledData::CodeOffsetToLine *lineNumbers = cf->lineNumberTable(); + uint nLineNumbers = cf->nLineNumbers; + const CompiledData::CodeOffsetToLine *line = std::lower_bound(lineNumbers, lineNumbers + nLineNumbers, offset, findLine) - 1; + return line->line; +} + +ReturnedValue CppStackFrame::thisObject() const { + return jsFrame->thisObject.asReturnedValue(); +} + diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h new file mode 100644 index 0000000000..aa507d61a6 --- /dev/null +++ b/src/qml/jsruntime/qv4stackframe_p.h @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** 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 QV4STACKFRAME_H +#define QV4STACKFRAME_H + +#include <private/qv4context_p.h> +#include <private/qv4enginebase_p.h> +#ifndef V4_BOOTSTRAP +#include <private/qv4function_p.h> +#endif + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct CallData +{ + enum Offsets { + Function = 0, + Context = 1, + Accumulator = 2, + This = 3, + NewTarget = 4, + Argc = 5, + + LastOffset = Argc, + OffsetCount = LastOffset + 1 + }; + + Value function; + Value context; + Value accumulator; + Value thisObject; + Value newTarget; + Value _argc; + + int argc() const { + Q_ASSERT(_argc.isInteger()); + return _argc.int_32(); + } + + void setArgc(int argc) { + Q_ASSERT(argc >= 0); + _argc.setInt_32(argc); + } + + inline ReturnedValue argument(int i) const { + return i < argc() ? args[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue(); + } + + Value args[1]; + + static Q_DECL_CONSTEXPR int HeaderSize() { return offsetof(CallData, args) / sizeof(QV4::Value); } +}; + +Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value); +Q_STATIC_ASSERT(offsetof(CallData, function ) == CallData::Function * sizeof(Value)); +Q_STATIC_ASSERT(offsetof(CallData, context ) == CallData::Context * sizeof(Value)); +Q_STATIC_ASSERT(offsetof(CallData, accumulator) == CallData::Accumulator * sizeof(Value)); +Q_STATIC_ASSERT(offsetof(CallData, thisObject ) == CallData::This * sizeof(Value)); +Q_STATIC_ASSERT(offsetof(CallData, newTarget ) == CallData::NewTarget * sizeof(Value)); +Q_STATIC_ASSERT(offsetof(CallData, _argc ) == CallData::Argc * sizeof(Value)); +Q_STATIC_ASSERT(offsetof(CallData, args ) == 6 * sizeof(Value)); + +struct Q_QML_EXPORT CppStackFrame { + EngineBase *engine; + Value *savedStackTop; + CppStackFrame *parent; + Function *v4Function; + CallData *jsFrame; + const Value *originalArguments; + int originalArgumentsCount; + int instructionPointer; + const char *yield; + const char *unwindHandler; + const char *unwindLabel; + int unwindLevel; + + void init(EngineBase *engine, Function *v4Function, const Value *argv, int argc) { + this->engine = engine; + + this->v4Function = v4Function; + originalArguments = argv; + originalArgumentsCount = argc; + instructionPointer = 0; + yield = nullptr; + unwindHandler = nullptr; + unwindLabel = nullptr; + unwindLevel = 0; + } + + void push() { + parent = engine->currentStackFrame; + engine->currentStackFrame = this; + savedStackTop = engine->jsStackTop; + } + + void pop() { + engine->currentStackFrame = parent; + engine->jsStackTop = savedStackTop; + } + +#ifndef V4_BOOTSTRAP + static uint requiredJSStackFrameSize(uint nRegisters) { + return CallData::HeaderSize() + nRegisters; + } + static uint requiredJSStackFrameSize(Function *v4Function) { + return CallData::HeaderSize() + v4Function->compiledFunction->nRegisters; + } + uint requiredJSStackFrameSize() const { + return requiredJSStackFrameSize(v4Function); + } + void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope, + const Value &thisObject, const Value &newTarget = Primitive::undefinedValue()) { + setupJSFrame(stackSpace, function, scope, thisObject, newTarget, + v4Function->nFormals, v4Function->compiledFunction->nRegisters); + } + void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope, + const Value &thisObject, const Value &newTarget, uint nFormals, uint nRegisters) + { + jsFrame = reinterpret_cast<CallData *>(stackSpace); + jsFrame->function = function; + jsFrame->context = scope->asReturnedValue(); + jsFrame->accumulator = Encode::undefined(); + jsFrame->thisObject = thisObject; + jsFrame->newTarget = newTarget; + + uint argc = uint(originalArgumentsCount); + if (argc > nFormals) + argc = nFormals; + jsFrame->setArgc(argc); + + memcpy(jsFrame->args, originalArguments, argc*sizeof(Value)); + const Value *end = jsFrame->args + nRegisters; + for (Value *v = jsFrame->args + argc; v < end; ++v) + *v = Encode::undefined(); + } +#endif + + QString source() const; + QString function() const; + inline QV4::ExecutionContext *context() const { + return static_cast<ExecutionContext *>(&jsFrame->context); + } + int lineNumber() const; + + inline QV4::Heap::CallContext *callContext() const { + Heap::ExecutionContext *ctx = static_cast<ExecutionContext &>(jsFrame->context).d();\ + while (ctx->type != Heap::ExecutionContext::Type_CallContext) + ctx = ctx->outer; + return static_cast<Heap::CallContext *>(ctx); + } + ReturnedValue thisObject() const; +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index 447992ebec..911103c05d 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::StringOrSymbol *id = s->identifier.asStringOrSymbol(); + 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,15 +77,16 @@ void Heap::String::markObjects(Heap::Base *that, MarkStack *markStack) } } +DEFINE_MANAGED_VTABLE(StringOrSymbol); DEFINE_MANAGED_VTABLE(String); -bool String::isEqualTo(Managed *t, Managed *o) +bool String::virtualIsEqualTo(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()) @@ -155,12 +166,12 @@ uint String::toUInt(bool *ok) const return UINT_MAX; } -void String::makeIdentifierImpl() const +void String::createPropertyKeyImpl() const { if (!d()->text) d()->simplifyString(); Q_ASSERT(d()->text); - engine()->identifierTable->identifier(this); + engine()->identifierTable->asPropertyKey(this); } void Heap::String::simplifyString() const @@ -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 = PropertyKey::invalid(); cs->left = cs->right = nullptr; internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(text->size) * (qptrdiff)sizeof(QChar)); @@ -227,17 +238,26 @@ 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; stringHash = QV4::String::calculateHashValue(ch, end, &subtype); } -uint String::getLength(const Managed *m) +PropertyKey StringOrSymbol::toPropertyKey() const { + if (d()->identifier.isValid()) + return d()->identifier; + createPropertyKey(); + return propertyKey(); +} + +qint64 String::virtualGetLength(const Managed *m) { return static_cast<const String *>(m)->d()->length(); } @@ -248,4 +268,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..8a4dc08693 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -60,13 +60,14 @@ QT_BEGIN_NAMESPACE namespace QV4 { struct ExecutionEngine; -struct Identifier; +struct PropertyKey; 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 PropertyKey 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,29 @@ 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 + }; + +private: + inline void createPropertyKey() const; +public: + PropertyKey propertyKey() const { Q_ASSERT(d()->identifier.isValid()); return d()->identifier; } + PropertyKey toPropertyKey() 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 @@ -177,24 +217,10 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { inline unsigned hashValue() const { return d()->hashValue(); } - uint asArrayIndex() const { - if (subtype() >= Heap::String::StringType_Unknown) - d()->createHashValue(); - Q_ASSERT(d()->subtype < Heap::String::StringType_Complex); - if (subtype() == Heap::String::StringType_ArrayIndex) - return d()->stringHash; - return UINT_MAX; - } uint toUInt(bool *ok) const; - void makeIdentifier() const { - if (d()->identifier) - return; - makeIdentifierImpl(); - } - // slow path - Q_NEVER_INLINE void makeIdentifierImpl() const; + Q_NEVER_INLINE void createPropertyKeyImpl() const; static uint createHashValue(const QChar *ch, int length, uint *subtype) { @@ -210,11 +236,9 @@ 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); + static bool virtualIsEqualTo(Managed *that, Managed *o); + static qint64 virtualGetLength(const Managed *m); #endif public: @@ -254,7 +278,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 +288,7 @@ public: } if (subtype) - *subtype = Heap::String::StringType_Regular; + *subtype = (toUInt(ch) == '@') ? Heap::StringOrSymbol::StringType_Symbol : Heap::StringOrSymbol::StringType_Regular; return h; } }; @@ -280,9 +304,22 @@ struct ComplexString : String { } }; +inline +void StringOrSymbol::createPropertyKey() const { + if (d()->identifier.isValid()) + return; + Q_ASSERT(isString()); + static_cast<const String *>(this)->createPropertyKeyImpl(); +} + +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..f2d0d52013 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> @@ -95,19 +97,19 @@ uint Heap::StringObject::length() const return string->length(); } -bool StringObject::deleteIndexedProperty(Managed *m, uint index) +bool StringObject::virtualDeleteProperty(Managed *m, PropertyKey id) { - ExecutionEngine *v4 = static_cast<StringObject *>(m)->engine(); - Scope scope(v4); - Scoped<StringObject> o(scope, m->as<StringObject>()); - Q_ASSERT(!!o); - - if (index < static_cast<uint>(o->d()->string->toQString().length())) - return false; - return true; + Q_ASSERT(m->as<StringObject>()); + if (id.isArrayIndex()) { + StringObject *o = static_cast<StringObject *>(m); + uint index = id.asArrayIndex(); + if (index < static_cast<uint>(o->d()->string->toQString().length())) + return false; + } + return Object::virtualDeleteProperty(m, id); } -void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) +void StringObject::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) { name->setM(nullptr); StringObject *s = static_cast<StringObject *>(m); @@ -116,9 +118,8 @@ void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, while (it->arrayIndex < slen) { *index = it->arrayIndex; ++it->arrayIndex; - PropertyAttributes a; Property pd; - s->getOwnProperty(*index, &a, &pd); + PropertyAttributes a = s->getOwnProperty(PropertyKey::fromArrayIndex(*index), &pd); if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { *attrs = a; p->copy(&pd, a); @@ -133,7 +134,27 @@ void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, } } - return Object::advanceIterator(m, it, name, index, p, attrs); + return Object::virtualAdvanceIterator(m, it, name, index, p, attrs); +} + +PropertyAttributes StringObject::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p) +{ + PropertyAttributes attributes = Object::virtualGetOwnProperty(m, id, p); + if (attributes != Attr_Invalid) + return attributes; + + Object *o = static_cast<Object *>(m); + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + if (o->isStringObject()) { + if (index >= static_cast<const StringObject *>(m)->length()) + return Attr_Invalid; + if (p) + p->value = static_cast<StringObject *>(o)->getIndex(index); + return Attr_NotConfigurable|Attr_NotWritable; + } + } + return Attr_Invalid; } DEFINE_OBJECT_VTABLE(StringCtor); @@ -143,7 +164,7 @@ void Heap::StringCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("String")); } -ReturnedValue StringCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue StringCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { ExecutionEngine *v4 = static_cast<const Object *>(f)->engine(); Scope scope(v4); @@ -152,16 +173,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) +ReturnedValue StringCtor::virtualCall(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 +232,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->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(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 +256,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 +272,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 +345,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 +396,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 +420,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 +442,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 +470,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)) @@ -418,7 +514,7 @@ ReturnedValue StringPrototype::method_match(const FunctionObject *b, const Value Scoped<RegExpObject> that(scope, argc ? argv[0] : Primitive::undefinedValue()); if (!that) { // convert args[0] to a regexp - that = RegExpCtor::callAsConstructor(b, argv, argc); + that = RegExpCtor::virtualCallAsConstructor(b, argv, argc, b); if (v4->hasException) return Encode::undefined(); } @@ -455,6 +551,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 +982,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 +1096,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 +1116,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..2d37e36b34 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); @@ -96,18 +98,22 @@ struct StringObject: Object { return d()->length(); } - static bool deleteIndexedProperty(Managed *m, uint index); - + using Object::getOwnProperty; protected: - static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs); + static bool virtualDeleteProperty(Managed *m, PropertyKey id); + static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs); + static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p); }; struct StringCtor: FunctionObject { V4_OBJECT2(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 virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(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 +124,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 +132,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 +147,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..bdefe1eb9e --- /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 = PropertyKey::fromStringOrSymbol(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::virtualCall(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.propertyKey()); + 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..46fa2979f8 --- /dev/null +++ b/src/qml/jsruntime/qv4symbol_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** 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 virtualCall(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 virtualPut(Managed *, PropertyKey, const Value &, Value *) { return false; } +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index ea1532b8ce..cb9cdd8df5 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); @@ -209,16 +214,19 @@ void Heap::TypedArrayCtor::init(QV4::ExecutionContext *scope, TypedArray::Type t type = t; } -ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) { Scope scope(f->engine()); const TypedArrayCtor *that = static_cast<const TypedArrayCtor *>(f); 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(); @@ -325,7 +336,7 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V char *b = newBuffer->d()->data->data(); ScopedValue val(scope); while (idx < l) { - val = o->getIndexed(idx); + val = o->get(idx); array->d()->type->write(scope.engine, b, 0, val); 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::virtualCall(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,13 +362,17 @@ 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) +ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { + if (!id.isArrayIndex()) + return Object::virtualGet(m, id, receiver, hasProperty); + + uint index = id.asArrayIndex(); Scope scope(static_cast<const Object *>(m)->engine()); Scoped<TypedArray> a(scope, static_cast<const TypedArray *>(m)); @@ -373,8 +388,12 @@ ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProp return a->d()->type->read(a->d()->buffer->data->data(), byteOffset); } -bool TypedArray::putIndexed(Managed *m, uint index, const Value &value) +bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { + if (!id.isArrayIndex()) + return Object::virtualPut(m, id, value, receiver); + + uint index = id.asArrayIndex(); ExecutionEngine *v4 = static_cast<Object *>(m)->engine(); if (v4->hasException) return false; @@ -395,21 +414,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->setPrototypeOf(engine->intrinsicTypedArrayCtor()); + + setPrototypeOf(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 +435,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 +445,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 +455,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 +465,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); @@ -487,7 +539,7 @@ ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Val char *b = buffer->d()->data->data() + a->d()->byteOffset + offset*elementSize; ScopedValue val(scope); while (idx < l) { - val = o->getIndexed(idx); + val = o->get(idx); a->d()->type->write(scope.engine, b, 0, val); if (scope.engine->hasException) RETURN_UNDEFINED(); @@ -538,7 +590,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 +630,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::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *) +{ + return f->engine()->throwTypeError(); +} + +ReturnedValue IntrinsicTypedArrayCtor::virtualCall(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..967d3235b2 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; @@ -132,34 +138,56 @@ struct Q_QML_PRIVATE_EXPORT TypedArray : Object Heap::TypedArray::Type arrayType() const { return static_cast<Heap::TypedArray::Type>(d()->arrayType); } + using Object::get; - static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - static bool putIndexed(Managed *m, uint index, const Value &value); + static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); +}; + +struct IntrinsicTypedArrayCtor: FunctionObject +{ + V4_OBJECT2(IntrinsicTypedArrayCtor, FunctionObject) + + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct TypedArrayCtor: FunctionObject { V4_OBJECT2(TypedArrayCtor, 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); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; - -struct 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/qv4util_p.h b/src/qml/jsruntime/qv4util_p.h index 2669a3e4bf..073832937d 100644 --- a/src/qml/jsruntime/qv4util_p.h +++ b/src/qml/jsruntime/qv4util_p.h @@ -59,26 +59,6 @@ QT_BEGIN_NAMESPACE namespace QV4 { -template <typename T> -struct TemporaryAssignment -{ - TemporaryAssignment(T &var, const T& temporaryValue) - : variable(var) - , savedValue(var) - { - variable = temporaryValue; - } - ~TemporaryAssignment() - { - variable = savedValue; - } - T &variable; - T savedValue; -private: - TemporaryAssignment(const TemporaryAssignment<T>&); - TemporaryAssignment operator=(const TemporaryAssignment<T>&); -}; - #if !defined(BROKEN_STD_VECTOR_BOOL_OR_BROKEN_STD_FIND) // Sanity: class BitVector diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp index 0d4711df3c..cbc153bb86 100644 --- a/src/qml/jsruntime/qv4value.cpp +++ b/src/qml/jsruntime/qv4value.cpp @@ -39,7 +39,9 @@ #include <qv4engine_p.h> #include <qv4runtime_p.h> #include <qv4string_p.h> +#include <qv4propertykey_p.h> #ifndef V4_BOOTSTRAP +#include <qv4symbol_p.h> #include <qv4object_p.h> #include <qv4objectproto_p.h> #include <private/qv4mm_p.h> @@ -84,7 +86,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 +109,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 +152,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 +206,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 +229,25 @@ QString Value::toQString() const } } // switch } + +QV4::PropertyKey Value::toPropertyKey(ExecutionEngine *e) const +{ + if (isInteger() && int_32() >= 0) + return PropertyKey::fromArrayIndex(static_cast<uint>(int_32())); + if (isStringOrSymbol()) { + Scope scope(e); + ScopedStringOrSymbol s(scope, this); + return s->toPropertyKey(); + } + Scope scope(e); + ScopedValue v(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT)); + if (!v->isStringOrSymbol()) + v = v->toString(e); + if (e->hasException) + return PropertyKey::invalid(); + ScopedStringOrSymbol s(scope, v); + return s->toPropertyKey(); +} #endif // V4_BOOTSTRAP bool Value::sameValue(Value other) const { @@ -235,6 +266,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..6a6df3eb6d 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() { @@ -338,14 +334,17 @@ public: return true; if (isDouble()) { double d = doubleValue(); - int i = (int)d; - if (i == d && !(d == 0 && std::signbit(d))) { - setInt_32(i); + if (isInt32(d)) { + setInt_32(int(d)); return true; } } return false; } + QML_NEARLY_ALWAYS_INLINE static bool isInt32(double d) { + int i = int(d); + return (i == d && !(d == 0 && std::signbit(d))); + } double asDouble() const { if (tag() == quint32(ValueTypeInternal::Integer)) return int_32(); @@ -362,7 +361,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 +397,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 +418,8 @@ public: return reinterpret_cast<Heap::String *>(m()); return toString(e, *this); } + QV4::PropertyKey toPropertyKey(ExecutionEngine *e) const; + static Heap::String *toString(ExecutionEngine *e, Value val); Heap::Object *toObject(ExecutionEngine *e) const { if (isObject()) @@ -428,11 +441,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); @@ -455,8 +468,6 @@ public: return static_cast<const T *>(managed()); } - inline uint asArrayIndex() const; - inline bool asArrayIndex(uint &idx) const; #ifndef V4_BOOTSTRAP uint asArrayLength(bool *ok) const; #endif @@ -465,8 +476,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 +512,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 @@ -528,43 +554,6 @@ inline double Value::toNumber() const return toNumberImpl(); } - -#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) - return (uint)int_32(); - if (!isDouble()) - return UINT_MAX; -#endif - double d = doubleValue(); - uint idx = (uint)d; - if (idx != d) - return UINT_MAX; - return idx; -} - -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; - } - double d = doubleValue(); - idx = (uint)d; - return (idx == d && idx != UINT_MAX); -} -#endif - inline ReturnedValue Heap::Base::asReturnedValue() const { @@ -576,7 +565,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 +590,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; } @@ -755,7 +736,7 @@ struct Encode { } static ReturnedValue smallestNumber(double d) { - if (static_cast<int>(d) == d && !(d == 0. && std::signbit(d))) + if (Value::isInt32(d)) return Encode(static_cast<int>(d)); else return Encode(d); @@ -788,6 +769,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 +837,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 +890,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/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp index bee17e0390..ef0877dbd0 100644 --- a/src/qml/jsruntime/qv4variantobject.cpp +++ b/src/qml/jsruntime/qv4variantobject.cpp @@ -70,7 +70,7 @@ bool VariantObject::Data::isScarce() const return t == QVariant::Pixmap || t == QVariant::Image; } -bool VariantObject::isEqualTo(Managed *m, Managed *other) +bool VariantObject::virtualIsEqualTo(Managed *m, Managed *other) { Q_ASSERT(m->as<QV4::VariantObject>()); QV4::VariantObject *lv = static_cast<QV4::VariantObject *>(m); diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h index 62fa7ff9a8..78e0a5373a 100644 --- a/src/qml/jsruntime/qv4variantobject_p.h +++ b/src/qml/jsruntime/qv4variantobject_p.h @@ -99,7 +99,8 @@ struct VariantObject : Object void addVmePropertyReference() const; void removeVmePropertyReference() const; - static bool isEqualTo(Managed *m, Managed *other); +protected: + static bool virtualIsEqualTo(Managed *m, Managed *other); }; struct VariantPrototype : VariantObject diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index a1f5b01fa9..53e5632eff 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 @@ -327,7 +328,7 @@ static struct InstrCount { #ifdef MOTH_COMPUTED_GOTO #define MOTH_END_INSTR(instr) \ - MOTH_DISPATCH() \ + MOTH_DISPATCH_SINGLE() \ } #else // !MOTH_COMPUTED_GOTO #define MOTH_END_INSTR(instr) \ @@ -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) \ @@ -500,82 +412,46 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs) } \ } while (false) -QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObject, const QV4::Value *argv, int argc) +ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) { qt_v4ResolvePendingBreakpointsHook(); - ExecutionEngine *engine; - QV4::Value *stack; - CppStackFrame frame; - frame.originalArguments = argv; - frame.originalArgumentsCount = argc; - Function *function; - - { - Heap::ExecutionContext *scope; - - quintptr d = reinterpret_cast<quintptr>(fo); - if (d & 0x1) { - // we don't have a FunctionObject, but a ExecData - ExecData *data = reinterpret_cast<ExecData *>(d - 1); - function = data->function; - scope = data->scope->d(); - fo = nullptr; - } else { - function = fo->function(); - scope = fo->scope(); - } - - engine = function->internalClass->engine; - - stack = engine->jsStackTop; - CallData *callData = reinterpret_cast<CallData *>(stack); - callData->function = fo ? fo->asReturnedValue() : Encode::undefined(); - callData->context = scope; - callData->accumulator = Encode::undefined(); - callData->thisObject = thisObject ? *thisObject : Primitive::undefinedValue(); - if (argc > int(function->nFormals)) - argc = int(function->nFormals); - callData->setArgc(argc); - - int jsStackFrameSize = offsetof(CallData, args)/sizeof(Value) + function->compiledFunction->nRegisters; - engine->jsStackTop += jsStackFrameSize; - memcpy(callData->args, argv, argc*sizeof(Value)); - for (Value *v = callData->args + argc; v < engine->jsStackTop; ++v) - *v = Encode::undefined(); - - frame.parent = engine->currentStackFrame; - frame.v4Function = function; - frame.instructionPointer = 0; - frame.jsFrame = callData; - engine->currentStackFrame = &frame; - } CHECK_STACK_LIMITS(engine); + Function *function = frame->v4Function; 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) { - if (engine->canJIT(function)) - QV4::JIT::BaselineJIT(function).generate(); - else - ++function->interpreterCallCount; + if (debugger == nullptr) { + if (function->jittedCode == nullptr) { + if (engine->canJIT(function)) + QV4::JIT::BaselineJIT(function).generate(); + else + ++function->interpreterCallCount; + } + if (function->jittedCode != nullptr) + return function->jittedCode(frame, engine); } #endif // V4_ENABLE_JIT + // interpreter if (debugger) debugger->enteringFunction(); - if (function->jittedCode != nullptr && debugger == nullptr) { - acc = function->jittedCode(&frame, engine); - } else { - // interpreter - const uchar *code = function->codeData; - const uchar *codeStart = code; + ReturnedValue result = interpret(frame, engine, function->codeData); + + if (debugger) + debugger->leavingFunction(result); + + return result; +} + +QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, 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); MOTH_JUMP_TABLE; @@ -629,12 +505,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,58 +567,37 @@ 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(); STORE_ACC(); - if (!Runtime::method_storeElement(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator) && function->isStrict()) - engine->throwTypeError(); + Runtime::method_storeElement(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreElement) 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(); - QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; - acc = l->getter(l, engine, STACK_VALUE(base)); - CHECK_EXCEPTION; - MOTH_END_INSTR(GetLookup) - - MOTH_BEGIN_INSTR(GetLookupA) - STORE_IP(); STORE_ACC(); QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; acc = l->getter(l, engine, accumulator); CHECK_EXCEPTION; - MOTH_END_INSTR(GetLookupA) + MOTH_END_INSTR(GetLookup) MOTH_BEGIN_INSTR(StoreProperty) STORE_IP(); STORE_ACC(); - if (!Runtime::method_storeProperty(engine, STACK_VALUE(base), name, accumulator) && function->isStrict()) - engine->throwTypeError(); + Runtime::method_storeProperty(engine, STACK_VALUE(base), name, accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreProperty) @@ -753,6 +610,20 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj CHECK_EXCEPTION; MOTH_END_INSTR(SetLookup) + MOTH_BEGIN_INSTR(LoadSuperProperty) + STORE_IP(); + STORE_ACC(); + acc = Runtime::method_loadSuperProperty(engine, STACK_VALUE(property)); + CHECK_EXCEPTION; + MOTH_END_INSTR(LoadSuperProperty) + + MOTH_BEGIN_INSTR(StoreSuperProperty) + STORE_IP(); + STORE_ACC(); + Runtime::method_storeSuperProperty(engine, STACK_VALUE(property), accumulator); + CHECK_EXCEPTION; + MOTH_END_INSTR(StoreSuperProperty) + MOTH_BEGIN_INSTR(StoreScopeObjectProperty) STORE_ACC(); Runtime::method_storeQmlScopeObjectProperty(engine, STACK_VALUE(base), propertyIndex, accumulator); @@ -784,14 +655,33 @@ 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); + Value undef = Primitive::undefinedValue(); + acc = static_cast<const FunctionObject &>(func).call(&undef, stack + argv, argc); CHECK_EXCEPTION; MOTH_END_INSTR(CallValue) @@ -809,7 +699,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 +742,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), ACC, stack + argv, argc); + CHECK_EXCEPTION; + MOTH_END_INSTR(Construct) + + MOTH_BEGIN_INSTR(ConstructWithSpread) + STORE_IP(); + acc = Runtime::method_constructWithSpread(engine, STACK_VALUE(func), ACC, 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,71 +794,90 @@ 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) - stack[CallData::Context] = ExecutionContext::newCallContext(&frame); + stack[CallData::Context] = ExecutionContext::newCallContext(frame); MOTH_END_INSTR(CreateCallContext) 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 +885,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,9 +913,13 @@ 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(CreateClass) + acc = Runtime::method_createClass(engine, classIndex, STACK_VALUE(heritage), stack + computedNames); + MOTH_END_INSTR(CreateClass) + MOTH_BEGIN_INSTR(CreateMappedArgumentsObject) acc = Runtime::method_createMappedArgumentsObject(engine); MOTH_END_INSTR(CreateMappedArgumentsObject) @@ -981,6 +928,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 +944,20 @@ 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(LoadSuperConstructor) + const Value *f = &stack[CallData::Function]; + if (!f->isFunctionObject()) { + engine->throwTypeError(); + } else { + acc = static_cast<const Object *>(f)->getPrototypeOf()->asReturnedValue(); + } CHECK_EXCEPTION; - MOTH_END_INSTR(Construct) + MOTH_END_INSTR(LoadSuperConstructor) + + MOTH_BEGIN_INSTR(ToObject) + acc = ACC.toObject(engine)->asReturnedValue(); + CHECK_EXCEPTION; + MOTH_END_INSTR(ToObject) MOTH_BEGIN_INSTR(Jump) code += offset; @@ -1023,6 +983,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 +1029,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 +1040,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 +1124,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 +1213,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 +1315,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 +1333,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..8a76e60f20 100644 --- a/src/qml/jsruntime/qv4vme_moth_p.h +++ b/src/qml/jsruntime/qv4vme_moth_p.h @@ -65,13 +65,8 @@ public: QV4::Function *function; const QV4::ExecutionContext *scope; }; - static inline - QV4::ReturnedValue exec(Function *v4Function, const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) { - ExecData data{v4Function, context}; - quintptr d = reinterpret_cast<quintptr>(&data) | 0x1; - 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 exec(CppStackFrame *frame, ExecutionEngine *engine); + static QV4::ReturnedValue interpret(CppStackFrame *frame, ExecutionEngine *engine, const char *codeEntry); }; } // namespace Moth diff --git a/src/qml/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h new file mode 100644 index 0000000000..b4e868d45d --- /dev/null +++ b/src/qml/jsruntime/qv4vtable_p.h @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** 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 QV4VTABLE_P_H +#define QV4VTABLE_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 "qv4global_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct VTable +{ + typedef void (*Destroy)(Heap::Base *); + typedef void (*MarkObjects)(Heap::Base *, MarkStack *markStack); + typedef bool (*IsEqualTo)(Managed *m, Managed *other); + + typedef ReturnedValue (*Get)(const Managed *, PropertyKey id, const Value *receiver, bool *hasProperty); + typedef bool (*Put)(Managed *, PropertyKey id, const Value &value, Value *receiver); + typedef bool (*DeleteProperty)(Managed *m, PropertyKey id); + typedef bool (*HasProperty)(const Managed *m, PropertyKey id); + typedef PropertyAttributes (*GetOwnProperty)(Managed *m, PropertyKey id, Property *p); + typedef bool (*DefineOwnProperty)(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs); + typedef bool (*IsExtensible)(const Managed *); + typedef bool (*PreventExtensions)(Managed *); + typedef Heap::Object *(*GetPrototypeOf)(const Managed *); + typedef bool (*SetPrototypeOf)(Managed *, const Object *); + typedef qint64 (*GetLength)(const Managed *m); + typedef void (*AdvanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); + typedef ReturnedValue (*InstanceOf)(const Object *typeObject, const Value &var); + + typedef ReturnedValue (*Call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + typedef ReturnedValue (*CallAsConstructor)(const FunctionObject *, const Value *argv, int argc, const Value *newTarget); + + const VTable * const parent; + quint32 inlinePropertyOffset : 16; + quint32 nInlineProperties : 16; + quint8 isExecutionContext; + quint8 isString; + quint8 isObject; + quint8 isFunctionObject; + quint8 isErrorObject; + quint8 isArrayData; + quint8 isStringOrSymbol; + quint8 type; + quint8 unused[4]; + const char *className; + + Destroy destroy; + MarkObjects markObjects; + IsEqualTo isEqualTo; + + Get get; + Put put; + DeleteProperty deleteProperty; + HasProperty hasProperty; + GetOwnProperty getOwnProperty; + DefineOwnProperty defineOwnProperty; + IsExtensible isExtensible; + PreventExtensions preventExtensions; + GetPrototypeOf getPrototypeOf; + SetPrototypeOf setPrototypeOf; + GetLength getLength; + AdvanceIterator advanceIterator; + InstanceOf instanceOf; + + Call call; + CallAsConstructor callAsConstructor; +}; + + +struct VTableBase { +protected: + static constexpr VTable::Destroy virtualDestroy = nullptr; + static constexpr VTable::IsEqualTo virtualIsEqualTo = nullptr; + + static constexpr VTable::Get virtualGet = nullptr; + static constexpr VTable::Put virtualPut = nullptr; + static constexpr VTable::DeleteProperty virtualDeleteProperty = nullptr; + static constexpr VTable::HasProperty virtualHasProperty = nullptr; + static constexpr VTable::GetOwnProperty virtualGetOwnProperty = nullptr; + static constexpr VTable::DefineOwnProperty virtualDefineOwnProperty = nullptr; + static constexpr VTable::IsExtensible virtualIsExtensible = nullptr; + static constexpr VTable::PreventExtensions virtualPreventExtensions = nullptr; + static constexpr VTable::GetPrototypeOf virtualGetPrototypeOf = nullptr; + static constexpr VTable::SetPrototypeOf virtualSetPrototypeOf = nullptr; + static constexpr VTable::GetLength virtualGetLength = nullptr; + static constexpr VTable::AdvanceIterator virtualAdvanceIterator = nullptr; + static constexpr VTable::InstanceOf virtualInstanceOf = nullptr; + + static constexpr VTable::Call virtualCall = nullptr; + static constexpr VTable::CallAsConstructor virtualCallAsConstructor = nullptr; +}; + +#define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \ +{ \ + parentVTable, \ + (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ + (sizeof(classname::Data) + (classname::NInlineProperties*sizeof(QV4::Value)) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \ + - (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ + classname::IsExecutionContext, \ + classname::IsString, \ + classname::IsObject, \ + classname::IsFunctionObject, \ + classname::IsErrorObject, \ + classname::IsArrayData, \ + classname::IsStringOrSymbol, \ + classname::MyType, \ + { 0, 0, 0, 0 }, \ + #classname, \ + \ + classname::virtualDestroy, \ + classname::Data::markObjects, \ + classname::virtualIsEqualTo, \ + \ + classname::virtualGet, \ + classname::virtualPut, \ + classname::virtualDeleteProperty, \ + classname::virtualHasProperty, \ + classname::virtualGetOwnProperty, \ + classname::virtualDefineOwnProperty, \ + classname::virtualIsExtensible, \ + classname::virtualPreventExtensions, \ + classname::virtualGetPrototypeOf, \ + classname::virtualSetPrototypeOf, \ + classname::virtualGetLength, \ + classname::virtualAdvanceIterator, \ + classname::virtualInstanceOf, \ + \ + classname::virtualCall, \ + classname::virtualCallAsConstructor, \ +} + +#define DEFINE_MANAGED_VTABLE(classname) \ +const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, 0) + +#define V4_OBJECT2(DataClass, superClass) \ + private: \ + DataClass() Q_DECL_EQ_DELETE; \ + Q_DISABLE_COPY(DataClass) \ + public: \ + Q_MANAGED_CHECK \ + typedef QV4::Heap::DataClass Data; \ + typedef superClass SuperClass; \ + static const QV4::VTable static_vtbl; \ + static inline const QV4::VTable *staticVTable() { return &static_vtbl; } \ + V4_MANAGED_SIZE_TEST \ + QV4::Heap::DataClass *d_unchecked() const { return static_cast<QV4::Heap::DataClass *>(m()); } \ + QV4::Heap::DataClass *d() const { \ + QV4::Heap::DataClass *dptr = d_unchecked(); \ + dptr->_checkIsInitialized(); \ + return dptr; \ + } \ + Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value); + +#define V4_PROTOTYPE(p) \ + static QV4::Object *defaultPrototype(QV4::ExecutionEngine *e) \ + { return e->p(); } + + +#define DEFINE_OBJECT_VTABLE_BASE(classname) \ + const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, (std::is_same<classname::SuperClass, Object>::value) ? nullptr : &classname::SuperClass::static_vtbl) + +#define DEFINE_OBJECT_VTABLE(classname) \ +DEFINE_OBJECT_VTABLE_BASE(classname) + +#define DEFINE_OBJECT_TEMPLATE_VTABLE(classname) \ +template<> DEFINE_OBJECT_VTABLE_BASE(classname) + +} + +QT_END_NAMESPACE + +#endif |