diff options
Diffstat (limited to 'src/qml/jsruntime')
64 files changed, 1567 insertions, 1300 deletions
diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 6ab838c387..0905c2828a 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -48,32 +48,33 @@ DEFINE_OBJECT_VTABLE(ArgumentsObject); void Heap::ArgumentsObject::init(QV4::CallContext *context) { + ExecutionEngine *v4 = internalClass->engine; + Object::init(); fullyCreated = false; - this->context = context->d(); + this->context.set(v4, context->d()); Q_ASSERT(vtable() == QV4::ArgumentsObject::staticVTable()); - ExecutionEngine *v4 = context->engine(); Scope scope(v4); Scoped<QV4::ArgumentsObject> args(scope, this); if (context->d()->strictMode) { Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(v4->id_callee())); Q_ASSERT(CallerPropertyIndex == args->internalClass()->find(v4->id_caller())); - *args->propertyData(CalleePropertyIndex + QV4::Object::GetterOffset) = v4->thrower(); - *args->propertyData(CalleePropertyIndex + QV4::Object::SetterOffset) = v4->thrower(); - *args->propertyData(CallerPropertyIndex + QV4::Object::GetterOffset) = v4->thrower(); - *args->propertyData(CallerPropertyIndex + QV4::Object::SetterOffset) = v4->thrower(); + args->setProperty(CalleePropertyIndex + QV4::Object::GetterOffset, *v4->thrower()); + args->setProperty(CalleePropertyIndex + QV4::Object::SetterOffset, *v4->thrower()); + args->setProperty(CallerPropertyIndex + QV4::Object::GetterOffset, *v4->thrower()); + args->setProperty(CallerPropertyIndex + QV4::Object::SetterOffset, *v4->thrower()); args->arrayReserve(context->argc()); args->arrayPut(0, context->args(), context->argc()); args->d()->fullyCreated = true; } else { Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(v4->id_callee())); - *args->propertyData(CalleePropertyIndex) = context->d()->function->asReturnedValue(); + args->setProperty(CalleePropertyIndex, context->d()->function); } Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(v4->id_length())); - *args->propertyData(LengthPropertyIndex) = Primitive::fromInt32(context->d()->callData->argc); + args->setProperty(LengthPropertyIndex, Primitive::fromInt32(context->d()->callData->argc)); } void ArgumentsObject::fullyCreate() @@ -90,9 +91,9 @@ void ArgumentsObject::fullyCreate() Scoped<MemberData> md(scope, d()->mappedArguments); if (numAccessors) { - d()->mappedArguments = md->allocate(scope.engine, numAccessors); + d()->mappedArguments.set(scope.engine, md->allocate(scope.engine, numAccessors)); for (uint i = 0; i < numAccessors; ++i) { - d()->mappedArguments->data[i] = context()->callData->args[i]; + d()->mappedArguments->values.set(scope.engine, i, context()->callData->args[i]); arraySet(i, scope.engine->argumentsAccessors + i, Attr_Accessor); } } @@ -108,22 +109,22 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con fullyCreate(); Scope scope(engine); - Property *pd = arrayData() ? arrayData()->getProperty(index) : 0; ScopedProperty map(scope); PropertyAttributes mapAttrs; + uint numAccessors = qMin(context()->formalParameterCount(), static_cast<uint>(context()->callData->argc)); bool isMapped = false; - uint numAccessors = qMin((int)context()->formalParameterCount(), context()->callData->argc); - if (pd && index < (uint)numAccessors) - isMapped = arrayData()->attributes(index).isAccessor() && - pd->getter() == scope.engine->argumentsAccessors[index].getter(); + if (arrayData() && index < numAccessors && + arrayData()->attributes(index).isAccessor() && + arrayData()->get(index) == scope.engine->argumentsAccessors[index].getter()->asReturnedValue()) + isMapped = true; if (isMapped) { Q_ASSERT(arrayData()); mapAttrs = arrayData()->attributes(index); - map->copy(pd, mapAttrs); + arrayData()->getProperty(index, map, &mapAttrs); setArrayAttributes(index, Attr_Data); - pd = arrayData()->getProperty(index); - pd->value = d()->mappedArguments->data[index]; + ArrayData::Index arrayIndex{ arrayData(), arrayData()->mappedIndex(index) }; + arrayIndex.set(scope.engine, d()->mappedArguments->values[index]); } bool strict = engine->current->strictMode; @@ -141,8 +142,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con if (attrs.isWritable()) { setArrayAttributes(index, mapAttrs); - pd = arrayData()->getProperty(index); - pd->copy(map, mapAttrs); + arrayData()->setProperty(engine, index, map); } } @@ -167,18 +167,17 @@ ReturnedValue ArgumentsObject::getIndexed(const Managed *m, uint index, bool *ha return Encode::undefined(); } -void ArgumentsObject::putIndexed(Managed *m, uint index, const Value &value) +bool ArgumentsObject::putIndexed(Managed *m, uint index, const Value &value) { ArgumentsObject *args = static_cast<ArgumentsObject *>(m); if (!args->fullyCreated() && index >= static_cast<uint>(args->context()->callData->argc)) args->fullyCreate(); - if (args->fullyCreated()) { - Object::putIndexed(m, index, value); - return; - } + if (args->fullyCreated()) + return Object::putIndexed(m, index, value); args->context()->callData->args[index] = value; + return true; } bool ArgumentsObject::deleteIndexedProperty(Managed *m, uint index) @@ -237,17 +236,6 @@ void ArgumentsSetterFunction::call(const Managed *setter, Scope &scope, CallData scope.result = Encode::undefined(); } -void ArgumentsObject::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - ArgumentsObject::Data *o = static_cast<ArgumentsObject::Data *>(that); - if (o->context) - o->context->mark(e); - if (o->mappedArguments) - o->mappedArguments->mark(e); - - Object::markObjects(that, e); -} - uint ArgumentsObject::getLength(const Managed *m) { const ArgumentsObject *a = static_cast<const ArgumentsObject *>(m); diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index 0a2ea3b42a..46e1f884e8 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -59,26 +59,35 @@ namespace QV4 { namespace Heap { -struct ArgumentsGetterFunction : FunctionObject { +#define ArgumentsGetterFunctionMembers(class, Member) \ + Member(class, NoMark, uint, index) + +DECLARE_HEAP_OBJECT(ArgumentsGetterFunction, FunctionObject) { + DECLARE_MARK_TABLE(ArgumentsGetterFunction); inline void init(QV4::ExecutionContext *scope, uint index); - uint index; }; -struct ArgumentsSetterFunction : FunctionObject { +#define ArgumentsSetterFunctionMembers(class, Member) \ + Member(class, NoMark, uint, index) + +DECLARE_HEAP_OBJECT(ArgumentsSetterFunction, FunctionObject) { + DECLARE_MARK_TABLE(ArgumentsSetterFunction); inline void init(QV4::ExecutionContext *scope, uint index); - uint index; }; -struct ArgumentsObject : Object { +#define ArgumentsObjectMembers(class, Member) \ + Member(class, Pointer, CallContext *, context) \ + Member(class, Pointer, MemberData *, mappedArguments) \ + Member(class, NoMark, bool, fullyCreated) + +DECLARE_HEAP_OBJECT(ArgumentsObject, Object) { + DECLARE_MARK_TABLE(ArgumentsObject); enum { LengthPropertyIndex = 0, CalleePropertyIndex = 1, CallerPropertyIndex = 3 }; void init(QV4::CallContext *context); - Pointer<CallContext> context; - bool fullyCreated; - Pointer<MemberData> mappedArguments; }; } @@ -128,10 +137,9 @@ struct ArgumentsObject: Object { bool defineOwnProperty(ExecutionEngine *engine, uint index, const Property *desc, PropertyAttributes attrs); static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - static void putIndexed(Managed *m, uint index, const Value &value); + 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 void markObjects(Heap::Base *that, ExecutionEngine *e); static uint getLength(const Managed *m); void fullyCreate(); diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index 6d1fb62e0a..df9884d84a 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -52,6 +52,7 @@ const QV4::VTable QV4::ArrayData::static_vtbl = { 0, 0, 0, + 0, QV4::ArrayData::IsExecutionContext, QV4::ArrayData::IsString, QV4::ArrayData::IsObject, @@ -130,7 +131,7 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt if (d->type() < Heap::ArrayData::Sparse) { offset = d->d()->offset; - toCopy = d->d()->len; + toCopy = d->d()->values.size; } else { toCopy = d->alloc(); } @@ -151,7 +152,7 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt Heap::SimpleArrayData *n = scope.engine->memoryManager->allocManaged<SimpleArrayData>(size); n->init(); n->offset = 0; - n->len = d ? d->d()->len : 0; + n->values.size = d ? d->d()->values.size : 0; newData = n; } else { Heap::SparseArrayData *n = scope.engine->memoryManager->allocManaged<SparseArrayData>(size); @@ -160,7 +161,7 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt } newData->setAlloc(alloc); newData->setType(newType); - newData->setAttrs(enforceAttributes ? reinterpret_cast<PropertyAttributes *>(newData->d()->arrayData + alloc) : 0); + newData->setAttrs(enforceAttributes ? reinterpret_cast<PropertyAttributes *>(newData->d()->values.values + alloc) : 0); o->setArrayData(newData); if (d) { @@ -172,12 +173,14 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt newData->attrs()[i] = Attr_Data; } - if (toCopy > d->d()->alloc - offset) { - uint copyFromStart = toCopy - (d->d()->alloc - offset); - memcpy(newData->d()->arrayData + toCopy - copyFromStart, d->d()->arrayData, sizeof(Value)*copyFromStart); + if (toCopy > d->d()->values.alloc - offset) { + uint copyFromStart = toCopy - (d->d()->values.alloc - offset); + // no write barrier required here + memcpy(newData->d()->values.values + toCopy - copyFromStart, d->d()->values.values, sizeof(Value)*copyFromStart); toCopy -= copyFromStart; } - memcpy(newData->d()->arrayData, d->d()->arrayData + offset, sizeof(Value)*toCopy); + // no write barrier required here + memcpy(newData->d()->values.values, d->d()->values.values + offset, sizeof(Value)*toCopy); } if (newType != Heap::ArrayData::Sparse) @@ -197,22 +200,22 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt lastFree = &sparse->freeList; storeValue(lastFree, 0); for (uint i = 0; i < toCopy; ++i) { - if (!sparse->arrayData[i].isEmpty()) { + if (!sparse->values[i].isEmpty()) { SparseArrayNode *n = sparse->sparse->insert(i); n->value = i; } else { storeValue(lastFree, i); - sparse->arrayData[i].setEmpty(); - lastFree = &sparse->arrayData[i].rawValueRef(); + sparse->values.values[i].setEmpty(); + lastFree = &sparse->values.values[i].rawValueRef(); } } } - if (toCopy < sparse->alloc) { - for (uint i = toCopy; i < sparse->alloc; ++i) { + if (toCopy < sparse->values.alloc) { + for (uint i = toCopy; i < sparse->values.alloc; ++i) { storeValue(lastFree, i); - sparse->arrayData[i].setEmpty(); - lastFree = &sparse->arrayData[i].rawValueRef(); + sparse->values.values[i].setEmpty(); + lastFree = &sparse->values.values[i].rawValueRef(); } storeValue(lastFree, UINT_MAX); } @@ -235,24 +238,10 @@ void ArrayData::ensureAttributes(Object *o) ArrayData::realloc(o, Heap::ArrayData::Simple, 0, true); } - -void SimpleArrayData::markObjects(Heap::Base *d, ExecutionEngine *e) -{ - Heap::SimpleArrayData *dd = static_cast<Heap::SimpleArrayData *>(d); - uint end = dd->offset + dd->len; - if (end > dd->alloc) { - for (uint i = 0; i < end - dd->alloc; ++i) - dd->arrayData[i].mark(e); - end = dd->alloc; - } - for (uint i = dd->offset; i < end; ++i) - dd->arrayData[i].mark(e); -} - ReturnedValue SimpleArrayData::get(const Heap::ArrayData *d, uint index) { const Heap::SimpleArrayData *dd = static_cast<const Heap::SimpleArrayData *>(d); - if (index >= dd->len) + if (index >= dd->values.size) return Primitive::emptyValue().asReturnedValue(); return dd->data(index).asReturnedValue(); } @@ -260,13 +249,13 @@ ReturnedValue SimpleArrayData::get(const Heap::ArrayData *d, uint index) bool SimpleArrayData::put(Object *o, uint index, const Value &value) { Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); - Q_ASSERT(index >= dd->len || !dd->attrs || !dd->attrs[index].isAccessor()); + Q_ASSERT(index >= dd->values.size || !dd->attrs || !dd->attrs[index].isAccessor()); // ### honour attributes - dd->data(index) = value; - if (index >= dd->len) { + dd->setData(o->engine(), index, value); + if (index >= dd->values.size) { if (dd->attrs) dd->attrs[index] = Attr_Data; - dd->len = index + 1; + dd->values.size = index + 1; } return true; } @@ -274,11 +263,11 @@ bool SimpleArrayData::put(Object *o, uint index, const Value &value) bool SimpleArrayData::del(Object *o, uint index) { Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); - if (index >= dd->len) + if (index >= dd->values.size) return true; if (!dd->attrs || dd->attrs[index].isConfigurable()) { - dd->data(index) = Primitive::emptyValue(); + dd->setData(o->engine(), index, Primitive::emptyValue()); if (dd->attrs) dd->attrs[index] = Attr_Data; return true; @@ -297,8 +286,8 @@ void SimpleArrayData::push_front(Object *o, const Value *values, uint n) { Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); Q_ASSERT(!dd->attrs); - if (dd->len + n > dd->alloc) { - realloc(o, Heap::ArrayData::Simple, dd->len + n, false); + if (dd->values.size + n > dd->values.alloc) { + realloc(o, Heap::ArrayData::Simple, dd->values.size + n, false); Q_ASSERT(o->d()->arrayData->type == Heap::ArrayData::Simple); dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } @@ -306,70 +295,71 @@ void SimpleArrayData::push_front(Object *o, const Value *values, uint n) dd->offset -= n; // there is enough space left in front } else { // we need to wrap around, so: - dd->offset = dd->alloc - // start at the back, but subtract: + dd->offset = dd->values.alloc - // start at the back, but subtract: (n - dd->offset); // the number of items we can put in the free space at the start of the allocated array } - dd->len += n; + dd->values.size += n; for (uint i = 0; i < n; ++i) - dd->data(i) = values[i].asReturnedValue(); + dd->setData(o->engine(), i, values[i]); } ReturnedValue SimpleArrayData::pop_front(Object *o) { Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); Q_ASSERT(!dd->attrs); - if (!dd->len) + if (!dd->values.size) return Encode::undefined(); ReturnedValue v = dd->data(0).isEmpty() ? Encode::undefined() : dd->data(0).asReturnedValue(); - dd->offset = (dd->offset + 1) % dd->alloc; - --dd->len; + dd->offset = (dd->offset + 1) % dd->values.alloc; + --dd->values.size; return v; } uint SimpleArrayData::truncate(Object *o, uint newLen) { Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); - if (dd->len < newLen) + if (dd->values.size < newLen) return newLen; if (!dd->attrs) { - dd->len = newLen; + dd->values.size = newLen; return newLen; } - while (dd->len > newLen) { - if (!dd->data(dd->len - 1).isEmpty() && !dd->attrs[dd->len - 1].isConfigurable()) - return dd->len; - --dd->len; + while (dd->values.size > newLen) { + if (!dd->data(dd->values.size - 1).isEmpty() && !dd->attrs[dd->values.size - 1].isConfigurable()) + return dd->values.size; + --dd->values.size; } - return dd->len; + return dd->values.size; } uint SimpleArrayData::length(const Heap::ArrayData *d) { - return d->len; + return d->values.size; } bool SimpleArrayData::putArray(Object *o, uint index, const Value *values, uint n) { Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); - if (index + n > dd->alloc) { + if (index + n > dd->values.alloc) { reallocate(o, index + n + 1, false); dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } - for (uint i = dd->len; i < index; ++i) - dd->data(i) = Primitive::emptyValue(); + QV4::ExecutionEngine *e = o->engine(); + for (uint i = dd->values.size; i < index; ++i) + dd->setData(e, i, Primitive::emptyValue()); for (uint i = 0; i < n; ++i) - dd->data(index + i) = values[i]; - dd->len = qMax(dd->len, index + n); + dd->setData(e, index + i, values[i]); + dd->values.size = qMax(dd->values.size, index + n); return true; } void SparseArrayData::free(Heap::ArrayData *d, uint idx) { Q_ASSERT(d && d->type == Heap::ArrayData::Sparse); - Value *v = d->arrayData + 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->freeList).emptyValue()); @@ -382,15 +372,6 @@ void SparseArrayData::free(Heap::ArrayData *d, uint idx) d->attrs[idx].clear(); } - -void SparseArrayData::markObjects(Heap::Base *d, ExecutionEngine *e) -{ - Heap::SparseArrayData *dd = static_cast<Heap::SparseArrayData *>(d); - uint l = dd->alloc; - for (uint i = 0; i < l; ++i) - dd->arrayData[i].mark(e); -} - Heap::ArrayData *SparseArrayData::reallocate(Object *o, uint n, bool enforceAttributes) { realloc(o, Heap::ArrayData::Sparse, n, enforceAttributes); @@ -406,32 +387,32 @@ uint SparseArrayData::allocate(Object *o, bool doubleSlot) ReturnedValue *last = &dd->freeList; while (1) { if (Value::fromReturnedValue(*last).value() == UINT_MAX) { - reallocate(o, dd->alloc + 2, true); + reallocate(o, dd->values.alloc + 2, true); dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); last = &dd->freeList; Q_ASSERT(Value::fromReturnedValue(*last).value() != UINT_MAX); } - Q_ASSERT(dd->arrayData[Value::fromReturnedValue(*last).value()].value() != Value::fromReturnedValue(*last).value()); - if (dd->arrayData[Value::fromReturnedValue(*last).value()].value() == (Value::fromReturnedValue(*last).value() + 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)) { // found two slots in a row uint idx = Value::fromReturnedValue(*last).emptyValue(); Value lastV = Value::fromReturnedValue(*last); - lastV.setEmpty(dd->arrayData[lastV.emptyValue() + 1].value()); + lastV.setEmpty(dd->values[lastV.emptyValue() + 1].value()); *last = lastV.rawValue(); dd->attrs[idx] = Attr_Accessor; return idx; } - last = &dd->arrayData[Value::fromReturnedValue(*last).value()].rawValueRef(); + last = &dd->values.values[Value::fromReturnedValue(*last).value()].rawValueRef(); } } else { if (Value::fromReturnedValue(dd->freeList).value() == UINT_MAX) { - reallocate(o, dd->alloc + 1, false); + reallocate(o, dd->values.alloc + 1, false); dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } uint idx = Value::fromReturnedValue(dd->freeList).value(); Q_ASSERT(idx != UINT_MAX); - dd->freeList = dd->arrayData[idx].asReturnedValue(); + dd->freeList = dd->values[idx].asReturnedValue(); Q_ASSERT(Value::fromReturnedValue(dd->freeList).isEmpty()); if (dd->attrs) dd->attrs[idx] = Attr_Data; @@ -445,7 +426,7 @@ ReturnedValue SparseArrayData::get(const Heap::ArrayData *d, uint index) index = s->mappedIndex(index); if (index == UINT_MAX) return Primitive::emptyValue().asReturnedValue(); - return s->arrayData[index].asReturnedValue(); + return s->values[index].asReturnedValue(); } bool SparseArrayData::put(Object *o, uint index, const Value &value) @@ -459,7 +440,7 @@ bool SparseArrayData::put(Object *o, uint index, const Value &value) if (n->value == UINT_MAX) n->value = allocate(o); s = o->d()->arrayData.cast<Heap::SparseArrayData>(); - s->arrayData[n->value] = value; + s->setArrayData(o->engine(), n->value, value); if (s->attrs) s->attrs[n->value] = Attr_Data; return true; @@ -474,7 +455,7 @@ bool SparseArrayData::del(Object *o, uint index) return true; uint pidx = n->value; - Q_ASSERT(!dd->arrayData[pidx].isEmpty()); + Q_ASSERT(!dd->values[pidx].isEmpty()); bool isAccessor = false; if (dd->attrs) { @@ -487,11 +468,11 @@ bool SparseArrayData::del(Object *o, uint index) if (isAccessor) { // free up both indices - dd->arrayData[pidx + 1].setEmpty(Value::fromReturnedValue(dd->freeList).emptyValue()); - dd->arrayData[pidx].setEmpty(pidx + 1); + dd->values.values[pidx + 1].setEmpty(Value::fromReturnedValue(dd->freeList).emptyValue()); + dd->values.values[pidx].setEmpty(pidx + 1); } else { Q_ASSERT(dd->type == Heap::ArrayData::Sparse); - dd->arrayData[pidx].setEmpty(Value::fromReturnedValue(dd->freeList).emptyValue()); + dd->values.values[pidx].setEmpty(Value::fromReturnedValue(dd->freeList).emptyValue()); } dd->freeList = Primitive::emptyValue(pidx).asReturnedValue(); @@ -520,10 +501,10 @@ void SparseArrayData::push_front(Object *o, const Value *values, uint n) { Heap::SparseArrayData *d = o->d()->arrayData.cast<Heap::SparseArrayData>(); Q_ASSERT(!d->attrs); - for (int i = n - 1; i >= 0; --i) { + for (int i = static_cast<int>(n) - 1; i >= 0; --i) { uint idx = allocate(o); d = o->d()->arrayData.cast<Heap::SparseArrayData>(); - d->arrayData[idx] = values[i]; + d->setArrayData(o->engine(), idx, values[i]); d->sparse->push_front(idx); } } @@ -535,7 +516,7 @@ ReturnedValue SparseArrayData::pop_front(Object *o) uint idx = d->sparse->pop_front(); ReturnedValue v; if (idx != UINT_MAX) { - v = d->arrayData[idx].asReturnedValue(); + v = d->values[idx].asReturnedValue(); free(o->arrayData(), idx); } else { v = Encode::undefined(); @@ -613,24 +594,24 @@ uint ArrayData::append(Object *obj, ArrayObject *otherObj, uint n) ScopedValue v(scope); for (const SparseArrayNode *it = os->sparse->begin(); it != os->sparse->end(); it = it->nextNode()) { - v = otherObj->getValue(os->arrayData[it->value], other->d()->attrs[it->value]); + v = otherObj->getValue(os->values[it->value], other->d()->attrs[it->value]); obj->arraySet(oldSize + it->key(), v); } } else { for (const SparseArrayNode *it = other->d()->sparse->begin(); it != os->sparse->end(); it = it->nextNode()) - obj->arraySet(oldSize + it->key(), os->arrayData[it->value]); + obj->arraySet(oldSize + it->key(), os->values[it->value]); } } else { Heap::SimpleArrayData *os = static_cast<Heap::SimpleArrayData *>(other->d()); uint toCopy = n; uint chunk = toCopy; - if (chunk > os->alloc - os->offset) - chunk -= os->alloc - os->offset; - obj->arrayPut(oldSize, os->arrayData + os->offset, chunk); + if (chunk > os->values.alloc - os->offset) + chunk -= os->values.alloc - os->offset; + obj->arrayPut(oldSize, os->values.data() + os->offset, chunk); toCopy -= chunk; if (toCopy) - obj->arrayPut(oldSize + chunk, os->arrayData, toCopy); + obj->arrayPut(oldSize + chunk, os->values.data(), toCopy); } return oldSize + n; @@ -640,18 +621,18 @@ void ArrayData::insert(Object *o, uint index, const Value *v, bool isAccessor) { if (!isAccessor && o->d()->arrayData->type != Heap::ArrayData::Sparse) { Heap::SimpleArrayData *d = o->d()->arrayData.cast<Heap::SimpleArrayData>(); - if (index < 0x1000 || index < d->len + (d->len >> 2)) { - if (index >= d->alloc) { + if (index < 0x1000 || index < d->values.size + (d->values.size >> 2)) { + if (index >= d->values.alloc) { o->arrayReserve(index + 1); d = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } - if (index >= d->len) { + if (index >= d->values.size) { // mark possible hole in the array - for (uint i = d->len; i < index; ++i) - d->data(i) = Primitive::emptyValue(); - d->len = index + 1; + for (uint i = d->values.size; i < index; ++i) + d->setData(o->engine(), i, Primitive::emptyValue()); + d->values.size = index + 1; } - d->arrayData[d->mappedIndex(index)] = *v; + d->setData(o->engine(), index, *v); return; } } @@ -662,9 +643,9 @@ void ArrayData::insert(Object *o, uint index, const Value *v, bool isAccessor) if (n->value == UINT_MAX) n->value = SparseArrayData::allocate(o, isAccessor); s = o->d()->arrayData.cast<Heap::SparseArrayData>(); - s->arrayData[n->value] = *v; + s->setArrayData(o->engine(), n->value, *v); if (isAccessor) - s->arrayData[n->value + Object::SetterOffset] = v[Object::SetterOffset]; + s->setArrayData(o->engine(), n->value + Object::SetterOffset, v[Object::SetterOffset]); } @@ -801,7 +782,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c break; PropertyAttributes a = sparse->attrs() ? sparse->attrs()[n->value] : Attr_Data; - d->data(i) = thisObject->getValue(sparse->arrayData()[n->value], a); + d->setData(engine, i, Value::fromReturnedValue(thisObject->getValue(sparse->arrayData()[n->value], a))); d->attrs[i] = a.isAccessor() ? Attr_Data : a; n = n->nextNode(); @@ -811,12 +792,12 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c while (n != sparse->sparse()->end()) { if (n->value >= len) break; - d->data(i) = sparse->arrayData()[n->value]; + d->setData(engine, i, sparse->arrayData()[n->value]); n = n->nextNode(); ++i; } } - d->len = i; + d->values.size = i; if (len > i) len = i; if (n != sparse->sparse()->end()) { @@ -824,7 +805,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c thisObject->initSparseArray(); while (n != sparse->sparse()->end()) { PropertyAttributes a = sparse->attrs() ? sparse->attrs()[n->value] : Attr_Data; - thisObject->arraySet(n->value, reinterpret_cast<Property *>(sparse->arrayData() + n->value), a); + thisObject->arraySet(n->value, reinterpret_cast<const Property *>(sparse->arrayData() + n->value), a); n = n->nextNode(); } @@ -832,8 +813,8 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c } } else { Heap::SimpleArrayData *d = thisObject->d()->arrayData.cast<Heap::SimpleArrayData>(); - if (len > d->len) - len = d->len; + if (len > d->values.size) + len = d->values.size; // sort empty values to the end for (uint i = 0; i < len; i++) { @@ -842,8 +823,8 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c if (!d->data(len).isEmpty()) break; Q_ASSERT(!d->attrs || !d->attrs[len].isAccessor()); - d->data(i) = d->data(len); - d->data(len) = Primitive::emptyValue(); + d->setData(engine, i, d->data(len)); + d->setData(engine, len, Primitive::emptyValue()); } } @@ -854,7 +835,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c ArrayElementLessThan lessThan(engine, thisObject, comparefn); - Value *begin = thisObject->arrayData()->arrayData; + Value *begin = thisObject->arrayData()->values.values; sortHelper(begin, begin + len, *begin, lessThan); #ifdef CHECK_SPARSE_ARRAYS diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h index daf8c36814..e1de2e82e6 100644 --- a/src/qml/jsruntime/qv4arraydata_p.h +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -90,27 +90,31 @@ struct ArrayVTable namespace Heap { -struct ArrayData : public Base { - enum Type { - Simple = 0, - Complex = 1, - Sparse = 2, - Custom = 3 +#define ArrayDataMembers(class, Member) \ + Member(class, NoMark, uint, type) \ + Member(class, NoMark, uint, offset) \ + Member(class, NoMark, PropertyAttributes *, attrs) \ + Member(class, NoMark, ReturnedValue, freeList) \ + Member(class, NoMark, SparseArray *, sparse) \ + Member(class, ValueArray, ValueArray, values) + +DECLARE_HEAP_OBJECT(ArrayData, Base) { + DECLARE_MARK_TABLE(ArrayData); + + 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; } }; - uint alloc; - Type type; - PropertyAttributes *attrs; - union { - uint len; - ReturnedValue freeList; - }; - union { - uint offset; - SparseArray *sparse; - }; - Value arrayData[1]; - bool isSparse() const { return type == Sparse; } const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(Base::vtable()); } @@ -118,35 +122,32 @@ struct ArrayData : public Base { inline ReturnedValue get(uint i) const { return vtable()->get(this, i); } - inline void getProperty(uint index, Property *p, PropertyAttributes *attrs); - inline void setProperty(uint index, const Property *p); - inline Property *getProperty(uint index); - inline Value *getValueOrSetter(uint index, PropertyAttributes *attrs); + 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 PropertyAttributes attributes(uint i) const; bool isEmpty(uint i) const { return get(i) == Primitive::emptyValue().asReturnedValue(); } - inline ReturnedValue length() const { + inline uint length() const { return vtable()->length(this); } + void setArrayData(EngineBase *e, uint index, Value newVal) { + values.set(e, index, newVal); + } + + uint mappedIndex(uint index) const; }; V4_ASSERT_IS_TRIVIAL(ArrayData) struct SimpleArrayData : public ArrayData { - uint mappedIndex(uint index) const { return (index + offset) % alloc; } - Value data(uint index) const { return arrayData[mappedIndex(index)]; } - Value &data(uint index) { return arrayData[mappedIndex(index)]; } - - Property *getProperty(uint index) { - if (index >= len) - return 0; - index = mappedIndex(index); - if (arrayData[index].isEmpty()) - return 0; - return reinterpret_cast<Property *>(arrayData + index); + uint mappedIndex(uint index) const { return (index + offset) % values.alloc; } + const Value &data(uint index) const { return values[mappedIndex(index)]; } + void setData(EngineBase *e, uint index, Value newVal) { + values.set(e, mappedIndex(index), newVal); } PropertyAttributes attributes(uint i) const { @@ -168,13 +169,6 @@ struct SparseArrayData : public ArrayData { return n->value; } - Property *getProperty(uint index) { - SparseArrayNode *n = sparse->findNode(index); - if (!n) - return 0; - return reinterpret_cast<Property *>(arrayData + n->value); - } - PropertyAttributes attributes(uint i) const { if (!attrs) return Attr_Data; @@ -189,16 +183,23 @@ struct Q_QML_EXPORT ArrayData : public Managed { typedef Heap::ArrayData::Type Type; V4_MANAGED(ArrayData, Managed) + enum { + IsArrayData = true + }; - uint alloc() const { return d()->alloc; } - uint &alloc() { return d()->alloc; } - void setAlloc(uint a) { d()->alloc = a; } - Type type() const { return d()->type; } + 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; } + Type type() const { return static_cast<Type>(d()->type); } void setType(Type t) { d()->type = t; } PropertyAttributes *attrs() const { return d()->attrs; } void setAttrs(PropertyAttributes *a) { d()->attrs = a; } - const Value *arrayData() const { return &d()->arrayData[0]; } - Value *arrayData() { return &d()->arrayData[0]; } + const Value *arrayData() const { return d()->values.data(); } + void setArrayData(EngineBase *e, uint index, Value newVal) { + d()->setArrayData(e, index, newVal); + } const ArrayVTable *vtable() const { return d()->vtable(); } bool isSparse() const { return type() == Heap::ArrayData::Sparse; } @@ -221,9 +222,6 @@ struct Q_QML_EXPORT ArrayData : public Managed ReturnedValue get(uint i) const { return d()->get(i); } - inline Property *getProperty(uint index) { - return d()->getProperty(index); - } static void ensureAttributes(Object *o); static void realloc(Object *o, Type newType, uint alloc, bool enforceAttributes); @@ -240,15 +238,12 @@ struct Q_QML_EXPORT SimpleArrayData : public ArrayData uint mappedIndex(uint index) const { return d()->mappedIndex(index); } Value data(uint index) const { return d()->data(index); } - Value &data(uint index) { return d()->data(index); } - uint &len() { return d()->len; } - uint len() const { return d()->len; } + uint &len() { return d()->values.size; } + uint len() const { return d()->values.size; } static Heap::ArrayData *reallocate(Object *o, uint n, bool enforceAttributes); - static void markObjects(Heap::Base *d, ExecutionEngine *e); - static ReturnedValue get(const Heap::ArrayData *d, uint index); static bool put(Object *o, uint index, const Value &value); static bool putArray(Object *o, uint index, const Value *values, uint n); @@ -276,8 +271,6 @@ struct Q_QML_EXPORT SparseArrayData : public ArrayData uint mappedIndex(uint index) const { return d()->mappedIndex(index); } - static void markObjects(Heap::Base *d, ExecutionEngine *e); - static Heap::ArrayData *reallocate(Object *o, uint n, bool enforceAttributes); static ReturnedValue get(const Heap::ArrayData *d, uint index); static bool put(Object *o, uint index, const Value &value); @@ -292,30 +285,38 @@ struct Q_QML_EXPORT SparseArrayData : public ArrayData namespace Heap { -void ArrayData::getProperty(uint index, Property *p, PropertyAttributes *attrs) +inline uint ArrayData::mappedIndex(uint index) const { - Property *pd = getProperty(index); - Q_ASSERT(pd); - *attrs = attributes(index); - p->value = pd->value; - if (attrs->isAccessor()) - p->set = pd->set; + if (isSparse()) + return static_cast<const SparseArrayData *>(this)->mappedIndex(index); + if (index >= values.size) + return UINT_MAX; + uint idx = static_cast<const SimpleArrayData *>(this)->mappedIndex(index); + return values[idx].isEmpty() ? UINT_MAX : idx; } -void ArrayData::setProperty(uint index, const Property *p) +bool ArrayData::getProperty(uint index, Property *p, PropertyAttributes *attrs) { - Property *pd = getProperty(index); - Q_ASSERT(pd); - pd->value = p->value; - if (attributes(index).isAccessor()) - pd->set = p->set; + uint mapped = mappedIndex(index); + if (mapped == UINT_MAX) { + *attrs = Attr_Invalid; + return false; + } + + *attrs = attributes(index); + p->value = *(Index{ this, mapped }); + if (attrs->isAccessor()) + p->set = *(Index{ this, mapped + 1 /*Object::SetterOffset*/ }); + return true; } -inline Property *ArrayData::getProperty(uint index) +void ArrayData::setProperty(QV4::EngineBase *e, uint index, const Property *p) { - if (isSparse()) - return static_cast<SparseArrayData *>(this)->getProperty(index); - return static_cast<SimpleArrayData *>(this)->getProperty(index); + uint mapped = mappedIndex(index); + Q_ASSERT(mapped != UINT_MAX); + values.set(e, mapped, p->value); + if (attributes(index).isAccessor()) + values.set(e, mapped + 1 /*QV4::Object::SetterOffset*/, p->set); } inline PropertyAttributes ArrayData::attributes(uint i) const @@ -325,16 +326,16 @@ inline PropertyAttributes ArrayData::attributes(uint i) const return static_cast<const SimpleArrayData *>(this)->attributes(i); } -Value *ArrayData::getValueOrSetter(uint index, PropertyAttributes *attrs) +ArrayData::Index ArrayData::getValueOrSetter(uint index, PropertyAttributes *attrs) { - Property *p = getProperty(index); - if (!p) { + uint idx = mappedIndex(index); + if (idx == UINT_MAX) { *attrs = Attr_Invalid; - return 0; + return { 0, 0 }; } *attrs = attributes(index); - return attrs->isAccessor() ? &p->set : &p->value; + return { this, attrs->isAccessor() ? idx + 1 /* QV4::Object::SetterOffset*/ : idx }; } diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 759354f4e2..a2c19e1f2d 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -690,8 +690,8 @@ void ArrayPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallD } else { Q_ASSERT(instance->arrayType() == Heap::ArrayData::Simple || instance->arrayType() == Heap::ArrayData::Complex); Heap::SimpleArrayData *sa = instance->d()->arrayData.cast<Heap::SimpleArrayData>(); - if (len > sa->len) - len = sa->len; + if (len > sa->values.size) + len = sa->values.size; uint idx = fromIndex; while (idx < len) { value = sa->data(idx); diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 3ff864d7b9..6807c835b0 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -54,41 +54,44 @@ using namespace QV4; DEFINE_MANAGED_VTABLE(ExecutionContext); +DEFINE_MANAGED_VTABLE(SimpleCallContext); DEFINE_MANAGED_VTABLE(CallContext); DEFINE_MANAGED_VTABLE(WithContext); DEFINE_MANAGED_VTABLE(CatchContext); DEFINE_MANAGED_VTABLE(GlobalContext); -/* Function *f, int argc */ -#define requiredMemoryForExecutionContect(f, argc) \ - ((sizeof(CallContext::Data) + 7) & ~7) + \ - sizeof(Value) * (f->compiledFunction->nLocals + qMax((uint)argc, f->nFormals)) + sizeof(CallData) - Heap::CallContext *ExecutionContext::newCallContext(Function *function, CallData *callData) { - Heap::CallContext *c = engine()->memoryManager->allocManaged<CallContext>( - requiredMemoryForExecutionContect(function, callData->argc)); + uint localsAndFormals = function->compiledFunction->nLocals + sizeof(CallData)/sizeof(Value) - 1 + qMax(static_cast<uint>(callData->argc), function->nFormals); + size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * (localsAndFormals); + + ExecutionEngine *v4 = engine(); + Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory); c->init(Heap::ExecutionContext::Type_CallContext); c->v4Function = function; c->strictMode = function->isStrict(); - c->outer = this->d(); - - c->activation = 0; + c->outer.set(v4, this->d()); c->compilationUnit = function->compilationUnit; c->lookups = function->compilationUnit->runtimeLookups; c->constantTable = function->compilationUnit->constants; - c->locals = (Value *)((quintptr(c + 1) + 7) & ~7); const CompiledData::Function *compiledFunction = function->compiledFunction; - int nLocals = compiledFunction->nLocals; + uint nLocals = compiledFunction->nLocals; + c->locals.size = nLocals; + c->locals.alloc = localsAndFormals; +#if QT_POINTER_SIZE == 8 + // memory allocated from the JS heap is 0 initialized, so skip the std::fill() below + Q_ASSERT(Primitive::undefinedValue().asReturnedValue() == 0); +#else if (nLocals) - std::fill(c->locals, c->locals + nLocals, Primitive::undefinedValue()); + std::fill(c->locals.values, c->locals.values + nLocals, Primitive::undefinedValue()); +#endif - c->callData = reinterpret_cast<CallData *>(c->locals + nLocals); - ::memcpy(c->callData, callData, sizeof(CallData) + (callData->argc - 1) * sizeof(Value)); + c->callData = reinterpret_cast<CallData *>(c->locals.values + nLocals); + ::memcpy(c->callData, callData, sizeof(CallData) - sizeof(Value) + static_cast<uint>(callData->argc) * sizeof(Value)); if (callData->argc < static_cast<int>(compiledFunction->nFormals)) std::fill(c->callData->args + c->callData->argc, c->callData->args + compiledFunction->nFormals, Primitive::undefinedValue()); @@ -118,10 +121,10 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) switch (ctx->d()->type) { case Heap::ExecutionContext::Type_CallContext: case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); + Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); if (!activation) { if (!c->activation) - c->activation = scope.engine->newObject(); + c->activation.set(scope.engine, scope.engine->newObject()); activation = c->activation; } break; @@ -144,7 +147,7 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) ctx = ctx->d()->outer; } - if (activation->hasProperty(name)) + if (activation->hasOwnProperty(name)) return; ScopedProperty desc(scope); PropertyAttributes attrs(Attr_Data); @@ -155,41 +158,52 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) void Heap::GlobalContext::init(ExecutionEngine *eng) { Heap::ExecutionContext::init(Heap::ExecutionContext::Type_GlobalContext); - global = eng->globalObject->d(); + global.set(eng, eng->globalObject->d()); } void Heap::CatchContext::init(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue) { Heap::ExecutionContext::init(Heap::ExecutionContext::Type_CatchContext); - outer = outerContext; + outer.set(internalClass->engine, outerContext); strictMode = outer->strictMode; callData = outer->callData; lookups = outer->lookups; constantTable = outer->constantTable; compilationUnit = outer->compilationUnit; - this->exceptionVarName = exceptionVarName; - this->exceptionValue = exceptionValue; + this->exceptionVarName.set(internalClass->engine, exceptionVarName); + this->exceptionValue.set(internalClass->engine, exceptionValue); } +void Heap::WithContext::init(ExecutionContext *outerContext, Object *with) +{ + Heap::ExecutionContext::init(Heap::ExecutionContext::Type_WithContext); + outer.set(internalClass->engine, outerContext); + callData = outer->callData; + lookups = outer->lookups; + constantTable = outer->constantTable; + compilationUnit = outer->compilationUnit; -Identifier * const *CallContext::formals() const + withObject.set(internalClass->engine, with); +} + +Identifier * const *SimpleCallContext::formals() const { return d()->v4Function ? d()->v4Function->internalClass->nameMap.constData() : 0; } -unsigned int CallContext::formalCount() const +unsigned int SimpleCallContext::formalCount() const { return d()->v4Function ? d()->v4Function->nFormals : 0; } -Identifier * const *CallContext::variables() const +Identifier * const *SimpleCallContext::variables() const { return d()->v4Function ? d()->v4Function->internalClass->nameMap.constData() + d()->v4Function->nFormals : 0; } -unsigned int CallContext::variableCount() const +unsigned int SimpleCallContext::variableCount() const { return d()->v4Function ? d()->v4Function->compiledFunction->nLocals : 0; } @@ -202,7 +216,6 @@ bool ExecutionContext::deleteProperty(String *name) Identifier *id = name->identifier(); Scope scope(this); - bool hasWith = false; ScopedContext ctx(scope, this); for (; ctx; ctx = ctx->d()->outer) { switch (ctx->d()->type) { @@ -213,7 +226,6 @@ bool ExecutionContext::deleteProperty(String *name) break; } case Heap::ExecutionContext::Type_WithContext: { - hasWith = true; ScopedObject withObject(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); if (withObject->hasProperty(name)) return withObject->deleteProperty(name); @@ -226,14 +238,13 @@ bool ExecutionContext::deleteProperty(String *name) break; } case Heap::ExecutionContext::Type_CallContext: + Q_FALLTHROUGH(); case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); - if (c->v4Function && (c->v4Function->needsActivation() || hasWith)) { - uint index = c->v4Function->internalClass->find(id); - if (index < UINT_MAX) - // ### throw in strict mode? - return false; - } + Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); + uint index = c->v4Function->internalClass->find(id); + if (index < UINT_MAX) + // ### throw in strict mode? + return false; ScopedObject qml(scope, c->activation); if (qml && qml->hasProperty(name)) return qml->deleteProperty(name); @@ -250,61 +261,6 @@ bool ExecutionContext::deleteProperty(String *name) return true; } -bool CallContext::needsOwnArguments() const -{ - QV4::Function *f = d()->v4Function; - return (f && f->needsActivation()) || (argc() < (f ? static_cast<int>(f->nFormals) : 0)); -} - -void ExecutionContext::markObjects(Heap::Base *m, ExecutionEngine *engine) -{ - ExecutionContext::Data *ctx = static_cast<ExecutionContext::Data *>(m); - - if (ctx->outer) - ctx->outer->mark(engine); - - switch (ctx->type) { - case Heap::ExecutionContext::Type_CatchContext: { - CatchContext::Data *c = static_cast<CatchContext::Data *>(ctx); - c->exceptionVarName->mark(engine); - c->exceptionValue.mark(engine); - break; - } - case Heap::ExecutionContext::Type_WithContext: { - WithContext::Data *w = static_cast<WithContext::Data *>(ctx); - if (w->withObject) - w->withObject->mark(engine); - break; - } - case Heap::ExecutionContext::Type_GlobalContext: { - GlobalContext::Data *g = static_cast<GlobalContext::Data *>(ctx); - g->global->mark(engine); - break; - } - case Heap::ExecutionContext::Type_SimpleCallContext: - break; - case Heap::ExecutionContext::Type_CallContext: { - QV4::Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); - Q_ASSERT(c->v4Function); - ctx->callData->thisObject.mark(engine); - for (int arg = 0; arg < qMax(ctx->callData->argc, (int)c->v4Function->nFormals); ++arg) - ctx->callData->args[arg].mark(engine); - for (unsigned local = 0, lastLocal = c->v4Function->compiledFunction->nLocals; local < lastLocal; ++local) - c->locals[local].mark(engine); - if (c->activation) - c->activation->mark(engine); - if (c->function) - c->function->mark(engine); - break; - } - case Heap::ExecutionContext::Type_QmlContext: { - QmlContext::Data *g = static_cast<QmlContext::Data *>(ctx); - g->qml->mark(engine); - break; - } - } -} - // Do a standard call with this execution context as the outer scope void ExecutionContext::call(Scope &scope, CallData *callData, Function *function, const FunctionObject *f) { @@ -312,7 +268,7 @@ void ExecutionContext::call(Scope &scope, CallData *callData, Function *function Scoped<CallContext> ctx(scope, newCallContext(function, callData)); if (f) - ctx->d()->function = f->d(); + ctx->d()->function.set(scope.engine, f->d()); scope.engine->pushContext(ctx); scope.result = Q_V4_PROFILE(scope.engine, function); @@ -328,7 +284,7 @@ void QV4::ExecutionContext::simpleCall(Scope &scope, CallData *callData, Functio ExecutionContextSaver ctxSaver(scope); - CallContext::Data *ctx = scope.engine->memoryManager->allocSimpleCallContext(); + SimpleCallContext::Data *ctx = scope.engine->memoryManager->allocSimpleCallContext(); ctx->strictMode = function->isStrict(); ctx->callData = callData; @@ -336,8 +292,7 @@ void QV4::ExecutionContext::simpleCall(Scope &scope, CallData *callData, Functio ctx->compilationUnit = function->compilationUnit; ctx->lookups = function->compilationUnit->runtimeLookups; ctx->constantTable = function->compilationUnit->constants; - ctx->outer = this->d(); - ctx->locals = scope.alloc(function->compiledFunction->nLocals); + ctx->outer.set(scope.engine, this->d()); for (int i = callData->argc; i < (int)function->nFormals; ++i) callData->args[i] = Encode::undefined(); @@ -366,7 +321,7 @@ void ExecutionContext::setProperty(String *name, const Value &value) case Heap::ExecutionContext::Type_CatchContext: { Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); if (c->exceptionVarName->isEqualTo(name->d())) { - c->exceptionValue = value; + c->exceptionValue.set(scope.engine, value); return; } break; @@ -385,15 +340,16 @@ void ExecutionContext::setProperty(String *name, const Value &value) } case Heap::ExecutionContext::Type_CallContext: case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); + Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); if (c->v4Function) { uint index = c->v4Function->internalClass->find(id); if (index < UINT_MAX) { if (index < c->v4Function->nFormals) { c->callData->args[c->v4Function->nFormals - index - 1] = value; } else { + Q_ASSERT(c->type == Heap::ExecutionContext::Type_CallContext); index -= c->v4Function->nFormals; - c->locals[index] = value; + static_cast<Heap::CallContext *>(c)->locals.set(scope.engine, index, value); } return; } @@ -434,13 +390,10 @@ ReturnedValue ExecutionContext::getProperty(String *name) if (name->equals(engine()->id_this())) return thisObject().asReturnedValue(); - bool hasWith = false; - bool hasCatchScope = false; ScopedContext ctx(scope, this); for (; ctx; ctx = ctx->d()->outer) { switch (ctx->d()->type) { case Heap::ExecutionContext::Type_CatchContext: { - hasCatchScope = true; Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); if (c->exceptionVarName->isEqualTo(name->d())) return c->exceptionValue.asReturnedValue(); @@ -448,7 +401,6 @@ ReturnedValue ExecutionContext::getProperty(String *name) } case Heap::ExecutionContext::Type_WithContext: { ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); - hasWith = true; bool hasProperty = false; v = w->get(name, &hasProperty); if (hasProperty) { @@ -465,19 +417,20 @@ ReturnedValue ExecutionContext::getProperty(String *name) break; } case Heap::ExecutionContext::Type_CallContext: + Q_FALLTHROUGH(); case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); - if (c->v4Function && (c->v4Function->needsActivation() || hasWith || hasCatchScope)) { - name->makeIdentifier(); - Identifier *id = name->identifier(); - - uint index = c->v4Function->internalClass->find(id); - if (index < UINT_MAX) { - if (index < c->v4Function->nFormals) - return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue(); - return c->locals[index - c->v4Function->nFormals].asReturnedValue(); - } + Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); + + name->makeIdentifier(); + Identifier *id = name->identifier(); + uint index = c->v4Function->internalClass->find(id); + if (index < UINT_MAX) { + if (index < c->v4Function->nFormals) + return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue(); + if (c->type == Heap::ExecutionContext::Type_CallContext) + return static_cast<Heap::CallContext *>(c)->locals[index - c->v4Function->nFormals].asReturnedValue(); } + ScopedObject activation(scope, c->activation); if (activation) { bool hasProperty = false; @@ -485,9 +438,12 @@ ReturnedValue ExecutionContext::getProperty(String *name) if (hasProperty) return v->asReturnedValue(); } - if (c->function && c->v4Function->isNamedExpression() - && name->equals(ScopedString(scope, c->v4Function->name()))) - return c->function->asReturnedValue(); + + if (c->v4Function->isNamedExpression() && c->type == Heap::ExecutionContext::Type_CallContext) { + if (name->equals(ScopedString(scope, c->v4Function->name()))) + if (auto func = static_cast<Heap::CallContext *>(c)->function) + return func->asReturnedValue(); + } break; } case Heap::ExecutionContext::Type_QmlContext: { @@ -514,13 +470,10 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) if (name->equals(engine()->id_this())) return thisObject().asReturnedValue(); - bool hasWith = false; - bool hasCatchScope = false; ScopedContext ctx(scope, this); for (; ctx; ctx = ctx->d()->outer) { switch (ctx->d()->type) { case Heap::ExecutionContext::Type_CatchContext: { - hasCatchScope = true; Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); if (c->exceptionVarName->isEqualTo(name->d())) return c->exceptionValue.asReturnedValue(); @@ -528,7 +481,6 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) } case Heap::ExecutionContext::Type_WithContext: { ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); - hasWith = true; bool hasProperty = false; v = w->get(name, &hasProperty); if (hasProperty) { @@ -546,19 +498,20 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) break; } case Heap::ExecutionContext::Type_CallContext: + Q_FALLTHROUGH(); case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); - if (c->v4Function && (c->v4Function->needsActivation() || hasWith || hasCatchScope)) { - name->makeIdentifier(); - Identifier *id = name->identifier(); - - uint index = c->v4Function->internalClass->find(id); - if (index < UINT_MAX) { - if (index < c->v4Function->nFormals) - return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue(); - return c->locals[index - c->v4Function->nFormals].asReturnedValue(); - } + Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); + + name->makeIdentifier(); + Identifier *id = name->identifier(); + uint index = c->v4Function->internalClass->find(id); + if (index < UINT_MAX) { + if (index < c->v4Function->nFormals) + return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue(); + if (c->type == Heap::ExecutionContext::Type_CallContext) + return static_cast<Heap::CallContext *>(c)->locals[index - c->v4Function->nFormals].asReturnedValue(); } + ScopedObject activation(scope, c->activation); if (activation) { bool hasProperty = false; @@ -566,9 +519,12 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) if (hasProperty) return v->asReturnedValue(); } - if (c->function && c->v4Function->isNamedExpression() - && name->equals(ScopedString(scope, c->v4Function->name()))) - return c->function->asReturnedValue(); + + if (c->v4Function->isNamedExpression() && c->type == Heap::ExecutionContext::Type_CallContext) { + if (name->equals(ScopedString(scope, c->v4Function->name()))) + if (auto func = static_cast<Heap::CallContext *>(c)->function) + return func->asReturnedValue(); + } break; } case Heap::ExecutionContext::Type_QmlContext: { @@ -592,7 +548,7 @@ Function *ExecutionContext::getFunction() const Scope scope(engine()); ScopedContext it(scope, this->d()); for (; it; it = it->d()->outer) { - if (const CallContext *callCtx = it->asCallContext()) + if (const SimpleCallContext *callCtx = it->asSimpleCallContext()) return callCtx->d()->v4Function; else if (it->asCatchContext() || it->asWithContext()) continue; // look in the parent context for a FunctionObject diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index 0b63922a4b..a854c324d0 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -68,10 +68,11 @@ struct Function; struct Function; struct Identifier; struct CallContext; +struct SimpleCallContext; struct CatchContext; struct WithContext; struct QmlContext; -struct QmlContextWrapper; +struct QQmlContextWrapper; // Attention: Make sure that this structure is the same size on 32-bit and 64-bit // architecture or you'll have to change the JIT code. @@ -101,34 +102,17 @@ namespace Heap { struct QmlContext; -// ### Temporary arrangment until this code hits the dev branch and -// can use the Members macro -struct ExecutionContextData { - CallData *callData; - ExecutionContext *outer; - Lookup *lookups; - const QV4::Value *constantTable; - CompiledData::CompilationUnitBase *compilationUnit; - // as member of non-pointer size this has to come last to preserve the ability to - // translate offsetof of it between 64-bit and 32-bit. - int lineNumber; -#if QT_POINTER_SIZE == 8 - uint padding_; -#endif -}; - -Q_STATIC_ASSERT(std::is_standard_layout<ExecutionContextData>::value); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, callData) == 0); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, outer) == offsetof(ExecutionContextData, callData) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, lookups) == offsetof(ExecutionContextData, outer) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, constantTable) == offsetof(ExecutionContextData, lookups) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, compilationUnit) == offsetof(ExecutionContextData, constantTable) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, lineNumber) == offsetof(ExecutionContextData, compilationUnit) + QT_POINTER_SIZE); +#define ExecutionContextMembers(class, Member) \ + Member(class, NoMark, CallData *, callData) \ + Member(class, Pointer, ExecutionContext *, outer) \ + Member(class, NoMark, Lookup *, lookups) \ + Member(class, NoMark, const QV4::Value *, constantTable) \ + Member(class, NoMark, CompiledData::CompilationUnitBase *, compilationUnit) \ + Member(class, NoMark, int, lineNumber) // as member of non-pointer size this has to come last to preserve the ability to + // translate offsetof of it between 64-bit and 32-bit. -struct ExecutionContextSizeStruct : public Base, public ExecutionContextData {}; - -struct ExecutionContext : Base, public ExecutionContextData { - static Q_CONSTEXPR size_t baseOffset = sizeof(ExecutionContextSizeStruct) - sizeof(ExecutionContextData); +DECLARE_HEAP_OBJECT(ExecutionContext, Base) { + DECLARE_MARK_TABLE(ExecutionContext); enum ContextType { Type_GlobalContext = 0x1, @@ -158,17 +142,20 @@ struct ExecutionContext : Base, public ExecutionContextData { V4_ASSERT_IS_TRIVIAL(ExecutionContext) Q_STATIC_ASSERT(sizeof(ExecutionContext) == sizeof(Base) + sizeof(ExecutionContextData) + QT_POINTER_SIZE); -struct CallContextData { - Value *locals; -}; - -Q_STATIC_ASSERT(std::is_standard_layout<CallContextData>::value); -Q_STATIC_ASSERT(offsetof(CallContextData, locals) == 0); +Q_STATIC_ASSERT(std::is_standard_layout<ExecutionContextData>::value); +Q_STATIC_ASSERT(offsetof(ExecutionContextData, callData) == 0); +Q_STATIC_ASSERT(offsetof(ExecutionContextData, outer) == offsetof(ExecutionContextData, callData) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(offsetof(ExecutionContextData, lookups) == offsetof(ExecutionContextData, outer) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(offsetof(ExecutionContextData, constantTable) == offsetof(ExecutionContextData, lookups) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(offsetof(ExecutionContextData, compilationUnit) == offsetof(ExecutionContextData, constantTable) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(offsetof(ExecutionContextData, lineNumber) == offsetof(ExecutionContextData, compilationUnit) + QT_POINTER_SIZE); -struct CallContextSizeStruct : public ExecutionContext, public CallContextData {}; +#define SimpleCallContextMembers(class, Member) \ + Member(class, Pointer, Object *, activation) \ + Member(class, NoMark, QV4::Function *, v4Function) -struct CallContext : ExecutionContext, public CallContextData { - static Q_CONSTEXPR size_t baseOffset = sizeof(CallContextSizeStruct) - sizeof(CallContextData); +DECLARE_HEAP_OBJECT(SimpleCallContext, ExecutionContext) { + DECLARE_MARK_TABLE(SimpleCallContext); void init(ContextType t = Type_SimpleCallContext) { @@ -177,39 +164,66 @@ struct CallContext : ExecutionContext, public CallContextData { inline unsigned int formalParameterCount() const; - Pointer<FunctionObject> function; - QV4::Function *v4Function; - Pointer<Object> activation; }; -V4_ASSERT_IS_TRIVIAL(CallContext) +V4_ASSERT_IS_TRIVIAL(SimpleCallContext) +Q_STATIC_ASSERT(std::is_standard_layout<SimpleCallContextData>::value); +Q_STATIC_ASSERT(offsetof(SimpleCallContextData, activation) == 0); +Q_STATIC_ASSERT(offsetof(SimpleCallContextData, v4Function) == offsetof(SimpleCallContextData, activation) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(sizeof(SimpleCallContextData) == 2 * QT_POINTER_SIZE); +Q_STATIC_ASSERT(sizeof(SimpleCallContext) == sizeof(ExecutionContext) + sizeof(SimpleCallContextData)); + +#if QT_POINTER_SIZE == 8 +#define CallContextMembers(class, Member) \ + Member(class, Pointer, FunctionObject *, function) \ + Member(class, ValueArray, ValueArray, locals) +#else +#define CallContextMembers(class, Member) \ + Member(class, Pointer, FunctionObject *, function) \ + Member(class, NoMark, void *, padding) \ + Member(class, ValueArray, ValueArray, locals) +#endif + +DECLARE_HEAP_OBJECT(CallContext, SimpleCallContext) { + DECLARE_MARK_TABLE(CallContext); + + using SimpleCallContext::formalParameterCount; +}; + +Q_STATIC_ASSERT(std::is_standard_layout<CallContextData>::value); +Q_STATIC_ASSERT(offsetof(CallContextData, function) == 0); +// IMPORTANT: we cannot do offsetof(CallContextData, locals) in the JIT as the offset does not scale with +// the pointer size. On 32-bit ARM the offset of the ValueArray is aligned to 8 bytes, on 32-bit x86 for +// example it is not. Therefore we have a padding in place and always have a distance of 8 bytes. +Q_STATIC_ASSERT(offsetof(CallContextData, locals) == offsetof(CallContextData, function) + 8); + +#define GlobalContextMembers(class, Member) \ + Member(class, Pointer, Object *, global) + +DECLARE_HEAP_OBJECT(GlobalContext, ExecutionContext) { + DECLARE_MARK_TABLE(GlobalContext); -struct GlobalContext : ExecutionContext { void init(ExecutionEngine *engine); - Pointer<Object> global; }; V4_ASSERT_IS_TRIVIAL(GlobalContext) -struct CatchContext : ExecutionContext { +#define CatchContextMembers(class, Member) \ + Member(class, Pointer, String *, exceptionVarName) \ + Member(class, HeapValue, HeapValue, exceptionValue) + +DECLARE_HEAP_OBJECT(CatchContext, ExecutionContext) { + DECLARE_MARK_TABLE(CatchContext); + void init(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue); - Pointer<String> exceptionVarName; - Value exceptionValue; }; V4_ASSERT_IS_TRIVIAL(CatchContext) -struct WithContext : ExecutionContext { - void init(ExecutionContext *outerContext, Object *with) - { - Heap::ExecutionContext::init(Heap::ExecutionContext::Type_WithContext); - outer = outerContext; - callData = outer->callData; - lookups = outer->lookups; - constantTable = outer->constantTable; - compilationUnit = outer->compilationUnit; - - withObject = with; - } +#define WithContextMembers(class, Member) \ + Member(class, Pointer, Object *, withObject) + +DECLARE_HEAP_OBJECT(WithContext, ExecutionContext) { + DECLARE_MARK_TABLE(WithContext); - Pointer<Object> withObject; + void init(ExecutionContext *outerContext, Object *with); }; V4_ASSERT_IS_TRIVIAL(WithContext) @@ -236,15 +250,13 @@ struct Q_QML_EXPORT ExecutionContext : public Managed ReturnedValue getPropertyAndBase(String *name, Value *base); bool deleteProperty(String *name); - inline CallContext *asCallContext(); - inline const CallContext *asCallContext() const; + inline SimpleCallContext *asSimpleCallContext(); + inline const SimpleCallContext *asSimpleCallContext() const; inline const CatchContext *asCatchContext() const; inline const WithContext *asWithContext() const; Function *getFunction() const; - static void markObjects(Heap::Base *m, ExecutionEngine *e); - Value &thisObject() const { return d()->callData->thisObject; } @@ -262,10 +274,10 @@ struct Q_QML_EXPORT ExecutionContext : public Managed void simpleCall(Scope &scope, CallData *callData, QV4::Function *function); }; -struct Q_QML_EXPORT CallContext : public ExecutionContext +struct Q_QML_EXPORT SimpleCallContext : public ExecutionContext { - V4_MANAGED(CallContext, ExecutionContext) - V4_INTERNALCLASS(CallContext) + V4_MANAGED(SimpleCallContext, ExecutionContext) + V4_INTERNALCLASS(SimpleCallContext) // formals are in reverse order Identifier * const *formals() const; @@ -274,14 +286,17 @@ struct Q_QML_EXPORT CallContext : public ExecutionContext unsigned int variableCount() const; inline ReturnedValue argument(int i) const; - bool needsOwnArguments() const; - }; -inline ReturnedValue CallContext::argument(int i) const { +inline ReturnedValue SimpleCallContext::argument(int i) const { return i < argc() ? args()[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue(); } +struct Q_QML_EXPORT CallContext : public SimpleCallContext +{ + V4_MANAGED(CallContext, SimpleCallContext) +}; + struct GlobalContext : public ExecutionContext { V4_MANAGED(GlobalContext, ExecutionContext) @@ -298,14 +313,14 @@ struct WithContext : public ExecutionContext V4_MANAGED(WithContext, ExecutionContext) }; -inline CallContext *ExecutionContext::asCallContext() +inline SimpleCallContext *ExecutionContext::asSimpleCallContext() { - return d()->type >= Heap::ExecutionContext::Type_SimpleCallContext ? static_cast<CallContext *>(this) : 0; + return d()->type >= Heap::ExecutionContext::Type_SimpleCallContext ? static_cast<SimpleCallContext *>(this) : 0; } -inline const CallContext *ExecutionContext::asCallContext() const +inline const SimpleCallContext *ExecutionContext::asSimpleCallContext() const { - return d()->type >= Heap::ExecutionContext::Type_SimpleCallContext ? static_cast<const CallContext *>(this) : 0; + return d()->type >= Heap::ExecutionContext::Type_SimpleCallContext ? static_cast<const SimpleCallContext *>(this) : 0; } inline const CatchContext *ExecutionContext::asCatchContext() const diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp index a810b38f24..f1405e08ee 100644 --- a/src/qml/jsruntime/qv4dataview.cpp +++ b/src/qml/jsruntime/qv4dataview.cpp @@ -73,7 +73,7 @@ void DataViewCtor::construct(const Managed *, Scope &scope, CallData *callData) } Scoped<DataView> a(scope, scope.engine->memoryManager->allocObject<DataView>()); - a->d()->buffer = buffer->d(); + a->d()->buffer.set(scope.engine, buffer->d()); a->d()->byteLength = byteLength; a->d()->byteOffset = byteOffset; scope.result = a.asReturnedValue(); @@ -84,13 +84,6 @@ void DataViewCtor::call(const Managed *that, Scope &scope, CallData *callData) construct(that, scope, callData); } - -void DataView::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - DataView::Data *v = static_cast<DataView::Data *>(that); - v->buffer->mark(e); -} - void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h index 11cc0a6bd9..5c50df4655 100644 --- a/src/qml/jsruntime/qv4dataview_p.h +++ b/src/qml/jsruntime/qv4dataview_p.h @@ -63,11 +63,14 @@ struct DataViewCtor : FunctionObject { void init(QV4::ExecutionContext *scope); }; -struct DataView : Object { +#define DataViewMembers(class, Member) \ + Member(class, Pointer, ArrayBuffer *, buffer) \ + Member(class, NoMark, uint, byteLength) \ + Member(class, NoMark, uint, byteOffset) + +DECLARE_HEAP_OBJECT(DataView, Object) { + DECLARE_MARK_TABLE(DataView); void init() { Object::init(); } - Pointer<ArrayBuffer> buffer; - uint byteLength; - uint byteOffset; }; } @@ -84,8 +87,6 @@ struct DataView : Object { V4_OBJECT2(DataView, Object) V4_PROTOTYPE(dataViewPrototype) - - static void markObjects(Heap::Base *that, ExecutionEngine *e); }; struct DataViewPrototype: Object diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index b90c335b1c..c56d007028 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -340,7 +340,9 @@ static inline double TimeClip(double t) { if (! qt_is_finite(t) || fabs(t) > 8.64e15) return qt_qnan(); - return Primitive::toInteger(t); + + // +0 looks weird, but is correct. See ES6 20.3.1.15. We must not return -0. + return Primitive::toInteger(t) + 0; } static inline double ParseString(const QString &s) @@ -724,7 +726,7 @@ void DatePrototype::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(7)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(7)); LocalTZA = getLocalTZA(); ctor->defineDefaultProperty(QStringLiteral("parse"), method_parse, 1); @@ -774,8 +776,21 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("setYear"), method_setYear, 1); defineDefaultProperty(QStringLiteral("setFullYear"), method_setFullYear, 3); defineDefaultProperty(QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); - defineDefaultProperty(QStringLiteral("toUTCString"), method_toUTCString, 0); - defineDefaultProperty(QStringLiteral("toGMTString"), method_toUTCString, 0); + + // ES6: B.2.4.3 & 20.3.4.43: + // We have to use the *same object* for toUTCString and toGMTString + { + QString toUtcString(QStringLiteral("toUTCString")); + QString toGmtString(QStringLiteral("toGMTString")); + ScopedString us(scope, engine->newIdentifier(toUtcString)); + ScopedString gs(scope, engine->newIdentifier(toGmtString)); + ExecutionContext *global = engine->rootContext(); + ScopedFunctionObject toUtcGmtStringFn(scope, BuiltinFunction::create(global, us, method_toUTCString)); + toUtcGmtStringFn->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); + defineDefaultProperty(us, toUtcGmtStringFn); + defineDefaultProperty(gs, toUtcGmtStringFn); + } + defineDefaultProperty(QStringLiteral("toISOString"), method_toISOString, 0); defineDefaultProperty(QStringLiteral("toJSON"), method_toJSON, 1); } @@ -1025,6 +1040,7 @@ void DatePrototype::method_setTime(const BuiltinFunction *, Scope &scope, CallDa THROW_TYPE_ERROR(); double t = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); self->setDate(TimeClip(t)); scope.result = Encode(self->date()); } @@ -1036,7 +1052,9 @@ void DatePrototype::method_setMilliseconds(const BuiltinFunction *, Scope &scope THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double ms = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); scope.result = Encode(self->date()); } @@ -1048,7 +1066,9 @@ void DatePrototype::method_setUTCMilliseconds(const BuiltinFunction *, Scope &sc THROW_TYPE_ERROR(); double t = self->date(); + CHECK_EXCEPTION(); double ms = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); self->setDate(TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); scope.result = Encode(self->date()); } @@ -1060,8 +1080,11 @@ void DatePrototype::method_setSeconds(const BuiltinFunction *, Scope &scope, Cal THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double sec = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); double ms = (callData->argc < 2) ? msFromTime(t) : callData->args[1].toNumber(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); self->setDate(t); scope.result = Encode(self->date()); @@ -1088,9 +1111,13 @@ void DatePrototype::method_setMinutes(const BuiltinFunction *, Scope &scope, Cal THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double min = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); double sec = (callData->argc < 2) ? SecFromTime(t) : callData->args[1].toNumber(); + CHECK_EXCEPTION(); double ms = (callData->argc < 3) ? msFromTime(t) : callData->args[2].toNumber(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); self->setDate(t); scope.result = Encode(self->date()); @@ -1118,10 +1145,15 @@ void DatePrototype::method_setHours(const BuiltinFunction *, Scope &scope, CallD THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double hour = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); double min = (callData->argc < 2) ? MinFromTime(t) : callData->args[1].toNumber(); + CHECK_EXCEPTION(); double sec = (callData->argc < 3) ? SecFromTime(t) : callData->args[2].toNumber(); + CHECK_EXCEPTION(); double ms = (callData->argc < 4) ? msFromTime(t) : callData->args[3].toNumber(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); self->setDate(t); scope.result = Encode(self->date()); @@ -1150,7 +1182,9 @@ void DatePrototype::method_setDate(const BuiltinFunction *, Scope &scope, CallDa THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double date = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); self->setDate(t); scope.result = Encode(self->date()); @@ -1163,7 +1197,9 @@ void DatePrototype::method_setUTCDate(const BuiltinFunction *, Scope &scope, Cal THROW_TYPE_ERROR(); double t = self->date(); + CHECK_EXCEPTION(); double date = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); t = TimeClip(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t))); self->setDate(t); scope.result = Encode(self->date()); @@ -1176,8 +1212,11 @@ void DatePrototype::method_setMonth(const BuiltinFunction *, Scope &scope, CallD THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double month = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); double date = (callData->argc < 2) ? DateFromTime(t) : callData->args[1].toNumber(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); self->setDate(t); scope.result = Encode(self->date()); @@ -1245,11 +1284,15 @@ void DatePrototype::method_setFullYear(const BuiltinFunction *, Scope &scope, Ca THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); if (std::isnan(t)) t = 0; double year = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); double month = (callData->argc < 2) ? MonthFromTime(t) : callData->args[1].toNumber(); + CHECK_EXCEPTION(); double date = (callData->argc < 3) ? DateFromTime(t) : callData->args[2].toNumber(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); self->setDate(t); scope.result = Encode(self->date()); diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index ecd57bcd8d..b0373884dd 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -112,7 +112,7 @@ struct DateCtor: FunctionObject static void call(const Managed *that, Scope &scope, CallData *); }; -struct DatePrototype: DateObject +struct DatePrototype: Object { V4_PROTOTYPE(objectPrototype) diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 0d3f29d089..f19f134c53 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -134,6 +134,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) , regExpAllocator(new QV4::ExecutableAllocator) , bumperPointerAllocator(new WTF::BumpPointerAllocator) , jsStack(new WTF::PageAllocation) + , gcStack(new WTF::PageAllocation) , globalCode(0) , v8Engine(0) , argumentsAccessors(0) @@ -182,18 +183,22 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) iselFactory.reset(factory); // reserve space for the JS stack - // we allow it to grow to 2 times JSStackLimit, as we can overshoot due to garbage collection - // and ScopedValues allocated outside of JIT'ed methods. - *jsStack = WTF::PageAllocation::allocate(2 * JSStackLimit, WTF::OSAllocator::JSVMStackPages, + // we allow it to grow to a bit more than JSStackLimit, as we can overshoot due to ScopedValues + // allocated outside of JIT'ed methods. + *jsStack = WTF::PageAllocation::allocate(JSStackLimit + 256*1024, WTF::OSAllocator::JSVMStackPages, /* writable */ true, /* executable */ false, /* includesGuardPages */ true); jsStackBase = (Value *)jsStack->base(); #ifdef V4_USE_VALGRIND - VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, 2*JSStackLimit); + VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, JSStackLimit + 256*1024); #endif jsStackTop = jsStackBase; + *gcStack = WTF::PageAllocation::allocate(GCStackLimit, WTF::OSAllocator::JSVMStackPages, + /* writable */ true, /* executable */ false, + /* includesGuardPages */ true); + exceptionValue = jsAlloca(1); globalObject = static_cast<Object *>(jsAlloca(1)); jsObjects = jsAlloca(NJSObjects); @@ -215,7 +220,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) internalClasses[Class_SparseArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable()); internalClasses[Class_ExecutionContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable()); internalClasses[EngineBase::Class_QmlContext] = internalClasses[EngineBase::Class_ExecutionContext]->changeVTable(QV4::QmlContext::staticVTable()); - internalClasses[Class_CallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable()); + internalClasses[Class_SimpleCallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable()); jsStrings[String_Empty] = newIdentifier(QString()); jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined")); @@ -257,7 +262,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) 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::QmlContextWrapper::staticVTable()); + internalClasses[EngineBase::Class_QmlContextWrapper] = internalClasses[Class_Object]->changeVTable(QV4::QQmlContextWrapper::staticVTable()); ic = newInternalClass(ArrayPrototype::staticVTable(), objectPrototype()); Q_ASSERT(ic->prototype); @@ -422,13 +427,14 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) // // set up the global object // - rootContext()->d()->global = globalObject->d(); + rootContext()->d()->global.set(scope.engine, globalObject->d()); rootContext()->d()->callData->thisObject = globalObject; Q_ASSERT(globalObject->d()->vtable()); globalObject->defineDefaultProperty(QStringLiteral("Object"), *objectCtor()); globalObject->defineDefaultProperty(QStringLiteral("String"), *stringCtor()); - globalObject->defineDefaultProperty(QStringLiteral("Number"), *numberCtor()); + FunctionObject *numberObject = numberCtor(); + globalObject->defineDefaultProperty(QStringLiteral("Number"), *numberObject); globalObject->defineDefaultProperty(QStringLiteral("Boolean"), *booleanCtor()); globalObject->defineDefaultProperty(QStringLiteral("Array"), *arrayCtor()); globalObject->defineDefaultProperty(QStringLiteral("Function"), *functionCtor()); @@ -458,8 +464,26 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) jsObjects[Eval_Function] = memoryManager->allocObject<EvalFunction>(global); globalObject->defineDefaultProperty(QStringLiteral("eval"), *evalFunction()); - globalObject->defineDefaultProperty(QStringLiteral("parseInt"), GlobalFunctions::method_parseInt, 2); - globalObject->defineDefaultProperty(QStringLiteral("parseFloat"), GlobalFunctions::method_parseFloat, 1); + // ES6: 20.1.2.12 & 20.1.2.13: + // parseInt and parseFloat must be the same FunctionObject on the global & + // Number object. + { + QString piString(QStringLiteral("parseInt")); + QString pfString(QStringLiteral("parseFloat")); + Scope scope(this); + ScopedString pi(scope, newIdentifier(piString)); + ScopedString pf(scope, newIdentifier(pfString)); + ExecutionContext *global = rootContext(); + ScopedFunctionObject parseIntFn(scope, BuiltinFunction::create(global, pi, GlobalFunctions::method_parseInt)); + ScopedFunctionObject parseFloatFn(scope, BuiltinFunction::create(global, pf, GlobalFunctions::method_parseFloat)); + parseIntFn->defineReadonlyConfigurableProperty(id_length(), Primitive::fromInt32(2)); + parseFloatFn->defineReadonlyConfigurableProperty(id_length(), Primitive::fromInt32(1)); + globalObject->defineDefaultProperty(piString, parseIntFn); + globalObject->defineDefaultProperty(pfString, parseFloatFn); + numberObject->defineDefaultProperty(piString, parseIntFn); + numberObject->defineDefaultProperty(pfString, parseFloatFn); + } + globalObject->defineDefaultProperty(QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1); globalObject->defineDefaultProperty(QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1); globalObject->defineDefaultProperty(QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1); @@ -491,6 +515,8 @@ ExecutionEngine::~ExecutionEngine() delete executableAllocator; jsStack->deallocate(); delete jsStack; + gcStack->deallocate(); + delete gcStack; delete [] argumentsAccessors; } @@ -602,12 +628,14 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int leng size_t size = sizeof(Heap::ArrayData) + (length-1)*sizeof(Value); Heap::SimpleArrayData *d = scope.engine->memoryManager->allocManaged<SimpleArrayData>(size); d->init(); - d->alloc = length; d->type = Heap::ArrayData::Simple; d->offset = 0; - d->len = length; - memcpy(&d->arrayData, values, length*sizeof(Value)); - a->d()->arrayData = d; + d->values.alloc = length; + d->values.size = length; + // this doesn't require a write barrier, things will be ok, when the new array data gets inserted into + // the parent object + memcpy(&d->values.values, values, length*sizeof(Value)); + a->d()->arrayData.set(this, d); a->setArrayLengthUnchecked(length); } return a->d(); @@ -884,7 +912,7 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file) QUrl base; ExecutionContext *c = currentContext; while (c) { - CallContext *callCtx = c->asCallContext(); + SimpleCallContext *callCtx = c->asSimpleCallContext(); if (callCtx && callCtx->d()->v4Function) { base = callCtx->d()->v4Function->finalUrl(); break; @@ -927,35 +955,24 @@ void ExecutionEngine::requireArgumentsAccessors(int n) } } -static void drainMarkStack(ExecutionEngine *engine, QV4::Value *markBase) +void ExecutionEngine::markObjects(MarkStack *markStack) { - while (engine->jsStackTop > markBase) { - Heap::Base *h = engine->popForGC(); - Q_ASSERT (h->vtable()->markObjects); - h->vtable()->markObjects(h, engine); - } -} - -void ExecutionEngine::markObjects() -{ - Value *markBase = jsStackTop; - identifierTable->mark(this); + identifierTable->mark(markStack); for (int i = 0; i < nArgumentsAccessors; ++i) { const Property &pd = argumentsAccessors[i]; if (Heap::FunctionObject *getter = pd.getter()) - getter->mark(this); + getter->mark(markStack); if (Heap::FunctionObject *setter = pd.setter()) - setter->mark(this); + setter->mark(markStack); } - classPool->markObjects(this); - - drainMarkStack(this, markBase); + classPool->markObjects(markStack); + markStack->drain(); for (auto compilationUnit: compilationUnits) { - compilationUnit->markObjects(this); - drainMarkStack(this, markBase); + compilationUnit->markObjects(markStack); + markStack->drain(); } } @@ -1150,9 +1167,9 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return QVariant::fromValue(QV4::JsonObject::toJsonObject(object)); } else if (QV4::QObjectWrapper *wrapper = object->as<QV4::QObjectWrapper>()) { return qVariantFromValue<QObject *>(wrapper->object()); - } else if (object->as<QV4::QmlContextWrapper>()) { + } else if (object->as<QV4::QQmlContextWrapper>()) { return QVariant(); - } else if (QV4::QmlTypeWrapper *w = object->as<QV4::QmlTypeWrapper>()) { + } else if (QV4::QQmlTypeWrapper *w = object->as<QV4::QQmlTypeWrapper>()) { return w->toVariant(); } else if (QV4::QQmlValueTypeWrapper *v = object->as<QV4::QQmlValueTypeWrapper>()) { return v->toVariant(); @@ -1574,12 +1591,6 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) return 0; } -void ExecutionEngine::assertObjectBelongsToEngine(const Heap::Base &baseObject) -{ - Q_ASSERT(!baseObject.vtable()->isObject || static_cast<const Heap::Object&>(baseObject).internalClass->engine == this); - Q_UNUSED(baseObject); -} - void ExecutionEngine::failStackLimitCheck(Scope &scope) { scope.result = throwRangeError(QStringLiteral("Maximum call stack size exceeded.")); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index fee06ebc58..4549cda5b9 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -104,17 +104,13 @@ public: WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine. - enum { JSStackLimit = 4*1024*1024 }; + enum { + JSStackLimit = 4*1024*1024, + GCStackLimit = 2*1024*1024 + }; WTF::PageAllocation *jsStack; - void pushForGC(Heap::Base *m) { - *jsStackTop = m; - ++jsStackTop; - } - Heap::Base *popForGC() { - --jsStackTop; - return jsStackTop->heapObject(); - } + WTF::PageAllocation *gcStack; QML_NEARLY_ALWAYS_INLINE Value *jsAlloca(int nValues) { Value *ptr = jsStackTop; @@ -422,7 +418,7 @@ public: void requireArgumentsAccessors(int n); - void markObjects(); + void markObjects(MarkStack *markStack); void initRootContext(); @@ -459,8 +455,6 @@ public: bool metaTypeFromJS(const Value *value, int type, void *data); QV4::ReturnedValue metaTypeToJS(int type, const void *data); - void assertObjectBelongsToEngine(const Heap::Base &baseObject); - bool checkStackLimits(Scope &scope); private: @@ -519,23 +513,32 @@ inline ExecutionContext *ExecutionEngine::parentContext(ExecutionContext *contex } inline -void Heap::Base::mark(QV4::ExecutionEngine *engine) +void Heap::Base::mark(QV4::MarkStack *markStack) { Q_ASSERT(inUse()); - if (isMarked()) - return; -#ifndef QT_NO_DEBUG - engine->assertObjectBelongsToEngine(*this); -#endif - setMarkBit(); - engine->pushForGC(this); + const HeapItem *h = reinterpret_cast<const HeapItem *>(this); + Chunk *c = h->chunk(); + size_t index = h - c->realBase(); + Q_ASSERT(!Chunk::testBit(c->extendsBitmap, index)); + quintptr *bitmap = c->blackBitmap + Chunk::bitmapIndex(index); + quintptr bit = Chunk::bitForIndex(index); + if (!(*bitmap & bit)) { + *bitmap |= bit; + markStack->push(this); + } } -inline void Value::mark(ExecutionEngine *e) +inline void Value::mark(MarkStack *markStack) { Heap::Base *o = heapObject(); if (o) - o->mark(e); + o->mark(markStack); +} + +inline void Managed::mark(MarkStack *markStack) +{ + Q_ASSERT(m()); + m()->mark(markStack); } #define CHECK_STACK_LIMITS(v4, scope) if ((v4)->checkStackLimits(scope)) return; \ diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index 38b5ed8909..8258d644ff 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -66,7 +66,9 @@ struct EngineBase { Heap::ExecutionContext *current = 0; Value *jsStackTop = 0; - quint32 hasException = false; + quint8 hasException = false; + quint8 writeBarrierActive = false; + quint16 unused = 0; #if QT_POINTER_SIZE == 8 quint8 padding[4]; #endif @@ -88,7 +90,7 @@ struct EngineBase { Class_SimpleArrayData, Class_SparseArrayData, Class_ExecutionContext, - Class_CallContext, + Class_SimpleCallContext, Class_Object, Class_ArrayObject, Class_FunctionObject, diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index 6811438915..b3bd28e18b 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -78,10 +78,10 @@ void Heap::ErrorObject::init() if (internalClass == scope.engine->internalClasses[EngineBase::Class_ErrorProto]) return; - *propertyData(QV4::ErrorObject::Index_Stack) = scope.engine->getStackFunction(); - *propertyData(QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset) = Encode::undefined(); - *propertyData(QV4::ErrorObject::Index_FileName) = Encode::undefined(); - *propertyData(QV4::ErrorObject::Index_LineNumber) = Encode::undefined(); + setProperty(scope.engine, QV4::ErrorObject::Index_Stack, scope.engine->getStackFunction()->d()); + setProperty(scope.engine, QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset, Primitive::undefinedValue()); + setProperty(scope.engine, QV4::ErrorObject::Index_FileName, Primitive::undefinedValue()); + setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Primitive::undefinedValue()); } void Heap::ErrorObject::init(const Value &message, ErrorType t) @@ -92,17 +92,17 @@ void Heap::ErrorObject::init(const Value &message, ErrorType t) Scope scope(internalClass->engine); Scoped<QV4::ErrorObject> e(scope, this); - *propertyData(QV4::ErrorObject::Index_Stack) = scope.engine->getStackFunction(); - *propertyData(QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset) = Encode::undefined(); + setProperty(scope.engine, QV4::ErrorObject::Index_Stack, scope.engine->getStackFunction()->d()); + setProperty(scope.engine, QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset, Primitive::undefinedValue()); e->d()->stackTrace = new StackTrace(scope.engine->stackTrace()); if (!e->d()->stackTrace->isEmpty()) { - *propertyData(QV4::ErrorObject::Index_FileName) = scope.engine->newString(e->d()->stackTrace->at(0).source); - *propertyData(QV4::ErrorObject::Index_LineNumber) = Primitive::fromInt32(e->d()->stackTrace->at(0).line); + setProperty(scope.engine, QV4::ErrorObject::Index_FileName, scope.engine->newString(e->d()->stackTrace->at(0).source)); + setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Primitive::fromInt32(e->d()->stackTrace->at(0).line)); } if (!message.isUndefined()) - *propertyData(QV4::ErrorObject::Index_Message) = message; + setProperty(scope.engine, QV4::ErrorObject::Index_Message, message); } void Heap::ErrorObject::init(const Value &message, const QString &fileName, int line, int column, ErrorObject::ErrorType t) @@ -113,8 +113,8 @@ void Heap::ErrorObject::init(const Value &message, const QString &fileName, int Scope scope(internalClass->engine); Scoped<QV4::ErrorObject> e(scope, this); - *propertyData(QV4::ErrorObject::Index_Stack) = scope.engine->getStackFunction(); - *propertyData(QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset) = Encode::undefined(); + setProperty(scope.engine, QV4::ErrorObject::Index_Stack, scope.engine->getStackFunction()->d()); + setProperty(scope.engine, QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset, Primitive::undefinedValue()); e->d()->stackTrace = new StackTrace(scope.engine->stackTrace()); StackFrame frame; @@ -124,12 +124,12 @@ void Heap::ErrorObject::init(const Value &message, const QString &fileName, int e->d()->stackTrace->prepend(frame); if (!e->d()->stackTrace->isEmpty()) { - *propertyData(QV4::ErrorObject::Index_FileName) = scope.engine->newString(e->d()->stackTrace->at(0).source); - *propertyData(QV4::ErrorObject::Index_LineNumber) = Primitive::fromInt32(e->d()->stackTrace->at(0).line); + setProperty(scope.engine, QV4::ErrorObject::Index_FileName, scope.engine->newString(e->d()->stackTrace->at(0).source)); + setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Primitive::fromInt32(e->d()->stackTrace->at(0).line)); } if (!message.isUndefined()) - *propertyData(QV4::ErrorObject::Index_Message) = message; + setProperty(scope.engine, QV4::ErrorObject::Index_Message, message); } const char *ErrorObject::className(Heap::ErrorObject::ErrorType t) @@ -168,19 +168,11 @@ void ErrorObject::method_get_stack(const BuiltinFunction *, Scope &scope, CallDa if (frame.line >= 0) trace += QLatin1Char(':') + QString::number(frame.line); } - This->d()->stack = scope.engine->newString(trace); + This->d()->stack.set(scope.engine, scope.engine->newString(trace)); } scope.result = This->d()->stack; } -void ErrorObject::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - ErrorObject::Data *This = static_cast<ErrorObject::Data *>(that); - if (This->stack) - This->stack->mark(e); - Object::markObjects(that, e); -} - DEFINE_OBJECT_VTABLE(ErrorObject); void Heap::SyntaxErrorObject::init(const Value &msg) @@ -325,9 +317,9 @@ void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, He ScopedObject o(scope); ctor->defineReadonlyProperty(engine->id_prototype(), (o = obj)); ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); - *obj->propertyData(Index_Constructor) = ctor; - *obj->propertyData(Index_Message) = engine->id_empty(); - *obj->propertyData(Index_Name) = engine->newString(QString::fromLatin1(ErrorObject::className(t))); + 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)))); obj->defineDefaultProperty(engine->id_toString(), method_toString, 0); } diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index 12531c9309..d556617b48 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -62,7 +62,12 @@ struct SyntaxErrorObject; namespace Heap { -struct ErrorObject : Object { + +#define ErrorObjectMembers(class, Member) \ + Member(class, Pointer, String *, stack) + +DECLARE_HEAP_OBJECT(ErrorObject, Object) { + DECLARE_MARK_TABLE(ErrorObject); enum ErrorType { Error, EvalError, @@ -72,6 +77,8 @@ struct ErrorObject : Object { TypeError, URIError }; + StackTrace *stackTrace; + ErrorType errorType; void init(); void init(const Value &message, ErrorType t = Error); @@ -80,10 +87,6 @@ struct ErrorObject : Object { delete stackTrace; Object::destroy(); } - - ErrorType errorType; - StackTrace *stackTrace; - Pointer<String> stack; }; struct EvalErrorObject : ErrorObject { @@ -173,7 +176,6 @@ struct ErrorObject: Object { static const char *className(Heap::ErrorObject::ErrorType t); static void method_get_stack(const BuiltinFunction *, Scope &scope, CallData *callData); - static void markObjects(Heap::Base *that, ExecutionEngine *e); }; template<> diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index f78555dbda..4b88d85e68 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -59,7 +59,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, , hasQmlDependencies(function->hasQmlDependencies()) { internalClass = engine->internalClasses[EngineBase::Class_Empty]; - const CompiledData::LEUInt32 *formalsIndices = compiledFunction->formalsTable(); + const quint32_le *formalsIndices = compiledFunction->formalsTable(); // iterate backwards, so we get the right ordering for duplicate names Scope scope(engine); ScopedString arg(scope); @@ -78,14 +78,11 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, } nFormals = compiledFunction->nFormals; - const CompiledData::LEUInt32 *localsIndices = compiledFunction->localsTable(); + 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); - activationRequired = compiledFunction->nInnerFunctions > 0 || (compiledFunction->flags & (CompiledData::Function::HasDirectEval | CompiledData::Function::UsesArgumentsObject)); - - canUseSimpleCall = !needsActivation() && !(compiledFunction->flags & CompiledData::Function::HasCatchOrWith) && - !(compiledFunction->nFormals > QV4::Global::ReservedArgumentCount) && !isNamedExpression(); + canUseSimpleCall = compiledFunction->flags & CompiledData::Function::CanUseSimpleCall; } Function::~Function() @@ -113,11 +110,11 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr } nFormals = parameters.size(); - const CompiledData::LEUInt32 *localsIndices = compiledFunction->localsTable(); + 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); - activationRequired = true; + canUseSimpleCall = false; } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index b212b3d027..8b2d14a0cf 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -69,7 +69,6 @@ struct Q_QML_EXPORT Function { // first nArguments names in internalClass are the actual arguments InternalClass *internalClass; uint nFormals; - bool activationRequired; bool hasQmlDependencies; bool canUseSimpleCall; @@ -90,9 +89,6 @@ struct Q_QML_EXPORT Function { inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; } inline bool isNamedExpression() const { return compiledFunction->flags & CompiledData::Function::IsNamedExpression; } - inline bool needsActivation() const - { return activationRequired; } - inline bool canUseSimpleFunction() const { return canUseSimpleCall; } QQmlSourceLocation sourceLocation() const @@ -103,7 +99,7 @@ struct Q_QML_EXPORT Function { }; -inline unsigned int Heap::CallContext::formalParameterCount() const +inline unsigned int Heap::SimpleCallContext::formalParameterCount() const { return v4Function ? v4Function->nFormals : 0; } diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 63396998fa..2375aae92b 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -69,11 +69,13 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(FunctionObject); +Q_STATIC_ASSERT((Heap::FunctionObject::markTable & Heap::Object::markTable) == Heap::Object::markTable); + void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, bool createProto) { Object::init(); function = nullptr; - this->scope = scope->d(); + this->scope.set(scope->engine(), scope->d()); Scope s(scope->engine()); ScopedFunctionObject f(s, this); f->init(name, createProto); @@ -84,7 +86,7 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function Object::init(); this->function = function; function->compilationUnit->addref(); - this->scope = scope->d(); + this->scope.set(scope->engine(), scope->d()); Scope s(scope->engine()); ScopedString name(s, function->name()); ScopedFunctionObject f(s, this); @@ -102,9 +104,9 @@ void Heap::FunctionObject::init() { Object::init(); function = nullptr; - this->scope = internalClass->engine->rootContext()->d(); + this->scope.set(internalClass->engine, internalClass->engine->rootContext()->d()); Q_ASSERT(internalClass && internalClass->find(internalClass->engine->id_prototype()) == Index_Prototype); - *propertyData(Index_Prototype) = Encode::undefined(); + setProperty(internalClass->engine, Index_Prototype, Primitive::undefinedValue()); } @@ -124,14 +126,14 @@ void FunctionObject::init(String *n, bool createProto) 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->propertyData(Heap::FunctionObject::Index_ProtoConstructor) = this->asReturnedValue(); - *propertyData(Heap::FunctionObject::Index_Prototype) = proto.asReturnedValue(); + proto->setProperty(Heap::FunctionObject::Index_ProtoConstructor, d()); + setProperty(Heap::FunctionObject::Index_Prototype, proto); } else { - *propertyData(Heap::FunctionObject::Index_Prototype) = Encode::undefined(); + setProperty(Heap::FunctionObject::Index_Prototype, Primitive::undefinedValue()); } if (n) - defineReadonlyProperty(s.engine->id_name(), *n); + defineReadonlyConfigurableProperty(s.engine->id_name(), *n); } ReturnedValue FunctionObject::name() const @@ -149,15 +151,6 @@ void FunctionObject::call(const Managed *, Scope &scope, CallData *) scope.result = Encode::undefined(); } -void FunctionObject::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - Heap::FunctionObject *o = static_cast<Heap::FunctionObject *>(that); - if (o->scope) - o->scope->mark(e); - - Object::markObjects(that, e); -} - Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function) { return scope->engine()->memoryManager->allocObject<ScriptFunction>(scope, function); @@ -258,10 +251,10 @@ void FunctionPrototype::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)); - defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(0)); + 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); @@ -309,7 +302,7 @@ void FunctionPrototype::method_apply(const BuiltinFunction *, Scope &scope, Call cData->args[i] = Primitive::undefinedValue(); } else if (arr->arrayType() == Heap::ArrayData::Simple && !arr->protoHasArray()) { auto sad = static_cast<Heap::SimpleArrayData *>(arr->arrayData()); - uint alen = sad ? sad->len : 0; + uint alen = sad ? sad->values.size : 0; if (alen > len) alen = len; for (uint i = 0; i < alen; ++i) @@ -352,8 +345,9 @@ void FunctionPrototype::method_bind(const BuiltinFunction *, Scope &scope, CallD Scoped<MemberData> boundArgs(scope, (Heap::MemberData *)0); if (callData->argc > 1) { boundArgs = MemberData::allocate(scope.engine, callData->argc - 1); - boundArgs->d()->size = callData->argc - 1; - memcpy(boundArgs->data(), callData->args + 1, (callData->argc - 1)*sizeof(Value)); + boundArgs->d()->values.size = callData->argc - 1; + for (uint i = 0; i < static_cast<uint>(callData->argc - 1); ++i) + boundArgs->set(scope.engine, i, callData->args[i + 1]); } ExecutionContext *global = scope.engine->rootContext(); @@ -420,7 +414,7 @@ void ScriptFunction::call(const Managed *that, Scope &scope, CallData *callData) void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function) { FunctionObject::init(); - this->scope = scope->d(); + this->scope.set(scope->engine(), scope->d()); this->function = function; function->compilationUnit->addref(); @@ -433,7 +427,7 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function ScopedString name(s, function->name()); f->init(name, true); Q_ASSERT(internalClass && internalClass->find(s.engine->id_length()) == Index_Length); - *propertyData(Index_Length) = Primitive::fromInt32(f->formalParameterCount()); + setProperty(s.engine, Index_Length, Primitive::fromInt32(f->formalParameterCount())); if (scope->d()->strictMode) { ScopedProperty pd(s); @@ -459,7 +453,6 @@ InternalClass *ScriptFunction::classForConstructor() const return ic; } - DEFINE_OBJECT_VTABLE(BuiltinFunction); void Heap::BuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, void (*code)(const QV4::BuiltinFunction *, Scope &, CallData *)) @@ -497,7 +490,7 @@ void IndexedBuiltinFunction::call(const Managed *that, Scope &scope, CallData *c ExecutionContextSaver ctxSaver(scope); - CallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(); + SimpleCallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(); ctx->strictMode = f->scope()->strictMode; // ### needed? scope or parent context? ctx->callData = callData; v4->pushContext(ctx); @@ -514,12 +507,12 @@ DEFINE_OBJECT_VTABLE(BoundFunction); void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs) { + Scope s(scope); Heap::FunctionObject::init(scope, QStringLiteral("__bound function__")); - this->target = target->d(); - this->boundArgs = boundArgs ? boundArgs->d() : 0; - this->boundThis = boundThis; + this->target.set(s.engine, target->d()); + this->boundArgs.set(s.engine, boundArgs ? boundArgs->d() : 0); + this->boundThis.set(scope->engine(), boundThis); - Scope s(scope); ScopedObject f(s, this); ScopedValue l(s, target->get(s.engine->id_length())); @@ -528,7 +521,7 @@ void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject len -= boundArgs->size(); if (len < 0) len = 0; - f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(len)); + f->defineReadonlyConfigurableProperty(s.engine->id_length(), Primitive::fromInt32(len)); ScopedProperty pd(s); pd->value = s.engine->thrower(); @@ -577,14 +570,3 @@ void BoundFunction::construct(const Managed *that, Scope &scope, CallData *dd) ScopedFunctionObject t(scope, f->target()); t->construct(scope, callData); } - -void BoundFunction::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - BoundFunction::Data *o = static_cast<BoundFunction::Data *>(that); - if (o->target) - o->target->mark(e); - o->boundThis.mark(e); - if (o->boundArgs) - o->boundArgs->mark(e); - FunctionObject::markObjects(that, e); -} diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index d691b869fe..6ce5734b6d 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -65,7 +65,12 @@ struct BuiltinFunction; namespace Heap { -struct Q_QML_PRIVATE_EXPORT FunctionObject : Object { +#define FunctionObjectMembers(class, Member) \ + Member(class, Pointer, ExecutionContext *, scope) \ + Member(class, NoMark, Function *, function) + +DECLARE_HEAP_OBJECT(FunctionObject, Object) { + DECLARE_MARK_TABLE(FunctionObject); enum { Index_Prototype = 0, Index_ProtoConstructor = 0 @@ -79,12 +84,8 @@ struct Q_QML_PRIVATE_EXPORT FunctionObject : Object { unsigned int formalParameterCount() { return function ? function->nFormals : 0; } unsigned int varCount() { return function ? function->compiledFunction->nLocals : 0; } - bool needsActivation() const { return function ? function->needsActivation() : false; } const QV4::Object *protoProperty() const { return propertyData(Index_Prototype)->as<QV4::Object>(); } - - Pointer<ExecutionContext> scope; - Function *function; }; struct FunctionCtor : FunctionObject { @@ -121,11 +122,15 @@ struct ScriptFunction : FunctionObject { QV4::InternalClass *cachedClassForConstructor; }; -struct BoundFunction : FunctionObject { +#define BoundFunctionMembers(class, Member) \ + Member(class, Pointer, FunctionObject *, target) \ + Member(class, HeapValue, HeapValue, boundThis) \ + Member(class, Pointer, MemberData *, boundArgs) + +DECLARE_HEAP_OBJECT(BoundFunction, FunctionObject) { + DECLARE_MARK_TABLE(BoundFunction); + void init(QV4::ExecutionContext *scope, QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs); - Pointer<FunctionObject> target; - Value boundThis; - Pointer<MemberData> boundArgs; }; } @@ -156,14 +161,11 @@ struct Q_QML_EXPORT FunctionObject: Object { static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function); - bool needsActivation() const { return d()->needsActivation(); } bool strictMode() const { return d()->function ? d()->function->isStrict() : false; } bool isBinding() const; bool isBoundFunction() const; QQmlSourceLocation sourceLocation() const; - - static void markObjects(Heap::Base *that, ExecutionEngine *e); }; template<> @@ -251,8 +253,6 @@ struct BoundFunction: FunctionObject { static void construct(const Managed *, Scope &scope, CallData *d); static void call(const Managed *that, Scope &scope, CallData *dd); - - static void markObjects(Heap::Base *that, ExecutionEngine *e); }; } diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index a3ef395076..5cddf2eaa6 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -178,6 +178,7 @@ namespace Heap { struct DataView; struct TypedArray; + template <typename T, size_t> struct Pointer; } class MemoryManager; @@ -192,9 +193,12 @@ struct ScriptFunction; struct InternalClass; struct Property; struct Value; +template<size_t> struct HeapValue; +template<size_t> struct ValueArray; struct Lookup; struct ArrayData; struct VTable; +struct Function; struct BooleanObject; struct NumberObject; @@ -252,6 +256,7 @@ enum PropertyFlag { Attr_NotEnumerable = 0x4, Attr_NotConfigurable = 0x8, Attr_ReadOnly = Attr_NotWritable|Attr_NotEnumerable|Attr_NotConfigurable, + Attr_ReadOnly_ButConfigurable = Attr_NotWritable|Attr_NotEnumerable, Attr_Invalid = 0xff }; diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h index 89af5db731..b0b08f1e54 100644 --- a/src/qml/jsruntime/qv4identifiertable_p.h +++ b/src/qml/jsruntime/qv4identifiertable_p.h @@ -93,14 +93,14 @@ public: Heap::String *stringFromIdentifier(Identifier *i); - void mark(ExecutionEngine *e) { + 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, e); + entry->vtable()->markObjects(entry, markStack); } } }; diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 0bcd510541..603da1df7b 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -133,48 +133,20 @@ InternalClass::InternalClass(const QV4::InternalClass &other) static void insertHoleIntoPropertyData(Object *object, int idx) { - int inlineSize = object->d()->vtable()->nInlineProperties; - int icSize = object->internalClass()->size; - int from = qMax(idx, inlineSize); - int to = from + 1; - if (from < icSize) { - memmove(object->propertyData(to), object->propertyData(from), - (icSize - from - 1) * sizeof(Value)); - } - if (from == idx) - return; - if (inlineSize < icSize) - *object->propertyData(inlineSize) = *object->propertyData(inlineSize - 1); - from = idx; - to = from + 1; - if (from < inlineSize - 1) { - memmove(object->propertyData(to), object->propertyData(from), - (inlineSize - from - 1) * sizeof(Value)); - } + Heap::Object *o = object->d(); + ExecutionEngine *v4 = o->internalClass->engine; + int size = o->internalClass->size; + for (int i = size - 1; i > idx; --i) + o->setProperty(v4, i, *o->propertyData(i - 1)); } static void removeFromPropertyData(Object *object, int idx, bool accessor = false) { - int inlineSize = object->d()->vtable()->nInlineProperties; - int delta = (accessor ? 2 : 1); - int oldSize = object->internalClass()->size + delta; - int to = idx; - int from = to + delta; - if (from < inlineSize) { - memmove(object->propertyData(to), object->d()->propertyData(from), (inlineSize - from)*sizeof(Value)); - to = inlineSize - delta; - from = inlineSize; - } - if (to < inlineSize && from < oldSize) { - Q_ASSERT(from >= inlineSize); - memcpy(object->propertyData(to), object->d()->propertyData(from), (inlineSize - to)*sizeof(Value)); - to = inlineSize; - from = inlineSize + delta; - } - if (from < oldSize) { - Q_ASSERT(to >= inlineSize && from > to); - memmove(object->propertyData(to), object->d()->propertyData(from), (oldSize - to)*sizeof(Value)); - } + Heap::Object *o = object->d(); + ExecutionEngine *v4 = o->internalClass->engine; + int size = o->internalClass->size; + for (int i = idx; i < size; ++i) + o->setProperty(v4, i, *o->propertyData(i + (accessor ? 2 : 1))); } void InternalClass::changeMember(Object *object, String *string, PropertyAttributes data, uint *index) @@ -188,7 +160,7 @@ void InternalClass::changeMember(Object *object, String *string, PropertyAttribu object->setInternalClass(newClass); if (newClass->size > oldClass->size) { Q_ASSERT(newClass->size == oldClass->size + 1); - insertHoleIntoPropertyData(object, idx + 1); + insertHoleIntoPropertyData(object, idx); } else if (newClass->size < oldClass->size) { Q_ASSERT(newClass->size == oldClass->size - 1); removeFromPropertyData(object, idx + 1); @@ -506,9 +478,9 @@ void InternalClass::destroy() } } -void InternalClassPool::markObjects(ExecutionEngine *engine) +void InternalClassPool::markObjects(MarkStack *markStack) { - InternalClass *ic = engine->internalClasses[EngineBase::Class_Empty]; + 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 @@ -519,10 +491,10 @@ void InternalClassPool::markObjects(ExecutionEngine *engine) InternalClass *ic2 = t.lookup; for (auto &t2 : ic2->transitions) { if (t2.flags == InternalClassTransition::PrototypeChange) - t2.lookup->prototype->mark(engine); + t2.lookup->prototype->mark(markStack); } } else if (t.flags == InternalClassTransition::PrototypeChange) { - t.lookup->prototype->mark(engine); + t.lookup->prototype->mark(markStack); } } } diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index 9e8ab9e73e..df17074e72 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -64,6 +64,7 @@ struct String; struct Object; struct Identifier; struct VTable; +struct MarkStack; struct PropertyHashData; struct PropertyHash @@ -305,7 +306,7 @@ private: struct InternalClassPool : public QQmlJS::MemoryPool { - void markObjects(ExecutionEngine *engine); + void markObjects(MarkStack *markStack); }; } diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index 1d571f53f3..0f021c8bd0 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -705,7 +705,7 @@ QString Stringify::Str(const QString &key, const Value &v) if (replacerFunction) { ScopedObject holder(scope, v4->newObject()); - holder->put(scope.engine, QString(), scope.result); + holder->put(scope.engine->id_empty(), scope.result); ScopedCallData callData(scope, 2); callData->args[0] = v4->newString(key); callData->args[1] = scope.result; diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 7a158ece35..afadf59bd5 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -60,7 +60,7 @@ ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttribu if (index != UINT_MAX) { level = i; *attrs = obj->internalClass->propertyData.at(index); - Value *v = obj->propertyData(index); + const Value *v = obj->propertyData(index); return !attrs->isAccessor() ? v->asReturnedValue() : Object::getValue(thisObject, *v, *attrs); } @@ -73,7 +73,7 @@ ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttribu index = obj->internalClass->find(name); if (index != UINT_MAX) { *attrs = obj->internalClass->propertyData.at(index); - Value *v = obj->propertyData(index); + const Value *v = obj->propertyData(index); return !attrs->isAccessor() ? v->asReturnedValue() : Object::getValue(thisObject, *v, *attrs); } @@ -95,7 +95,7 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs if (index != UINT_MAX) { level = i; *attrs = obj->internalClass->propertyData.at(index); - Value *v = obj->propertyData(index); + const Value *v = obj->propertyData(index); return !attrs->isAccessor() ? v->asReturnedValue() : thisObject->getValue(*v, *attrs); } @@ -108,7 +108,7 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs index = obj->internalClass->find(name); if (index != UINT_MAX) { *attrs = obj->internalClass->propertyData.at(index); - Value *v = obj->propertyData(index); + const Value *v = obj->propertyData(index); return !attrs->isAccessor() ? v->asReturnedValue() : thisObject->getValue(*v, *attrs); } @@ -117,20 +117,20 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs return Primitive::emptyValue().asReturnedValue(); } -ReturnedValue Lookup::indexedGetterGeneric(Lookup *l, const Value &object, const Value &index) +ReturnedValue Lookup::indexedGetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index) { uint idx; if (object.isObject() && index.asArrayIndex(idx)) { l->indexedGetter = indexedGetterObjectInt; - return indexedGetterObjectInt(l, object, index); + return indexedGetterObjectInt(l, engine, object, index); } - return indexedGetterFallback(l, object, index); + return indexedGetterFallback(l, engine, object, index); } -ReturnedValue Lookup::indexedGetterFallback(Lookup *l, const Value &object, const Value &index) +ReturnedValue Lookup::indexedGetterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index) { Q_UNUSED(l); - Scope scope(l->engine); + Scope scope(engine); uint idx = 0; bool isInt = index.asArrayIndex(idx); @@ -148,7 +148,7 @@ ReturnedValue Lookup::indexedGetterFallback(Lookup *l, const Value &object, cons if (object.isNullOrUndefined()) { QString message = QStringLiteral("Cannot read property '%1' of %2").arg(index.toQStringNoThrow()).arg(object.toQStringNoThrow()); - return l->engine->throwTypeError(message); + return engine->throwTypeError(message); } o = RuntimeHelpers::convertToObject(scope.engine, object); @@ -174,7 +174,7 @@ ReturnedValue Lookup::indexedGetterFallback(Lookup *l, const Value &object, cons } -ReturnedValue Lookup::indexedGetterObjectInt(Lookup *l, const Value &object, const Value &index) +ReturnedValue Lookup::indexedGetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index) { uint idx; if (index.asArrayIndex(idx)) { @@ -183,7 +183,7 @@ ReturnedValue Lookup::indexedGetterObjectInt(Lookup *l, const Value &object, con 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->len) + if (idx < s->values.size) if (!s->data(idx).isEmpty()) return s->data(idx).asReturnedValue(); } @@ -191,25 +191,25 @@ ReturnedValue Lookup::indexedGetterObjectInt(Lookup *l, const Value &object, con } } - return indexedGetterFallback(l, object, index); + return indexedGetterFallback(l, engine, object, index); } -void Lookup::indexedSetterGeneric(Lookup *l, const Value &object, const Value &index, const Value &v) +void Lookup::indexedSetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v) { if (Object *o = object.objectValue()) { uint idx; if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple && index.asArrayIndex(idx)) { l->indexedSetter = indexedSetterObjectInt; - indexedSetterObjectInt(l, object, index, v); + indexedSetterObjectInt(l, engine, object, index, v); return; } } - indexedSetterFallback(l, object, index, v); + indexedSetterFallback(l, engine, object, index, v); } -void Lookup::indexedSetterFallback(Lookup *l, const Value &object, const Value &index, const Value &value) +void Lookup::indexedSetterFallback(Lookup *, ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { - Scope scope(l->engine); + Scope scope(engine); ScopedObject o(scope, object.toObject(scope.engine)); if (scope.engine->hasException) return; @@ -218,8 +218,8 @@ void Lookup::indexedSetterFallback(Lookup *l, const Value &object, const Value & if (index.asArrayIndex(idx)) { if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) { Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>(); - if (idx < s->len) { - s->data(idx) = value; + if (idx < s->values.size) { + s->setData(engine, idx, value); return; } } @@ -231,7 +231,7 @@ void Lookup::indexedSetterFallback(Lookup *l, const Value &object, const Value & o->put(name, value); } -void Lookup::indexedSetterObjectInt(Lookup *l, const Value &object, const Value &index, const Value &v) +void Lookup::indexedSetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v) { uint idx; if (index.asArrayIndex(idx)) { @@ -240,15 +240,15 @@ void Lookup::indexedSetterObjectInt(Lookup *l, const Value &object, const Value 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->len) { - s->data(idx) = v; + if (idx < s->values.size) { + s->setData(engine, idx, v); return; } } } } } - indexedSetterFallback(l, object, index, v); + indexedSetterFallback(l, engine, object, index, v); } ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object) @@ -380,7 +380,7 @@ ReturnedValue Lookup::getter0MemberData(Lookup *l, ExecutionEngine *engine, cons Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) - return o->memberData->data[l->index].asReturnedValue(); + return o->memberData->values.data()[l->index].asReturnedValue(); } return getterTwoClasses(l, engine, object); } @@ -452,7 +452,7 @@ ReturnedValue Lookup::getter0Inlinegetter0MemberData(Lookup *l, ExecutionEngine if (l->classList[0] == o->internalClass) return o->inlinePropertyData(l->index)->asReturnedValue(); if (l->classList[2] == o->internalClass) - return o->memberData->data[l->index2].asReturnedValue(); + return o->memberData->values.data()[l->index2].asReturnedValue(); } l->getter = getterFallback; return getterFallback(l, engine, object); @@ -465,9 +465,9 @@ ReturnedValue Lookup::getter0MemberDatagetter0MemberData(Lookup *l, ExecutionEng Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) - return o->memberData->data[l->index].asReturnedValue(); + return o->memberData->values.data()[l->index].asReturnedValue(); if (l->classList[2] == o->internalClass) - return o->memberData->data[l->index2].asReturnedValue(); + return o->memberData->values.data()[l->index2].asReturnedValue(); } l->getter = getterFallback; return getterFallback(l, engine, object); @@ -495,7 +495,7 @@ ReturnedValue Lookup::getter0MemberDatagetter1(Lookup *l, ExecutionEngine *engin Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); if (o) { if (l->classList[0] == o->internalClass) - return o->memberData->data[l->index].asReturnedValue(); + return o->memberData->values.data()[l->index].asReturnedValue(); if (l->classList[2] == o->internalClass && l->classList[3] == o->prototype()->internalClass) return o->prototype()->propertyData(l->index2)->asReturnedValue(); } @@ -611,7 +611,7 @@ ReturnedValue Lookup::primitiveGetter0MemberData(Lookup *l, ExecutionEngine *eng if (object.type() == l->type) { Heap::Object *o = l->proto; if (l->classList[0] == o->internalClass) - return o->memberData->data[l->index].asReturnedValue(); + return o->memberData->values.data()[l->index].asReturnedValue(); } l->getter = getterGeneric; return getterGeneric(l, engine, object); @@ -738,7 +738,7 @@ ReturnedValue Lookup::globalGetter0MemberData(Lookup *l, ExecutionEngine *engine { Object *o = engine->globalObject; if (l->classList[0] == o->internalClass()) - return o->d()->memberData->data[l->index].asReturnedValue(); + return o->d()->memberData->values.data()[l->index].asReturnedValue(); l->globalGetter = globalGetterGeneric; return globalGetterGeneric(l, engine); @@ -880,7 +880,7 @@ void Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Va { Object *o = static_cast<Object *>(object.managed()); if (o && o->internalClass() == l->classList[0]) { - *o->propertyData(l->index) = value; + o->setProperty(engine, l->index, value); return; } @@ -891,7 +891,7 @@ void Lookup::setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, co { Object *o = static_cast<Object *>(object.managed()); if (o && o->internalClass() == l->classList[0]) { - *o->d()->inlinePropertyData(l->index) = value; + o->d()->setInlineProperty(engine, l->index, value); return; } @@ -904,7 +904,7 @@ void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, co if (o && o->internalClass() == l->classList[0]) { Q_ASSERT(!o->prototype()); o->setInternalClass(l->classList[3]); - *o->propertyData(l->index) = value; + o->setProperty(l->index, value); return; } @@ -921,7 +921,7 @@ void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, co if (p->internalClass == l->classList[1]) { Q_ASSERT(!p->prototype()); o->setInternalClass(l->classList[3]); - *o->propertyData(l->index) = value; + o->setProperty(l->index, value); return; } } @@ -942,7 +942,7 @@ void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, co if (p->internalClass == l->classList[2]) { Q_ASSERT(!p->prototype()); o->setInternalClass(l->classList[3]); - *o->propertyData(l->index) = value; + o->setProperty(l->index, value); return; } } @@ -957,11 +957,11 @@ void Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, c Object *o = static_cast<Object *>(object.managed()); if (o) { if (o->internalClass() == l->classList[0]) { - *o->propertyData(l->index) = value; + o->setProperty(l->index, value); return; } if (o->internalClass() == l->classList[1]) { - *o->propertyData(l->index2) = value; + o->setProperty(l->index2, value); return; } } diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 151231991f..ce5189a780 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -67,14 +67,13 @@ namespace QV4 { struct Lookup { enum { Size = 4 }; union { - ReturnedValue (*indexedGetter)(Lookup *l, const Value &object, const Value &index); - void (*indexedSetter)(Lookup *l, const Value &object, const Value &index, const Value &v); + ReturnedValue (*indexedGetter)(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index); + void (*indexedSetter)(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v); ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object); ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine); void (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v); }; union { - ExecutionEngine *engine; InternalClass *classList[Size]; struct { void *dummy0; @@ -91,13 +90,13 @@ struct Lookup { uint index; uint nameIndex; - static ReturnedValue indexedGetterGeneric(Lookup *l, const Value &object, const Value &index); - static ReturnedValue indexedGetterFallback(Lookup *l, const Value &object, const Value &index); - static ReturnedValue indexedGetterObjectInt(Lookup *l, const Value &object, const Value &index); + static ReturnedValue indexedGetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index); + static ReturnedValue indexedGetterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index); + static ReturnedValue indexedGetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index); - static void indexedSetterGeneric(Lookup *l, const Value &object, const Value &index, const Value &v); - static void indexedSetterFallback(Lookup *l, const Value &object, const Value &index, const Value &value); - static void indexedSetterObjectInt(Lookup *l, const Value &object, const Value &index, const Value &v); + static void indexedSetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v); + static void indexedSetterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &value); + static void indexedSetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v); static ReturnedValue getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object); @@ -154,7 +153,6 @@ Q_STATIC_ASSERT(std::is_standard_layout<Lookup>::value); // across 32-bit and 64-bit (matters when cross-compiling). Q_STATIC_ASSERT(offsetof(Lookup, indexedGetter) == 0); Q_STATIC_ASSERT(offsetof(Lookup, getter) == 0); -Q_STATIC_ASSERT(offsetof(Lookup, engine) == offsetof(Lookup, getter) + QT_POINTER_SIZE); } diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp index 200380eda0..e00eaa0d9b 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -49,6 +49,7 @@ const VTable Managed::static_vtbl = 0, 0, 0, + 0, Managed::IsExecutionContext, Managed::IsString, Managed::IsObject, diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index 6859334797..40dfc244ea 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -54,6 +54,7 @@ #include "qv4value_p.h" #include "qv4enginebase_p.h" #include <private/qv4heap_p.h> +#include <private/qv4writebarrier_p.h> QT_BEGIN_NAMESPACE @@ -92,6 +93,7 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} dptr->_checkIsInitialized(); \ return dptr; \ } \ + static Q_CONSTEXPR quint64 markTable = QV4::Heap::DataClass::markTable; \ V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass) #define V4_MANAGED(DataClass, superClass) \ @@ -130,6 +132,7 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} #define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \ { \ parentVTable, \ + markTable, \ (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ (sizeof(classname::Data) + (std::is_same<classname, Object>::value ? 2*sizeof(QV4::Value) : 0) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \ - (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \ @@ -143,7 +146,7 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} classname::MyType, \ #classname, \ Q_VTABLE_FUNCTION(classname, destroy), \ - markObjects, \ + Q_VTABLE_FUNCTION(classname, markObjects), \ isEqualTo \ } @@ -215,8 +218,10 @@ public: bool inUse() const { return d()->inUse(); } bool markBit() const { return d()->isMarked(); } + inline void mark(MarkStack *markStack); static void destroy(Heap::Base *) {} + static void markObjects(Heap::Base *, MarkStack *) {} Q_ALWAYS_INLINE Heap::Base *heapObject() const { return m(); diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp index 109d6c38b5..8782e6deae 100644 --- a/src/qml/jsruntime/qv4mathobject.cpp +++ b/src/qml/jsruntime/qv4mathobject.cpp @@ -42,6 +42,7 @@ #include <QtCore/qdatetime.h> #include <QtCore/qmath.h> +#include <QtCore/qrandom.h> #include <QtCore/private/qnumeric_p.h> #include <QtCore/qthreadstorage.h> @@ -272,20 +273,9 @@ void MathObject::method_pow(const BuiltinFunction *, Scope &scope, CallData *cal RETURN_RESULT(Encode(qt_qnan())); } -Q_GLOBAL_STATIC(QThreadStorage<bool *>, seedCreatedStorage); - void MathObject::method_random(const BuiltinFunction *, Scope &scope, CallData *) { - if (!seedCreatedStorage()->hasLocalData()) { - int msecs = QTime(0,0,0).msecsTo(QTime::currentTime()); - Q_ASSERT(msecs >= 0); - qsrand(uint(uint(msecs) ^ reinterpret_cast<quintptr>(scope.engine))); - seedCreatedStorage()->setLocalData(new bool(true)); - } - // rand()/qrand() return a value where the upperbound is RAND_MAX inclusive. So, instead of - // dividing by RAND_MAX (which would return 0..RAND_MAX inclusive), we divide by RAND_MAX + 1. - qint64 upperLimit = qint64(RAND_MAX) + 1; - RETURN_RESULT(Encode(qrand() / double(upperLimit))); + RETURN_RESULT(Encode(QRandomGenerator::global()->generateDouble())); } void MathObject::method_round(const BuiltinFunction *, Scope &scope, CallData *callData) diff --git a/src/qml/jsruntime/qv4memberdata.cpp b/src/qml/jsruntime/qv4memberdata.cpp index db45c77472..8f862d63e9 100644 --- a/src/qml/jsruntime/qv4memberdata.cpp +++ b/src/qml/jsruntime/qv4memberdata.cpp @@ -45,24 +45,19 @@ using namespace QV4; DEFINE_MANAGED_VTABLE(MemberData); -void MemberData::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - Heap::MemberData *m = static_cast<Heap::MemberData *>(that); - for (uint i = 0; i < m->size; ++i) - m->data[i].mark(e); -} - Heap::MemberData *MemberData::allocate(ExecutionEngine *e, uint n, Heap::MemberData *old) { - Q_ASSERT(!old || old->size < n); + Q_ASSERT(!old || old->values.size < n); Q_ASSERT(n); size_t alloc = MemoryManager::align(sizeof(Heap::MemberData) + (n - 1)*sizeof(Value)); Heap::MemberData *m = e->memoryManager->allocManaged<MemberData>(alloc); if (old) - memcpy(m, old, sizeof(Heap::MemberData) + (old->size - 1)* sizeof(Value)); + // no write barrier required here + memcpy(m, old, sizeof(Heap::MemberData) + (old->values.size - 1) * sizeof(Value)); else m->init(); - m->size = static_cast<uint>((alloc - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value)); + m->values.alloc = static_cast<uint>((alloc - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value)); + m->values.size = m->values.alloc; return m; } diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h index e239458849..bae524d088 100644 --- a/src/qml/jsruntime/qv4memberdata_p.h +++ b/src/qml/jsruntime/qv4memberdata_p.h @@ -59,12 +59,11 @@ namespace QV4 { namespace Heap { -struct MemberData : Base { - union { - uint size; - double _dummy; - }; - Value data[1]; +#define MemberDataMembers(class, Member) \ + Member(class, ValueArray, ValueArray, values) + +DECLARE_HEAP_OBJECT(MemberData, Base) { + DECLARE_MARK_TABLE(MemberData); }; V4_ASSERT_IS_TRIVIAL(MemberData) @@ -75,14 +74,26 @@ struct MemberData : Managed V4_MANAGED(MemberData, Managed) V4_INTERNALCLASS(MemberData) - Value &operator[] (uint idx) { return d()->data[idx]; } - const Value *data() const { return d()->data; } - Value *data() { return d()->data; } - inline uint size() const { return d()->size; } + struct Index { + Heap::Base *base; + Value *slot; - static Heap::MemberData *allocate(QV4::ExecutionEngine *e, uint n, Heap::MemberData *old = 0); + void set(EngineBase *e, Value newVal) { + WriteBarrier::write(e, base, slot, newVal); + } + const Value *operator->() const { return slot; } + const Value &operator*() const { return *slot; } + bool isNull() const { return !slot; } + }; - static void markObjects(Heap::Base *that, ExecutionEngine *e); + 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); } + void set(EngineBase *e, uint index, Heap::Base *b) { d()->values.set(e, index, b); } + + inline uint size() const { return d()->values.size; } + + static Heap::MemberData *allocate(QV4::ExecutionEngine *e, uint n, Heap::MemberData *old = 0); }; } diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index 255d0212c1..d5c80dbf80 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -102,6 +102,8 @@ void NumberPrototype::init(ExecutionEngine *engine, Object *ctor) ctor->defineReadonlyProperty(QStringLiteral("POSITIVE_INFINITY"), Primitive::fromDouble(qInf())); ctor->defineReadonlyProperty(QStringLiteral("MAX_VALUE"), Primitive::fromDouble(1.7976931348623158e+308)); ctor->defineReadonlyProperty(QStringLiteral("EPSILON"), Primitive::fromDouble(std::numeric_limits<double>::epsilon())); + ctor->defineReadonlyProperty(QStringLiteral("MAX_SAFE_INTEGER"), Primitive::fromDouble(9007199254740991)); + ctor->defineReadonlyProperty(QStringLiteral("MIN_SAFE_INTEGER"), Primitive::fromDouble(-9007199254740991)); QT_WARNING_PUSH QT_WARNING_DISABLE_INTEL(239) @@ -109,15 +111,17 @@ QT_WARNING_DISABLE_INTEL(239) QT_WARNING_POP ctor->defineDefaultProperty(QStringLiteral("isFinite"), method_isFinite, 1); + ctor->defineDefaultProperty(QStringLiteral("isInteger"), method_isInteger, 1); + ctor->defineDefaultProperty(QStringLiteral("isSafeInteger"), method_isSafeInteger, 1); ctor->defineDefaultProperty(QStringLiteral("isNaN"), method_isNaN, 1); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - defineDefaultProperty(engine->id_toString(), method_toString); + defineDefaultProperty(engine->id_toString(), method_toString, 1); defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString); defineDefaultProperty(engine->id_valueOf(), method_valueOf); defineDefaultProperty(QStringLiteral("toFixed"), method_toFixed, 1); - defineDefaultProperty(QStringLiteral("toExponential"), method_toExponential); - defineDefaultProperty(QStringLiteral("toPrecision"), method_toPrecision); + defineDefaultProperty(QStringLiteral("toExponential"), method_toExponential, 1); + defineDefaultProperty(QStringLiteral("toPrecision"), method_toPrecision, 1); } inline ReturnedValue thisNumberValue(Scope &scope, CallData *callData) @@ -155,6 +159,52 @@ void NumberPrototype::method_isFinite(const BuiltinFunction *, Scope &scope, Cal scope.result = Encode(!std::isnan(v) && !qt_is_inf(v)); } +void NumberPrototype::method_isInteger(const BuiltinFunction *, Scope &scope, CallData *callData) +{ + if (!callData->argc) { + scope.result = Encode(false); + return; + } + + const Value &v = callData->args[0]; + if (!v.isNumber()) { + scope.result = Encode(false); + return; + } + + double dv = v.toNumber(); + if (std::isnan(dv) || qt_is_inf(dv)) { + scope.result = Encode(false); + return; + } + + double iv = v.toInteger(); + scope.result = Encode(dv == iv); +} + +void NumberPrototype::method_isSafeInteger(const BuiltinFunction *, Scope &scope, CallData *callData) +{ + if (!callData->argc) { + scope.result = Encode(false); + return; + } + + const Value &v = callData->args[0]; + if (!v.isNumber()) { + scope.result = Encode(false); + return; + } + + double dv = v.toNumber(); + if (std::isnan(dv) || qt_is_inf(dv)) { + scope.result = Encode(false); + return; + } + + double iv = v.toInteger(); + scope.result = Encode(dv == iv && std::fabs(iv) <= (2^53)-1); +} + void NumberPrototype::method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData) { if (!callData->argc) { diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h index 85d306020c..0bc2cd8c65 100644 --- a/src/qml/jsruntime/qv4numberobject_p.h +++ b/src/qml/jsruntime/qv4numberobject_p.h @@ -89,6 +89,8 @@ struct NumberPrototype: NumberObject void init(ExecutionEngine *engine, Object *ctor); static void method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData); + static void method_isInteger(const BuiltinFunction *, Scope &scope, CallData *callData); + static void method_isSafeInteger(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 98f5c7464f..94d5a74fe5 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -67,8 +67,8 @@ void Object::setInternalClass(InternalClass *ic) return; bool hasMD = d()->memberData != nullptr; uint requiredSize = ic->size - nInline; - if (!hasMD || (hasMD && d()->memberData->size < requiredSize)) - d()->memberData = MemberData::allocate(ic->engine, requiredSize, d()->memberData); + if (!(hasMD && requiredSize) || (hasMD && d()->memberData->values.size < requiredSize)) + d()->memberData.set(ic->engine, MemberData::allocate(ic->engine, requiredSize, d()->memberData)); } void Object::getProperty(uint index, Property *p, PropertyAttributes *attrs) const @@ -81,9 +81,9 @@ void Object::getProperty(uint index, Property *p, PropertyAttributes *attrs) con void Object::setProperty(uint index, const Property *p) { - *propertyData(index) = p->value; + setProperty(index, p->value); if (internalClass()->propertyData.at(index).isAccessor()) - *propertyData(index + SetterOffset) = p->set; + setProperty(index + SetterOffset, p->set); } bool Object::setPrototype(Object *proto) @@ -99,13 +99,6 @@ bool Object::setPrototype(Object *proto) return true; } -void Object::put(ExecutionEngine *engine, const QString &name, const Value &value) -{ - Scope scope(engine); - ScopedString n(scope, engine->newString(name)); - put(n, value); -} - ReturnedValue Object::getValue(const Value &thisObject, const Value &v, PropertyAttributes attrs) { if (!attrs.isAccessor()) @@ -121,16 +114,16 @@ ReturnedValue Object::getValue(const Value &thisObject, const Value &v, Property return scope.result.asReturnedValue(); } -void Object::putValue(uint memberIndex, const Value &value) +bool Object::putValue(uint memberIndex, const Value &value) { QV4::InternalClass *ic = internalClass(); if (ic->engine->hasException) - return; + return false; PropertyAttributes attrs = ic->propertyData[memberIndex]; if (attrs.isAccessor()) { - FunctionObject *set = propertyData(memberIndex + SetterOffset)->as<FunctionObject>(); + const FunctionObject *set = propertyData(memberIndex + SetterOffset)->as<FunctionObject>(); if (set) { Scope scope(ic->engine); ScopedFunctionObject setter(scope, set); @@ -138,7 +131,7 @@ void Object::putValue(uint memberIndex, const Value &value) callData->args[0] = value; callData->thisObject = this; setter->call(scope, callData); - return; + return !ic->engine->hasException; } goto reject; } @@ -146,12 +139,13 @@ void Object::putValue(uint memberIndex, const Value &value) if (!attrs.isWritable()) goto reject; - *propertyData(memberIndex) = value; - return; + setProperty(memberIndex, value); + return true; reject: if (engine()->current->strictMode) engine()->throwTypeError(); + return false; } void Object::defineDefaultProperty(const QString &name, const Value &value) @@ -169,7 +163,7 @@ void Object::defineDefaultProperty(const QString &name, void (*code)(const Built ScopedString s(scope, e->newIdentifier(name)); ExecutionContext *global = e->rootContext(); ScopedFunctionObject function(scope, BuiltinFunction::create(global, s, code)); - function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); + function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); defineDefaultProperty(s, function); } @@ -179,7 +173,7 @@ void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunct Scope scope(e); ExecutionContext *global = e->rootContext(); ScopedFunctionObject function(scope, BuiltinFunction::create(global, name, code)); - function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); + function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); defineDefaultProperty(name, function); } @@ -217,19 +211,27 @@ void Object::defineReadonlyProperty(String *name, const Value &value) insertMember(name, value, Attr_ReadOnly); } -void Object::markObjects(Heap::Base *that, ExecutionEngine *e) +void Object::defineReadonlyConfigurableProperty(const QString &name, const Value &value) { - Heap::Object *o = static_cast<Heap::Object *>(that); + QV4::ExecutionEngine *e = engine(); + Scope scope(e); + ScopedString s(scope, e->newIdentifier(name)); + defineReadonlyConfigurableProperty(s, value); +} - if (o->memberData) - o->memberData->mark(e); - if (o->arrayData) - o->arrayData->mark(e); +void Object::defineReadonlyConfigurableProperty(String *name, const Value &value) +{ + insertMember(name, value, Attr_ReadOnly_ButConfigurable); +} + +void Object::markObjects(Heap::Base *b, MarkStack *stack) +{ + Heap::Object *o = static_cast<Heap::Object *>(b); uint nInline = o->vtable()->nInlineProperties; Value *v = reinterpret_cast<Value *>(o) + o->vtable()->inlinePropertyOffset; const Value *end = v + nInline; while (v < end) { - v->mark(e); + v->mark(stack); ++v; } } @@ -240,10 +242,10 @@ void Object::insertMember(String *s, const Property *p, PropertyAttributes attri InternalClass::addMember(this, s, attributes, &idx); if (attributes.isAccessor()) { - *propertyData(idx + GetterOffset) = p->value; - *propertyData(idx + SetterOffset) = p->set; + setProperty(idx + GetterOffset, p->value); + setProperty(idx + SetterOffset, p->set); } else { - *propertyData(idx) = p->value; + setProperty(idx, p->value); } } @@ -275,12 +277,9 @@ void Object::getOwnProperty(String *name, PropertyAttributes *attrs, Property *p void Object::getOwnProperty(uint index, PropertyAttributes *attrs, Property *p) { - Property *pd = arrayData() ? arrayData()->getProperty(index) : 0; - if (pd) { - *attrs = arrayData()->attributes(index); - if (p) - p->copy(pd, *attrs); - return; + if (arrayData()) { + if (arrayData()->getProperty(index, p, attrs)) + return; } if (isStringObject()) { *attrs = Attr_NotConfigurable|Attr_NotWritable; @@ -295,7 +294,7 @@ void Object::getOwnProperty(uint index, PropertyAttributes *attrs, Property *p) } // Section 8.12.2 -Value *Object::getValueOrSetter(String *name, PropertyAttributes *attrs) +MemberData::Index Object::getValueOrSetter(String *name, PropertyAttributes *attrs) { Q_ASSERT(name->asArrayIndex() == UINT_MAX); @@ -307,36 +306,38 @@ Value *Object::getValueOrSetter(String *name, PropertyAttributes *attrs) uint idx = o->internalClass->find(id); if (idx < UINT_MAX) { *attrs = o->internalClass->propertyData[idx]; - return o->propertyData(attrs->isAccessor() ? idx + SetterOffset : idx); + return o->writablePropertyData(attrs->isAccessor() ? idx + SetterOffset : idx ); } o = o->prototype(); } *attrs = Attr_Invalid; - return 0; + return { 0, 0 }; } -Value *Object::getValueOrSetter(uint index, PropertyAttributes *attrs) +ArrayData::Index Object::getValueOrSetter(uint index, PropertyAttributes *attrs) { Heap::Object *o = d(); while (o) { - Property *p = o->arrayData ? o->arrayData->getProperty(index) : 0; - if (p) { - *attrs = o->arrayData->attributes(index); - return attrs->isAccessor() ? &p->set : &p->value; + 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 (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<Value *>(0x1); + return { reinterpret_cast<Heap::ArrayData *>(0x1), 0 }; } } o = o->prototype(); } *attrs = Attr_Invalid; - return 0; + return { 0, 0 }; } bool Object::hasProperty(String *name) const @@ -421,14 +422,14 @@ ReturnedValue Object::getIndexed(const Managed *m, uint index, bool *hasProperty return static_cast<const Object *>(m)->internalGetIndexed(index, hasProperty); } -void Object::put(Managed *m, String *name, const Value &value) +bool Object::put(Managed *m, String *name, const Value &value) { - static_cast<Object *>(m)->internalPut(name, value); + return static_cast<Object *>(m)->internalPut(name, value); } -void Object::putIndexed(Managed *m, uint index, const Value &value) +bool Object::putIndexed(Managed *m, uint index, const Value &value) { - static_cast<Object *>(m)->internalPutIndexed(index, value); + return static_cast<Object *>(m)->internalPutIndexed(index, value); } PropertyAttributes Object::query(const Managed *m, String *name) @@ -524,7 +525,7 @@ void Object::setLookup(Managed *m, Lookup *l, const Value &value) l->classList[0] = o->internalClass(); l->index = idx; l->setter = idx < o->d()->vtable()->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0; - *o->propertyData(idx) = value; + o->setProperty(idx, value); return; } @@ -579,7 +580,7 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint * int k = it->arrayNode->key(); uint pidx = it->arrayNode->value; Heap::SparseArrayData *sa = o->d()->arrayData.cast<Heap::SparseArrayData>(); - Property *p = reinterpret_cast<Property *>(sa->arrayData + pidx); + const Property *p = reinterpret_cast<const Property *>(sa->values.data() + pidx); it->arrayNode = it->arrayNode->nextNode(); PropertyAttributes a = sa->attrs ? sa->attrs[pidx] : Attr_Data; if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { @@ -594,9 +595,9 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint * it->arrayIndex = UINT_MAX; } // dense arrays - while (it->arrayIndex < o->d()->arrayData->len) { + while (it->arrayIndex < o->d()->arrayData->values.size) { Heap::SimpleArrayData *sa = o->d()->arrayData.cast<Heap::SimpleArrayData>(); - Value &val = sa->data(it->arrayIndex); + const Value &val = sa->data(it->arrayIndex); PropertyAttributes a = o->arrayData()->attributes(it->arrayIndex); ++it->arrayIndex; if (!val.isEmpty() @@ -663,15 +664,14 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) const ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const { - Property *pd = 0; PropertyAttributes attrs; Scope scope(engine()); ScopedObject o(scope, this); + ScopedProperty pd(scope); + bool exists = false; while (o) { - Property *p = o->arrayData() ? o->arrayData()->getProperty(index) : 0; - if (p) { - pd = p; - attrs = o->arrayData()->attributes(index); + if (o->arrayData() && o->arrayData()->getProperty(index, pd, &attrs)) { + exists = true; break; } if (o->isStringObject()) { @@ -686,7 +686,7 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const o = o->prototype(); } - if (pd) { + if (exists) { if (hasProperty) *hasProperty = true; return getValue(pd->value, attrs); @@ -699,10 +699,11 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const // Section 8.12.5 -void Object::internalPut(String *name, const Value &value) +bool Object::internalPut(String *name, const Value &value) { - if (internalClass()->engine->hasException) - return; + ExecutionEngine *engine = this->engine(); + if (engine->hasException) + return false; uint idx = name->asArrayIndex(); if (idx != UINT_MAX) @@ -711,45 +712,46 @@ void Object::internalPut(String *name, const Value &value) name->makeIdentifier(); Identifier *id = name->identifier(); + MemberData::Index memberIndex{0, 0}; uint member = internalClass()->find(id); - Value *v = 0; PropertyAttributes attrs; if (member < UINT_MAX) { attrs = internalClass()->propertyData[member]; - v = propertyData(attrs.isAccessor() ? member + SetterOffset : member); + memberIndex = d()->writablePropertyData(attrs.isAccessor() ? member + SetterOffset : member); } // clause 1 - if (v) { + if (!memberIndex.isNull()) { if (attrs.isAccessor()) { - if (v->as<FunctionObject>()) + if (memberIndex->as<FunctionObject>()) goto cont; goto reject; } else if (!attrs.isWritable()) goto reject; - else if (isArrayObject() && name->equals(engine()->id_length())) { + else if (isArrayObject() && name->equals(engine->id_length())) { bool ok; uint l = value.asArrayLength(&ok); if (!ok) { - engine()->throwRangeError(value); - return; + engine->throwRangeError(value); + return false; } ok = setArrayLength(l); if (!ok) goto reject; } else { - *v = value; + memberIndex.set(engine, value); } - return; + return true; } else if (!prototype()) { if (!isExtensible()) goto reject; } else { // clause 4 - Scope scope(engine()); - if ((v = ScopedObject(scope, prototype())->getValueOrSetter(name, &attrs))) { + Scope scope(engine); + memberIndex = ScopedObject(scope, prototype())->getValueOrSetter(name, &attrs); + if (!memberIndex.isNull()) { if (attrs.isAccessor()) { - if (!v->as<FunctionObject>()) + if (!memberIndex->as<FunctionObject>()) goto reject; } else if (!isExtensible() || !attrs.isWritable()) { goto reject; @@ -762,64 +764,68 @@ void Object::internalPut(String *name, const Value &value) cont: // Clause 5 - if (v && attrs.isAccessor()) { - Q_ASSERT(v->as<FunctionObject>()); + if (!memberIndex.isNull() && attrs.isAccessor()) { + Q_ASSERT(memberIndex->as<FunctionObject>()); - Scope scope(engine()); - ScopedFunctionObject setter(scope, *v); + Scope scope(engine); + ScopedFunctionObject setter(scope, *memberIndex); ScopedCallData callData(scope, 1); callData->args[0] = value; callData->thisObject = this; setter->call(scope, callData); - return; + return !internalClass()->engine->hasException; } insertMember(name, value); - return; + return true; reject: - if (engine()->current->strictMode) { + // ### this should be removed once everything is ported to use Object::set() + if (engine->current->strictMode) { QString message = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); - engine()->throwTypeError(message); + engine->throwTypeError(message); } + return false; } -void Object::internalPutIndexed(uint index, const Value &value) +bool Object::internalPutIndexed(uint index, const Value &value) { - if (internalClass()->engine->hasException) - return; + ExecutionEngine *engine = this->engine(); + if (engine->hasException) + return false; PropertyAttributes attrs; - Value *v = arrayData() ? arrayData()->getValueOrSetter(index, &attrs) : 0; + ArrayData::Index arrayIndex = arrayData() ? arrayData()->getValueOrSetter(index, &attrs) : ArrayData::Index{ 0, 0 }; - if (!v && isStringObject()) { + if (arrayIndex.isNull() && isStringObject()) { if (index < static_cast<StringObject *>(this)->length()) // not writable goto reject; } // clause 1 - if (v) { + if (!arrayIndex.isNull()) { if (attrs.isAccessor()) { - if (v->as<FunctionObject>()) + if (arrayIndex->as<FunctionObject>()) goto cont; goto reject; } else if (!attrs.isWritable()) goto reject; - else - *v = value; - return; + + arrayIndex.set(engine, value); + return true; } else if (!prototype()) { if (!isExtensible()) goto reject; } else { // clause 4 - Scope scope(engine()); - if ((v = ScopedObject(scope, prototype())->getValueOrSetter(index, &attrs))) { + Scope scope(engine); + arrayIndex = ScopedObject(scope, prototype())->getValueOrSetter(index, &attrs); + if (!arrayIndex.isNull()) { if (attrs.isAccessor()) { - if (!v->as<FunctionObject>()) + if (!arrayIndex->as<FunctionObject>()) goto reject; } else if (!isExtensible() || !attrs.isWritable()) { goto reject; @@ -832,24 +838,26 @@ void Object::internalPutIndexed(uint index, const Value &value) cont: // Clause 5 - if (v && attrs.isAccessor()) { - Q_ASSERT(v->as<FunctionObject>()); + if (!arrayIndex.isNull() && attrs.isAccessor()) { + Q_ASSERT(arrayIndex->as<FunctionObject>()); - Scope scope(engine()); - ScopedFunctionObject setter(scope, *v); + Scope scope(engine); + ScopedFunctionObject setter(scope, *arrayIndex); ScopedCallData callData(scope, 1); callData->args[0] = value; callData->thisObject = this; setter->call(scope, callData); - return; + return !internalClass()->engine->hasException; } arraySet(index, value); - return; + return true; reject: - if (engine()->current->strictMode) - engine()->throwTypeError(); + // ### this should be removed once everything is ported to use Object::setIndexed() + if (engine->current->strictMode) + engine->throwTypeError(); + return false; } // Section 8.12.7 @@ -978,8 +986,8 @@ bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Prope // Clause 1 if (arrayData()) { - hasProperty = arrayData()->getProperty(index); - if (!hasProperty && isStringObject()) + hasProperty = arrayData()->mappedIndex(index) != UINT_MAX; + if (!hasProperty && isStringObject()) hasProperty = (index < static_cast<StringObject *>(this)->length()); } @@ -1091,7 +1099,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * setProperty(index, current); } else { setArrayAttributes(index, cattrs); - arrayData()->setProperty(index, current); + arrayData()->setProperty(scope.engine, index, current); } return true; reject: @@ -1127,7 +1135,8 @@ void Object::copyArrayData(Object *other) ; } else { Q_ASSERT(!arrayData() && other->arrayData()); - ArrayData::realloc(this, other->d()->arrayData->type, other->d()->arrayData->alloc, false); + ArrayData::realloc(this, static_cast<ArrayData::Type>(other->d()->arrayData->type), + other->d()->arrayData->values.alloc, false); if (other->arrayType() == Heap::ArrayData::Sparse) { Heap::ArrayData *od = other->d()->arrayData; Heap::ArrayData *dd = d()->arrayData; @@ -1135,10 +1144,11 @@ void Object::copyArrayData(Object *other) dd->freeList = od->freeList; } else { Heap::ArrayData *dd = d()->arrayData; - dd->len = other->d()->arrayData->len; + dd->values.size = other->d()->arrayData->values.size; dd->offset = other->d()->arrayData->offset; } - memcpy(d()->arrayData->arrayData, other->d()->arrayData->arrayData, other->d()->arrayData->alloc*sizeof(Value)); + // ### need a write barrier + memcpy(d()->arrayData->values.values, other->d()->arrayData->values.values, other->d()->arrayData->values.alloc*sizeof(Value)); } setArrayLengthUnchecked(other->getLength()); } diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 9592ff5d1b..066a93cc61 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -67,7 +67,12 @@ struct BuiltinFunction; namespace Heap { -struct Object : Base { +#define ObjectMembers(class, Member) \ + Member(class, Pointer, MemberData *, memberData) \ + Member(class, Pointer, ArrayData *, arrayData) + +DECLARE_HEAP_OBJECT(Object, Base) { + DECLARE_MARK_TABLE(Object); void init() { Base::init(); } void destroy() { Base::destroy(); } @@ -75,9 +80,23 @@ struct Object : Base { Q_ASSERT(index < vtable()->nInlineProperties); return reinterpret_cast<const Value *>(this) + vtable()->inlinePropertyOffset + index; } - Value *inlinePropertyData(uint index) { + void setInlineProperty(ExecutionEngine *e, uint index, Value v) { + Q_ASSERT(index < vtable()->nInlineProperties); + Value *prop = reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index; + WriteBarrier::write(e, this, prop, v); + } + void setInlineProperty(ExecutionEngine *e, uint index, Heap::Base *b) { Q_ASSERT(index < vtable()->nInlineProperties); - return reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index; + Value *prop = reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index; + WriteBarrier::write(e, this, prop, b); + } + + QV4::MemberData::Index writablePropertyData(uint index) { + uint nInline = vtable()->nInlineProperties; + if (index < nInline) + return { this, reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index}; + index -= nInline; + return { memberData, memberData->values.values + index }; } const Value *propertyData(uint index) const { @@ -85,21 +104,32 @@ struct Object : Base { if (index < nInline) return reinterpret_cast<const Value *>(this) + vtable()->inlinePropertyOffset + index; index -= nInline; - return memberData->data + index; + return memberData->values.data() + index; } - Value *propertyData(uint index) { + void setProperty(ExecutionEngine *e, uint index, Value v) { uint nInline = vtable()->nInlineProperties; - if (index < nInline) - return reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index; + if (index < nInline) { + setInlineProperty(e, index, v); + return; + } + index -= nInline; + memberData->values.set(e, index, v); + } + void setProperty(ExecutionEngine *e, uint index, Heap::Base *b) { + uint nInline = vtable()->nInlineProperties; + if (index < nInline) { + setInlineProperty(e, index, b); + return; + } index -= nInline; - return memberData->data + index; + memberData->values.set(e, index, b); } Heap::Object *prototype() const { return internalClass->prototype; } - Pointer<MemberData> memberData; - Pointer<ArrayData> arrayData; }; +Q_STATIC_ASSERT(Object::markTable == ((2 << 2) | (2 << 4))); + } #define V4_OBJECT(superClass) \ @@ -134,7 +164,8 @@ struct Object : Base { dptr->_checkIsInitialized(); \ return dptr; \ } \ - V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass); + V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass); \ + static Q_CONSTEXPR quint64 markTable = QV4::Heap::DataClass::markTable; #define V4_PROTOTYPE(p) \ static QV4::Object *defaultPrototype(QV4::ExecutionEngine *e) \ @@ -147,8 +178,8 @@ struct ObjectVTable void (*construct)(const Managed *, Scope &scope, CallData *data); ReturnedValue (*get)(const Managed *, String *name, bool *hasProperty); ReturnedValue (*getIndexed)(const Managed *, uint index, bool *hasProperty); - void (*put)(Managed *, String *name, const Value &value); - void (*putIndexed)(Managed *, uint index, const Value &value); + 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); @@ -206,13 +237,16 @@ struct Q_QML_EXPORT Object: Managed { void setInternalClass(InternalClass *ic); const Value *propertyData(uint index) const { return d()->propertyData(index); } - Value *propertyData(uint index) { return d()->propertyData(index); } Heap::ArrayData *arrayData() const { return d()->arrayData; } - void setArrayData(ArrayData *a) { d()->arrayData = a->d(); } + void setArrayData(ArrayData *a) { d()->arrayData.set(engine(), a->d()); } void getProperty(uint index, Property *p, PropertyAttributes *attrs) const; void setProperty(uint index, const Property *p); + void setProperty(uint index, Value v) const { d()->setProperty(engine(), index, v); } + void setProperty(uint index, Heap::Base *b) const { d()->setProperty(engine(), index, b); } + 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(); } @@ -221,8 +255,8 @@ struct Q_QML_EXPORT Object: Managed { void getOwnProperty(String *name, PropertyAttributes *attrs, Property *p = 0); void getOwnProperty(uint index, PropertyAttributes *attrs, Property *p = 0); - Value *getValueOrSetter(String *name, PropertyAttributes *attrs); - Value *getValueOrSetter(uint index, PropertyAttributes *attrs); + MemberData::Index getValueOrSetter(String *name, PropertyAttributes *attrs); + ArrayData::Index getValueOrSetter(uint index, PropertyAttributes *attrs); bool hasProperty(String *name) const; bool hasProperty(uint index) const; @@ -239,8 +273,6 @@ struct Q_QML_EXPORT Object: Managed { // // helpers // - void put(ExecutionEngine *engine, const QString &name, const Value &value); - static ReturnedValue getValue(const Value &thisObject, const Value &v, PropertyAttributes attrs); ReturnedValue getValue(const Value &v, PropertyAttributes attrs) const { Scope scope(this->engine()); @@ -248,7 +280,7 @@ struct Q_QML_EXPORT Object: Managed { return getValue(t, v, attrs); } - void putValue(uint memberIndex, const Value &value); + bool putValue(uint memberIndex, const Value &value); /* The spec default: Writable: true, Enumerable: false, Configurable: true */ void defineDefaultProperty(String *name, const Value &value) { @@ -265,6 +297,10 @@ struct Q_QML_EXPORT Object: Managed { 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 insertMember(String *s, const Value &v, PropertyAttributes attributes = Attr_Data) { Scope scope(engine()); ScopedProperty p(scope); @@ -304,7 +340,7 @@ public: void push_back(const Value &v); ArrayData::Type arrayType() const { - return arrayData() ? d()->arrayData->type : Heap::ArrayData::Simple; + return arrayData() ? static_cast<ArrayData::Type>(d()->arrayData->type) : Heap::ArrayData::Simple; } // ### remove me void setArrayType(ArrayData::Type t) { @@ -344,10 +380,47 @@ public: { return vtable()->get(this, name, hasProperty); } inline ReturnedValue getIndexed(uint idx, bool *hasProperty = 0) const { return vtable()->getIndexed(this, idx, hasProperty); } - inline void put(String *name, const Value &v) - { vtable()->put(this, name, v); } - inline void putIndexed(uint idx, const Value &v) - { vtable()->putIndexed(this, idx, v); } + + // 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); } + + enum ThrowOnFailure { + DoThrowOnRejection, + DoNotThrow + }; + + // ES6: 7.3.3 Set (O, P, V, Throw) + inline bool set(String *name, const Value &v, ThrowOnFailure shouldThrow) + { + bool ret = vtable()->put(this, name, v); + // ES6: 7.3.3, 6: If success is false and Throw is true, throw a TypeError exception. + 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 \"") + + name->toQString() + QLatin1Char('\"'); + e->throwTypeError(message); + } + } + return ret; + } + + inline bool setIndexed(uint idx, const Value &v, ThrowOnFailure shouldThrow) + { + bool ret = vtable()->putIndexed(this, idx, v); + if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) { + ExecutionEngine *e = engine(); + if (!e->hasException) { // allow a custom set impl to throw itself + e->throwTypeError(); + } + } + return ret; + } + + PropertyAttributes query(String *name) const { return vtable()->query(this, name); } PropertyAttributes queryIndexed(uint index) const @@ -371,13 +444,12 @@ public: inline void call(Scope &scope, CallData *d) const { vtable()->call(this, scope, d); } protected: - static void markObjects(Heap::Base *that, ExecutionEngine *e); static void construct(const Managed *m, Scope &scope, CallData *); static void call(const Managed *m, Scope &scope, CallData *); static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); - static void putIndexed(Managed *m, uint index, const Value &value); + 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); @@ -387,12 +459,13 @@ protected: 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 void markObjects(Heap::Base *, MarkStack *); private: ReturnedValue internalGet(String *name, bool *hasProperty) const; ReturnedValue internalGetIndexed(uint index, bool *hasProperty) const; - void internalPut(String *name, const Value &value); - void internalPutIndexed(uint index, const Value &value); + bool internalPut(String *name, const Value &value); + bool internalPutIndexed(uint index, const Value &value); bool internalDeleteProperty(String *name); bool internalDeleteIndexedProperty(uint index); @@ -436,7 +509,7 @@ struct ArrayObject : Object { private: void commonInit() - { *propertyData(LengthPropertyIndex) = Primitive::fromInt32(0); } + { setProperty(internalClass->engine, LengthPropertyIndex, Primitive::fromInt32(0)); } }; } @@ -476,7 +549,7 @@ struct ArrayObject: Object { inline void Object::setArrayLengthUnchecked(uint l) { if (isArrayObject()) - *propertyData(Heap::ArrayObject::LengthPropertyIndex) = Primitive::fromUInt32(l); + setProperty(Heap::ArrayObject::LengthPropertyIndex, Primitive::fromUInt32(l)); } inline void Object::push_back(const Value &v) @@ -493,7 +566,7 @@ inline void Object::arraySet(uint index, const Property *p, PropertyAttributes a { // ### Clean up arrayCreate(); - if (attributes.isAccessor() || (index > 0x1000 && index > 2*d()->arrayData->alloc)) { + if (attributes.isAccessor() || (index > 0x1000 && index > 2*d()->arrayData->values.alloc)) { initSparseArray(); } else { arrayData()->vtable()->reallocate(this, index + 1, false); @@ -508,7 +581,7 @@ inline void Object::arraySet(uint index, const Property *p, PropertyAttributes a inline void Object::arraySet(uint index, const Value &value) { arrayCreate(); - if (index > 0x1000 && index > 2*d()->arrayData->alloc) { + if (index > 0x1000 && index > 2*d()->arrayData->values.alloc) { initSparseArray(); } ArrayData::insert(this, index, &value); diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp index 59115dfe21..3427ee89fe 100644 --- a/src/qml/jsruntime/qv4objectiterator.cpp +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -177,10 +177,10 @@ ReturnedValue ObjectIterator::nextPropertyNameAsString() DEFINE_OBJECT_VTABLE(ForEachIteratorObject); -void ForEachIteratorObject::markObjects(Heap::Base *that, ExecutionEngine *e) +void ForEachIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack) { ForEachIteratorObject::Data *o = static_cast<ForEachIteratorObject::Data *>(that); - o->workArea[0].mark(e); - o->workArea[1].mark(e); - Object::markObjects(that, e); + 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 98e94a95ea..6168d59914 100644 --- a/src/qml/jsruntime/qv4objectiterator_p.h +++ b/src/qml/jsruntime/qv4objectiterator_p.h @@ -129,7 +129,7 @@ struct ForEachIteratorObject: Object { ReturnedValue nextPropertyName() { return d()->it().nextPropertyNameAsString(); } protected: - static void markObjects(Heap::Base *that, ExecutionEngine *e); + static void markObjects(Heap::Base *that, MarkStack *markStack); }; inline diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 97dbe24339..2e72c0f13f 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2017 Crimson AS <info@crimson.no> ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -89,10 +90,11 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) ScopedObject o(scope, this); ctor->defineReadonlyProperty(v4->id_prototype(), o); - ctor->defineReadonlyProperty(v4->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyConfigurableProperty(v4->id_length(), Primitive::fromInt32(1)); ctor->defineDefaultProperty(QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); + ctor->defineDefaultProperty(QStringLiteral("assign"), method_assign, 2); ctor->defineDefaultProperty(QStringLiteral("create"), method_create, 2); ctor->defineDefaultProperty(QStringLiteral("defineProperty"), method_defineProperty, 3); ctor->defineDefaultProperty(QStringLiteral("defineProperties"), method_defineProperties, 2); @@ -123,9 +125,8 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) void ObjectPrototype::method_getPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + CHECK_EXCEPTION(); ScopedObject p(scope, o->prototype()); scope.result = !!p ? p->asReturnedValue() : Encode::null(); @@ -133,11 +134,8 @@ void ObjectPrototype::method_getPrototypeOf(const BuiltinFunction *, Scope &scop void ObjectPrototype::method_getOwnPropertyDescriptor(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject O(scope, callData->argument(0)); - if (!O) { - scope.result = scope.engine->throwTypeError(); - return; - } + ScopedObject O(scope, callData->args[0].toObject(scope.engine)); + CHECK_EXCEPTION(); if (ArgumentsObject::isNonStrictArgumentsObject(O)) static_cast<ArgumentsObject *>(O.getPointer())->fullyCreate(); @@ -154,13 +152,54 @@ void ObjectPrototype::method_getOwnPropertyDescriptor(const BuiltinFunction *, S void ObjectPrototype::method_getOwnPropertyNames(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject O(scope, callData->argument(0)); - if (!O) { - scope.result = scope.engine->throwTypeError(); + ScopedObject O(scope, callData->args[0].toObject(scope.engine)); + CHECK_EXCEPTION(); + + scope.result = getOwnPropertyNames(scope.engine, callData->args[0]); +} + +// 19.1.2.1 +void ObjectPrototype::method_assign(const BuiltinFunction *, Scope &scope, CallData *callData) +{ + ScopedObject to(scope, callData->args[0].toObject(scope.engine)); + CHECK_EXCEPTION(); + + if (callData->argc == 1) { + scope.result = to; return; } - scope.result = getOwnPropertyNames(scope.engine, callData->args[0]); + for (int i = 1; i < callData->argc; ++i) { + if (callData->args[i].isUndefined() || callData->args[i].isNull()) + continue; + + ScopedObject from(scope, callData->args[i].toObject(scope.engine)); + CHECK_EXCEPTION(); + QV4::ScopedArrayObject keys(scope, QV4::ObjectPrototype::getOwnPropertyNames(scope.engine, from)); + quint32 length = keys->getLength(); + + ScopedString nextKey(scope); + ScopedValue propValue(scope); + for (quint32 i = 0; i < length; ++i) { + nextKey = Value::fromReturnedValue(keys->getIndexed(i)).toString(scope.engine); + + PropertyAttributes attrs; + ScopedProperty prop(scope); + from->getOwnProperty(nextKey, &attrs, prop); + + if (attrs == PropertyFlag::Attr_Invalid) + continue; + + if (!attrs.isEnumerable()) + continue; + + propValue = from->get(nextKey); + to->set(nextKey, propValue, Object::DoThrowOnRejection); + CHECK_EXCEPTION(); + } + } + + scope.result = to; } void ObjectPrototype::method_create(const BuiltinFunction *builtin, Scope &scope, CallData *callData) @@ -246,14 +285,17 @@ void ObjectPrototype::method_defineProperties(const BuiltinFunction *, Scope &sc void ObjectPrototype::method_seal(const BuiltinFunction *, Scope &scope, CallData *callData) { ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + if (!o) { + // 19.1.2.17, 1 + scope.result = callData->argument(0); + return; + } o->setInternalClass(o->internalClass()->sealed()); if (o->arrayData()) { ArrayData::ensureAttributes(o); - for (uint i = 0; i < o->d()->arrayData->alloc; ++i) { + for (uint i = 0; i < o->d()->arrayData->values.alloc; ++i) { if (!o->arrayData()->isEmpty(i)) o->d()->arrayData->attrs[i].setConfigurable(false); } @@ -265,8 +307,11 @@ void ObjectPrototype::method_seal(const BuiltinFunction *, Scope &scope, CallDat void ObjectPrototype::method_freeze(const BuiltinFunction *, Scope &scope, CallData *callData) { ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + if (!o) { + // 19.1.2.5, 1 + scope.result = callData->argument(0); + return; + } if (ArgumentsObject::isNonStrictArgumentsObject(o)) static_cast<ArgumentsObject *>(o.getPointer())->fullyCreate(); @@ -275,7 +320,7 @@ void ObjectPrototype::method_freeze(const BuiltinFunction *, Scope &scope, CallD if (o->arrayData()) { ArrayData::ensureAttributes(o); - for (uint i = 0; i < o->arrayData()->alloc; ++i) { + for (uint i = 0; i < o->arrayData()->values.alloc; ++i) { if (!o->arrayData()->isEmpty(i)) o->arrayData()->attrs[i].setConfigurable(false); if (o->arrayData()->attrs[i].isData()) @@ -287,9 +332,11 @@ void ObjectPrototype::method_freeze(const BuiltinFunction *, Scope &scope, CallD void ObjectPrototype::method_preventExtensions(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + if (!o) { + scope.result = callData->argument(0); + return; + } o->setInternalClass(o->internalClass()->nonExtensible()); scope.result = o; @@ -297,9 +344,11 @@ void ObjectPrototype::method_preventExtensions(const BuiltinFunction *, Scope &s void ObjectPrototype::method_isSealed(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + if (!o) { + scope.result = Encode(true); + return; + } if (o->isExtensible()) { scope.result = Encode(false); @@ -322,7 +371,7 @@ void ObjectPrototype::method_isSealed(const BuiltinFunction *, Scope &scope, Cal return; } - for (uint i = 0; i < o->arrayData()->alloc; ++i) { + for (uint i = 0; i < o->arrayData()->values.alloc; ++i) { if (!o->arrayData()->isEmpty(i)) if (o->arrayData()->attributes(i).isConfigurable()) { scope.result = Encode(false); @@ -335,9 +384,11 @@ void ObjectPrototype::method_isSealed(const BuiltinFunction *, Scope &scope, Cal void ObjectPrototype::method_isFrozen(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + if (!o) { + scope.result = Encode(true); + return; + } if (o->isExtensible()) { scope.result = Encode(false); @@ -360,7 +411,7 @@ void ObjectPrototype::method_isFrozen(const BuiltinFunction *, Scope &scope, Cal return; } - for (uint i = 0; i < o->arrayData()->alloc; ++i) { + for (uint i = 0; i < o->arrayData()->values.alloc; ++i) { if (!o->arrayData()->isEmpty(i)) if (o->arrayData()->attributes(i).isConfigurable() || o->arrayData()->attributes(i).isWritable()) { scope.result = Encode(false); @@ -373,18 +424,19 @@ void ObjectPrototype::method_isFrozen(const BuiltinFunction *, Scope &scope, Cal void ObjectPrototype::method_isExtensible(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + if (!o) { + scope.result = Encode(false); + return; + } scope.result = Encode((bool)o->isExtensible()); } void ObjectPrototype::method_keys(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + CHECK_EXCEPTION(); ScopedArrayObject a(scope, scope.engine->newArrayObject()); @@ -670,12 +722,12 @@ ReturnedValue ObjectPrototype::fromPropertyDescriptor(ExecutionEngine *engine, c return o.asReturnedValue(); } - +// es6: GetOwnPropertyKeys Heap::ArrayObject *ObjectPrototype::getOwnPropertyNames(ExecutionEngine *v4, const Value &o) { Scope scope(v4); ScopedArrayObject array(scope, v4->newArrayObject()); - ScopedObject O(scope, o); + ScopedObject O(scope, o.toObject(v4)); if (O) { ObjectIterator it(scope, O, ObjectIterator::NoFlags); ScopedValue name(scope); diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h index 1db8615511..44b54267f3 100644 --- a/src/qml/jsruntime/qv4objectproto_p.h +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -81,6 +81,7 @@ struct ObjectPrototype: Object static void method_getPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_getOwnPropertyDescriptor(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_getOwnPropertyNames(const BuiltinFunction *, Scope &scope, CallData *callData); + static void method_assign(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_create(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_defineProperty(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_defineProperties(const BuiltinFunction *, Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp index 75f7c95dde..973541553a 100644 --- a/src/qml/jsruntime/qv4persistent.cpp +++ b/src/qml/jsruntime/qv4persistent.cpp @@ -232,26 +232,15 @@ void PersistentValueStorage::free(Value *v) freePage(p); } -static void drainMarkStack(QV4::ExecutionEngine *engine, Value *markBase) +void PersistentValueStorage::mark(MarkStack *markStack) { - while (engine->jsStackTop > markBase) { - Heap::Base *h = engine->popForGC(); - Q_ASSERT (h->vtable()->markObjects); - h->vtable()->markObjects(h, engine); - } -} - -void PersistentValueStorage::mark(ExecutionEngine *e) -{ - Value *markBase = e->jsStackTop; - Page *p = static_cast<Page *>(firstPage); while (p) { for (int i = 0; i < kEntriesPerPage; ++i) { if (Managed *m = p->values[i].as<Managed>()) - m->mark(e); + m->mark(markStack); } - drainMarkStack(e, markBase); + markStack->drain(); p = p->header.next; } @@ -407,11 +396,11 @@ void WeakValue::allocVal(ExecutionEngine *engine) val = engine->memoryManager->m_weakValues->allocate(); } -void WeakValue::markOnce(ExecutionEngine *e) +void WeakValue::markOnce(MarkStack *markStack) { if (!val) return; - val->mark(e); + val->mark(markStack); } void WeakValue::free() diff --git a/src/qml/jsruntime/qv4persistent_p.h b/src/qml/jsruntime/qv4persistent_p.h index c1cd1f34df..1f838f5531 100644 --- a/src/qml/jsruntime/qv4persistent_p.h +++ b/src/qml/jsruntime/qv4persistent_p.h @@ -65,7 +65,7 @@ struct Q_QML_EXPORT PersistentValueStorage Value *allocate(); static void free(Value *e); - void mark(ExecutionEngine *e); + void mark(MarkStack *markStack); struct Iterator { Iterator(void *p, int idx); @@ -203,7 +203,7 @@ public: bool isNullOrUndefined() const { return !val || val->isNullOrUndefined(); } void clear() { free(); } - void markOnce(ExecutionEngine *e); + void markOnce(MarkStack *markStack); private: Value *val; diff --git a/src/qml/jsruntime/qv4property_p.h b/src/qml/jsruntime/qv4property_p.h index 5069d7690b..2a5b6f7f74 100644 --- a/src/qml/jsruntime/qv4property_p.h +++ b/src/qml/jsruntime/qv4property_p.h @@ -78,12 +78,6 @@ struct Property { attrs->resolve(); } - static Property genericDescriptor() { - Property pd; - pd.value = Primitive::emptyValue(); - return pd; - } - inline bool isSubset(const PropertyAttributes &attrs, const Property *other, PropertyAttributes otherAttrs) const; inline void merge(PropertyAttributes &attrs, const Property *other, PropertyAttributes otherAttrs); @@ -99,19 +93,12 @@ struct Property { } explicit Property() { value = Encode::undefined(); set = Value::fromHeapObject(0); } - explicit Property(Value v) : value(v) { set = Value::fromHeapObject(0); } - Property(FunctionObject *getter, FunctionObject *setter) { - value = reinterpret_cast<Managed *>(getter); - set = reinterpret_cast<Managed *>(setter); - } Property(Heap::FunctionObject *getter, Heap::FunctionObject *setter) { value.setM(reinterpret_cast<Heap::Base *>(getter)); set.setM(reinterpret_cast<Heap::Base *>(setter)); } - Property &operator=(Value v) { value = v; return *this; } private: - Property(const Property &); - Property &operator=(const Property &); + Q_DISABLE_COPY(Property) }; inline bool Property::isSubset(const PropertyAttributes &attrs, const Property *other, PropertyAttributes otherAttrs) const diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index d97d44379d..8b9ef149d6 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -59,10 +59,10 @@ QT_BEGIN_NAMESPACE using namespace QV4; -DEFINE_OBJECT_VTABLE(QmlContextWrapper); +DEFINE_OBJECT_VTABLE(QQmlContextWrapper); DEFINE_MANAGED_VTABLE(QmlContext); -void Heap::QmlContextWrapper::init(QQmlContextData *context, QObject *scopeObject) +void Heap::QQmlContextWrapper::init(QQmlContextData *context, QObject *scopeObject) { Object::init(); readOnly = true; @@ -71,17 +71,17 @@ void Heap::QmlContextWrapper::init(QQmlContextData *context, QObject *scopeObjec this->scopeObject.init(scopeObject); } -void Heap::QmlContextWrapper::destroy() +void Heap::QQmlContextWrapper::destroy() { delete context; scopeObject.destroy(); Object::destroy(); } -ReturnedValue QmlContextWrapper::get(const Managed *m, String *name, bool *hasProperty) +ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasProperty) { - Q_ASSERT(m->as<QmlContextWrapper>()); - const QmlContextWrapper *resource = static_cast<const QmlContextWrapper *>(m); + Q_ASSERT(m->as<QQmlContextWrapper>()); + const QQmlContextWrapper *resource = static_cast<const QQmlContextWrapper *>(m); QV4::ExecutionEngine *v4 = resource->engine(); QV4::Scope scope(v4); @@ -131,7 +131,7 @@ ReturnedValue QmlContextWrapper::get(const Managed *m, String *name, bool *hasPr if (context->imports && name->startsWithUpper()) { // Search for attached properties, enums and imported scripts - QQmlTypeNameCache::Result r = context->imports->query(name); + QQmlTypeNameCache::Result r = context->imports->query(name, QQmlImport::AllowRecursion); if (r.isValid()) { if (hasProperty) @@ -140,9 +140,9 @@ ReturnedValue QmlContextWrapper::get(const Managed *m, String *name, bool *hasPr QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); return scripts->getIndexed(r.scriptIndex); } else if (r.type.isValid()) { - return QmlTypeWrapper::create(v4, scopeObject, r.type); + return QQmlTypeWrapper::create(v4, scopeObject, r.type); } else if (r.importNamespace) { - return QmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace); + return QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace); } Q_ASSERT(!"Unreachable"); } @@ -222,21 +222,19 @@ ReturnedValue QmlContextWrapper::get(const Managed *m, String *name, bool *hasPr return Encode::undefined(); } -void QmlContextWrapper::put(Managed *m, String *name, const Value &value) +bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value) { - Q_ASSERT(m->as<QmlContextWrapper>()); - QmlContextWrapper *resource = static_cast<QmlContextWrapper *>(m); + Q_ASSERT(m->as<QQmlContextWrapper>()); + QQmlContextWrapper *resource = static_cast<QQmlContextWrapper *>(m); ExecutionEngine *v4 = resource->engine(); QV4::Scope scope(v4); if (scope.hasException()) - return; - QV4::Scoped<QmlContextWrapper> wrapper(scope, resource); + return false; + QV4::Scoped<QQmlContextWrapper> wrapper(scope, resource); uint member = wrapper->internalClass()->find(name); - if (member < UINT_MAX) { - wrapper->putValue(member, value); - return; - } + if (member < UINT_MAX) + return wrapper->putValue(member, value); if (wrapper->d()->isNullWrapper) { if (wrapper && wrapper->d()->readOnly) { @@ -244,11 +242,10 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) QLatin1Char('"'); ScopedString e(scope, v4->newString(error)); v4->throwError(e); - return; + return false; } - Object::put(m, name, value); - return; + return Object::put(m, name, value); } // Its possible we could delay the calculation of the "actual" context (in the case @@ -257,7 +254,7 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) QQmlContextData *expressionContext = context; if (!context) - return; + return false; // See QV8ContextWrapper::Getter for resolution order @@ -267,18 +264,18 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) const QV4::IdentifierHash<int> &properties = context->propertyNames(); // Search context properties if (properties.count() && properties.value(name) != -1) - return; + return false; // Search scope object if (scopeObject && QV4::QObjectWrapper::setQmlProperty(v4, context, scopeObject, name, QV4::QObjectWrapper::CheckRevision, value)) - return; + return true; scopeObject = 0; // Search context object if (context->contextObject && QV4::QObjectWrapper::setQmlProperty(v4, context, context->contextObject, name, QV4::QObjectWrapper::CheckRevision, value)) - return; + return true; context = context->parent; } @@ -289,23 +286,23 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) QString error = QLatin1String("Invalid write to global property \"") + name->toQString() + QLatin1Char('"'); v4->throwError(error); - return; + return false; } - Object::put(m, name, value); + return Object::put(m, name, value); } -void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QmlContextWrapper *qml) +void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml) { Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext); - outer = outerContext->d(); + outer.set(internalClass->engine, outerContext->d()); strictMode = false; callData = outer->callData; lookups = outer->lookups; constantTable = outer->constantTable; compilationUnit = outer->compilationUnit; - this->qml = qml->d(); + this->qml.set(internalClass->engine, qml->d()); } Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, const QUrl &source, Value *sendFunction) @@ -318,7 +315,7 @@ Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, cons context->isInternal = true; context->isJSContext = true; - Scoped<QmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QmlContextWrapper>(context, (QObject*)0)); + Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QQmlContextWrapper>(context, (QObject*)0)); qml->d()->isNullWrapper = true; qml->setReadOnly(false); @@ -336,7 +333,7 @@ Heap::QmlContext *QmlContext::create(ExecutionContext *parent, QQmlContextData * { Scope scope(parent); - Scoped<QmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QmlContextWrapper>(context, scopeObject)); + Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<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 8ef7356f4c..65bf4c60ce 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -62,11 +62,11 @@ QT_BEGIN_NAMESPACE namespace QV4 { -struct QmlContextWrapper; +struct QQmlContextWrapper; namespace Heap { -struct QmlContextWrapper : Object { +struct QQmlContextWrapper : Object { void init(QQmlContextData *context, QObject *scopeObject); void destroy(); bool readOnly; @@ -76,17 +76,20 @@ struct QmlContextWrapper : Object { QQmlQPointer<QObject> scopeObject; }; -struct QmlContext : ExecutionContext { - void init(QV4::ExecutionContext *outerContext, QV4::QmlContextWrapper *qml); +#define QmlContextMembers(class, Member) \ + Member(class, Pointer, QQmlContextWrapper *, qml) - Pointer<QmlContextWrapper> qml; +DECLARE_HEAP_OBJECT(QmlContext, ExecutionContext) { + DECLARE_MARK_TABLE(QmlContext); + + void init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml); }; } -struct Q_QML_EXPORT QmlContextWrapper : Object +struct Q_QML_EXPORT QQmlContextWrapper : Object { - V4_OBJECT2(QmlContextWrapper, Object) + V4_OBJECT2(QQmlContextWrapper, Object) V4_NEEDS_DESTROY V4_INTERNALCLASS(QmlContextWrapper) @@ -96,7 +99,7 @@ struct Q_QML_EXPORT QmlContextWrapper : Object void setReadOnly(bool b) { d()->readOnly = b; } static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); + static bool put(Managed *m, String *name, const Value &value); }; struct Q_QML_EXPORT QmlContext : public ExecutionContext diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 89c10e0995..51a957acc9 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -74,9 +74,14 @@ #include <QtCore/qtimer.h> #include <QtCore/qatomic.h> #include <QtCore/qmetaobject.h> +#include <QtCore/qabstractitemmodel.h> +#include <QtCore/qloggingcategory.h> +#include <vector> QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcBindingRemoval, "qt.qml.binding.removal", QtWarningMsg) + // The code in this file does not violate strict aliasing, but GCC thinks it does // so turn off the warnings for us to have a clean build QT_WARNING_DISABLE_GCC("-Wstrict-aliasing") @@ -202,19 +207,61 @@ void QObjectWrapper::initializeBindings(ExecutionEngine *engine) QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const { + QObject *o = d()->object(); + return findProperty(engine, o, qmlContext, name, revisionMode, local); +} + +QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QObject *o, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) +{ Q_UNUSED(revisionMode); - QQmlData *ddata = QQmlData::get(d()->object(), false); - if (!ddata) - return 0; + QQmlData *ddata = QQmlData::get(o, false); QQmlPropertyData *result = 0; if (ddata && ddata->propertyCache) - result = ddata->propertyCache->property(name, d()->object(), qmlContext); + result = ddata->propertyCache->property(name, o, qmlContext); else - result = QQmlPropertyCache::property(engine->jsEngine(), d()->object(), name, qmlContext, *local); + result = QQmlPropertyCache::property(engine->jsEngine(), o, name, qmlContext, *local); return result; } +ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired) +{ + QQmlData::flushPendingBinding(object, QQmlPropertyIndex(property->coreIndex())); + + if (property->isFunction() && !property->isVarProperty()) { + if (property->isVMEFunction()) { + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); + Q_ASSERT(vmemo); + return vmemo->vmeMethod(property->coreIndex()); + } else if (property->isV4Function()) { + Scope scope(engine); + ScopedContext global(scope, engine->qmlContext()); + if (!global) + global = engine->rootContext(); + return QV4::QObjectMethod::create(global, object, property->coreIndex()); + } else if (property->isSignalHandler()) { + QmlSignalHandler::initProto(engine); + return engine->memoryManager->allocObject<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue(); + } else { + ExecutionContext *global = engine->rootContext(); + return QV4::QObjectMethod::create(global, object, property->coreIndex()); + } + } + + QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : 0; + + if (captureRequired && ep && ep->propertyCapture && !property->isConstant()) + ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex()); + + if (property->isVarProperty()) { + QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); + Q_ASSERT(vmemo); + return vmemo->vmeProperty(property->coreIndex()); + } else { + return loadProperty(engine, object, *property); + } +} + ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, bool includeImports) const { @@ -250,11 +297,11 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String if (r.scriptIndex != -1) { return QV4::Encode::undefined(); } else if (r.type.isValid()) { - return QmlTypeWrapper::create(v4, d()->object(), - r.type, Heap::QmlTypeWrapper::ExcludeEnums); + return QQmlTypeWrapper::create(v4, d()->object(), + r.type, Heap::QQmlTypeWrapper::ExcludeEnums); } else if (r.importNamespace) { - return QmlTypeWrapper::create(v4, d()->object(), - qmlContext->imports, r.importNamespace, Heap::QmlTypeWrapper::ExcludeEnums); + return QQmlTypeWrapper::create(v4, d()->object(), + qmlContext->imports, r.importNamespace, Heap::QQmlTypeWrapper::ExcludeEnums); } Q_ASSERT(!"Unreachable"); } @@ -279,42 +326,19 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String return getProperty(v4, d()->object(), result); } -ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired) +ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired) { - QQmlData::flushPendingBinding(object, QQmlPropertyIndex(property->coreIndex())); - - if (property->isFunction() && !property->isVarProperty()) { - if (property->isVMEFunction()) { - QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); - Q_ASSERT(vmemo); - return vmemo->vmeMethod(property->coreIndex()); - } else if (property->isV4Function()) { - Scope scope(engine); - ScopedContext global(scope, engine->qmlContext()); - if (!global) - global = engine->rootContext(); - return QV4::QObjectMethod::create(global, object, property->coreIndex()); - } else if (property->isSignalHandler()) { - QmlSignalHandler::initProto(engine); - return engine->memoryManager->allocObject<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue(); - } else { - ExecutionContext *global = engine->rootContext(); - return QV4::QObjectMethod::create(global, object, property->coreIndex()); - } - } - - QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : 0; - - if (captureRequired && ep && ep->propertyCapture && !property->isConstant()) - ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex()); + if (QQmlData::wasDeleted(object)) + return QV4::Encode::null(); + QQmlData *ddata = QQmlData::get(object, /*create*/false); + if (!ddata) + return QV4::Encode::undefined(); - if (property->isVarProperty()) { - QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object); - Q_ASSERT(vmemo); - return vmemo->vmeProperty(property->coreIndex()); - } else { - return loadProperty(engine, object, *property); - } + QQmlPropertyCache *cache = ddata->propertyCache; + Q_ASSERT(cache); + QQmlPropertyData *property = cache->property(propertyIndex); + Q_ASSERT(property); // We resolved this property earlier, so it better exist! + return getProperty(engine, object, property, captureRequired); } ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty) @@ -325,12 +349,49 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC return QV4::Encode::null(); } - if (!QQmlData::get(object, true)) { + if (name->equals(engine->id_destroy()) || name->equals(engine->id_toString())) { + int index = name->equals(engine->id_destroy()) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod; if (hasProperty) - *hasProperty = false; - return QV4::Encode::null(); + *hasProperty = true; + ExecutionContext *global = engine->rootContext(); + return QV4::QObjectMethod::create(global, object, index); + } + + QQmlData *ddata = QQmlData::get(object, false); + QQmlPropertyData local; + QQmlPropertyData *result = findProperty(engine, object, qmlContext, name, revisionMode, &local); + + if (result) { + if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) { + if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) { + if (hasProperty) + *hasProperty = false; + return QV4::Encode::undefined(); + } + } + + if (hasProperty) + *hasProperty = true; + + return getProperty(engine, object, result); + } else { + // Check if this object is already wrapped. + if (!ddata || (ddata->jsWrapper.isUndefined() && + (ddata->jsEngineId == 0 || // Nobody owns the QObject + !ddata->hasTaintedV4Object))) { // Someone else has used the QObject, but it isn't tainted + + // Not wrapped. Last chance: try query QObjectWrapper's prototype. + // If it can't handle this, then there is no point + // to wrap the QObject just to look at an empty set of JS props. + QV4::Object *proto = QObjectWrapper::defaultPrototype(engine); + return proto->get(name, hasProperty); + } } + // If we get here, we must already be wrapped (which implies a ddata). + // There's no point wrapping again, as there wouldn't be any new props. + Q_ASSERT(ddata); + QV4::Scope scope(engine); QV4::Scoped<QObjectWrapper> wrapper(scope, wrap(engine, object)); if (!wrapper) { @@ -341,6 +402,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC return wrapper->getQmlProperty(qmlContext, name, revisionMode, hasProperty); } + bool QObjectWrapper::setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, const Value &value) { @@ -399,10 +461,23 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP } } - if (newBinding) + if (newBinding) { QQmlPropertyPrivate::setBinding(newBinding); - else + } else { + if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) { + if (auto binding = QQmlPropertyPrivate::binding(object, QQmlPropertyIndex(property->coreIndex()))) { + Q_ASSERT(!binding->isValueTypeProxy()); + const auto qmlBinding = static_cast<const QQmlBinding*>(binding); + const auto stackFrame = engine->currentStackFrame(); + qCInfo(lcBindingRemoval, + "Overwriting binding on %s::%s at %s:%d that was initially bound at %s", + object->metaObject()->className(), qPrintable(property->name(object)), + qPrintable(stackFrame.source), stackFrame.line, + qPrintable(qmlBinding->expressionIdentifier())); + } + } QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex())); + } if (!newBinding && property->isVarProperty()) { // allow assignment of "special" values (null, undefined, function) to var properties @@ -539,7 +614,7 @@ ReturnedValue QObjectWrapper::wrap_slowPath(ExecutionEngine *engine, QObject *ob } } -void QObjectWrapper::markWrapper(QObject *object, ExecutionEngine *engine) +void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack) { if (QQmlData::wasDeleted(object)) return; @@ -548,25 +623,10 @@ void QObjectWrapper::markWrapper(QObject *object, ExecutionEngine *engine) if (!ddata) return; - if (ddata->jsEngineId == engine->m_engineId) - ddata->jsWrapper.markOnce(engine); - else if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object) - engine->m_multiplyWrappedQObjects->mark(object, engine); -} - -ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired) -{ - if (QQmlData::wasDeleted(object)) - return QV4::Encode::null(); - QQmlData *ddata = QQmlData::get(object, /*create*/false); - if (!ddata) - return QV4::Encode::undefined(); - - QQmlPropertyCache *cache = ddata->propertyCache; - Q_ASSERT(cache); - QQmlPropertyData *property = cache->property(propertyIndex); - Q_ASSERT(property); // We resolved this property earlier, so it better exist! - return getProperty(engine, object, property, captureRequired); + if (ddata->jsEngineId == markStack->engine->m_engineId) + ddata->jsWrapper.markOnce(markStack); + else if (markStack->engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object) + markStack->engine->m_multiplyWrappedQObjects->mark(object, markStack); } void QObjectWrapper::setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value) @@ -598,7 +658,7 @@ bool QObjectWrapper::isEqualTo(Managed *a, Managed *b) QV4::QObjectWrapper *qobjectWrapper = static_cast<QV4::QObjectWrapper *>(a); QV4::Object *o = b->as<Object>(); if (o) { - if (QV4::QmlTypeWrapper *qmlTypeWrapper = o->as<QV4::QmlTypeWrapper>()) + if (QV4::QQmlTypeWrapper *qmlTypeWrapper = o->as<QV4::QQmlTypeWrapper>()) return qmlTypeWrapper->toVariant().value<QObject*>() == qobjectWrapper->object(); } @@ -625,13 +685,13 @@ QV4::ReturnedValue QObjectWrapper::get(const Managed *m, String *name, bool *has return that->getQmlProperty(qmlContext, name, IgnoreRevision, hasProperty, /*includeImports*/ true); } -void QObjectWrapper::put(Managed *m, String *name, const Value &value) +bool QObjectWrapper::put(Managed *m, String *name, const Value &value) { QObjectWrapper *that = static_cast<QObjectWrapper*>(m); ExecutionEngine *v4 = that->engine(); if (v4->hasException || QQmlData::wasDeleted(that->d()->object())) - return; + return false; QQmlContextData *qmlContext = v4->callingQmlContext(); if (!setQmlProperty(v4, qmlContext, that->d()->object(), name, QV4::QObjectWrapper::IgnoreRevision, value)) { @@ -642,10 +702,13 @@ void QObjectWrapper::put(Managed *m, String *name, const Value &value) QString error = QLatin1String("Cannot assign to non-existent property \"") + name->toQString() + QLatin1Char('\"'); v4->throwError(error); + return false; } else { - QV4::Object::put(m, name, value); + return QV4::Object::put(m, name, value); } } + + return true; } PropertyAttributes QObjectWrapper::query(const Managed *m, String *name) @@ -939,36 +1002,36 @@ void QObjectWrapper::method_disconnect(const BuiltinFunction *, Scope &scope, Ca RETURN_UNDEFINED(); } -static void markChildQObjectsRecursively(QObject *parent, QV4::ExecutionEngine *e) +static void markChildQObjectsRecursively(QObject *parent, QV4::MarkStack *markStack) { const QObjectList &children = parent->children(); for (int i = 0; i < children.count(); ++i) { QObject *child = children.at(i); if (!child) continue; - QObjectWrapper::markWrapper(child, e); - markChildQObjectsRecursively(child, e); + QObjectWrapper::markWrapper(child, markStack); + markChildQObjectsRecursively(child, markStack); } } -void QObjectWrapper::markObjects(Heap::Base *that, QV4::ExecutionEngine *e) +void QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markStack) { QObjectWrapper::Data *This = static_cast<QObjectWrapper::Data *>(that); if (QObject *o = This->object()) { QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o); if (vme) - vme->mark(e); + vme->mark(markStack); // Children usually don't need to be marked, the gc keeps them alive. // But in the rare case of a "floating" QObject without a parent that // _gets_ marked (we've been called here!) then we also need to // propagate the marking down to the children recursively. if (!o->parent()) - markChildQObjectsRecursively(o, e); + markChildQObjectsRecursively(o, markStack); } - QV4::Object::markObjects(that, e); + QV4::Object::markObjects(that, markStack); } void QObjectWrapper::destroyObject(bool lastCall) @@ -1038,12 +1101,21 @@ private: inline void cleanup(); + template <class T, class M> + void fromContainerValue(const QV4::Object *object, int type, M CallArgument::*member, bool &queryEngine); + union { float floatValue; double doubleValue; quint32 intValue; bool boolValue; QObject *qobjectPtr; + std::vector<int> *stdVectorIntPtr; + std::vector<qreal> *stdVectorRealPtr; + std::vector<bool> *stdVectorBoolPtr; + std::vector<QString> *stdVectorQStringPtr; + std::vector<QUrl> *stdVectorQUrlPtr; + std::vector<QModelIndex> *stdVectorQModelIndexPtr; char allocData[MaxSizeOf8<QVariant, QString, @@ -1473,6 +1545,18 @@ void *CallArgument::dataPtr() { if (type == -1) return qvariantPtr->data(); + else if (type == qMetaTypeId<std::vector<int>>()) + return stdVectorIntPtr; + else if (type == qMetaTypeId<std::vector<qreal>>()) + return stdVectorRealPtr; + else if (type == qMetaTypeId<std::vector<bool>>()) + return stdVectorBoolPtr; + else if (type == qMetaTypeId<std::vector<QString>>()) + return stdVectorQStringPtr; + else if (type == qMetaTypeId<std::vector<QUrl>>()) + return stdVectorQUrlPtr; + else if (type == qMetaTypeId<std::vector<QModelIndex>>()) + return stdVectorQModelIndexPtr; else if (type != 0) return (void *)&allocData; return 0; @@ -1522,6 +1606,19 @@ void CallArgument::initAsType(int callType) } } +template <class T, class M> +void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M CallArgument::*member, bool &queryEngine) +{ + if (object && object->isListType()) { + T* ptr = static_cast<T*>(QV4::SequencePrototype::getRawContainerPtr(object, callType)); + if (ptr) { + (this->*member) = ptr; + type = callType; + queryEngine = false; + } + } +} + void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const QV4::Value &value) { if (type != 0) { @@ -1560,7 +1657,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q qobjectPtr = 0; if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) qobjectPtr = qobjectWrapper->object(); - else if (const QV4::QmlTypeWrapper *qmlTypeWrapper = value.as<QV4::QmlTypeWrapper>()) + else if (const QV4::QQmlTypeWrapper *qmlTypeWrapper = value.as<QV4::QQmlTypeWrapper>()) queryEngine = qmlTypeWrapper->isSingleton(); type = callType; } else if (callType == qMetaTypeId<QVariant>()) { @@ -1603,6 +1700,33 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q type = callType; } else if (callType == QMetaType::Void) { *qvariantPtr = QVariant(); + } else if (callType == qMetaTypeId<std::vector<int>>() + || callType == qMetaTypeId<std::vector<qreal>>() + || callType == qMetaTypeId<std::vector<bool>>() + || callType == qMetaTypeId<std::vector<QString>>() + || callType == qMetaTypeId<std::vector<QUrl>>() + || callType == qMetaTypeId<std::vector<QModelIndex>>()) { + queryEngine = true; + const QV4::Object* object = value.as<Object>(); + if (callType == qMetaTypeId<std::vector<int>>()) { + stdVectorIntPtr = nullptr; + fromContainerValue<std::vector<int>>(object, callType, &CallArgument::stdVectorIntPtr, queryEngine); + } else if (callType == qMetaTypeId<std::vector<qreal>>()) { + stdVectorRealPtr = nullptr; + fromContainerValue<std::vector<qreal>>(object, callType, &CallArgument::stdVectorRealPtr, queryEngine); + } else if (callType == qMetaTypeId<std::vector<bool>>()) { + stdVectorBoolPtr = nullptr; + fromContainerValue<std::vector<bool>>(object, callType, &CallArgument::stdVectorBoolPtr, queryEngine); + } else if (callType == qMetaTypeId<std::vector<QString>>()) { + stdVectorQStringPtr = nullptr; + fromContainerValue<std::vector<QString>>(object, callType, &CallArgument::stdVectorQStringPtr, queryEngine); + } else if (callType == qMetaTypeId<std::vector<QUrl>>()) { + stdVectorQUrlPtr = nullptr; + fromContainerValue<std::vector<QUrl>>(object, callType, &CallArgument::stdVectorQUrlPtr, queryEngine); + } else if (callType == qMetaTypeId<std::vector<QModelIndex>>()) { + stdVectorQModelIndexPtr = nullptr; + fromContainerValue<std::vector<QModelIndex>>(object, callType, &CallArgument::stdVectorQModelIndexPtr, queryEngine); + } } else { queryEngine = true; } @@ -1712,7 +1836,7 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueType Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope)); method->d()->setPropertyCache(valueType->d()->propertyCache()); method->d()->index = index; - method->d()->valueTypeWrapper = valueType->d(); + method->d()->valueTypeWrapper.set(valueScope.engine, valueType->d()); return method.asReturnedValue(); } @@ -1849,15 +1973,6 @@ void QObjectMethod::callInternal(CallData *callData, Scope &scope) const } } -void QObjectMethod::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - QObjectMethod::Data *This = static_cast<QObjectMethod::Data*>(that); - if (This->valueTypeWrapper) - This->valueTypeWrapper->mark(e); - - FunctionObject::markObjects(that, e); -} - DEFINE_OBJECT_VTABLE(QObjectMethod); @@ -2083,12 +2198,12 @@ void MultiplyWrappedQObjectMap::remove(QObject *key) erase(it); } -void MultiplyWrappedQObjectMap::mark(QObject *key, ExecutionEngine *engine) +void MultiplyWrappedQObjectMap::mark(QObject *key, MarkStack *markStack) { Iterator it = find(key); if (it == end()) return; - it->markOnce(engine); + it->markOnce(markStack); } void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object) diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 6494c20bd2..018e444f7c 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -95,7 +95,15 @@ private: QQmlQPointer<QObject> qObj; }; -struct QObjectMethod : FunctionObject { +#define QObjectMethodMembers(class, Member) \ + Member(class, Pointer, QQmlValueTypeWrapper *, valueTypeWrapper) \ + Member(class, NoMark, QQmlQPointer<QObject>, qObj) \ + Member(class, NoMark, QQmlPropertyCache *, _propertyCache) \ + Member(class, NoMark, int, index) + +DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) { + DECLARE_MARK_TABLE(QObjectMethod); + void init(QV4::ExecutionContext *scope); void destroy() { @@ -113,18 +121,10 @@ struct QObjectMethod : FunctionObject { _propertyCache = c; } - Pointer<QQmlValueTypeWrapper> valueTypeWrapper; - const QMetaObject *metaObject(); QObject *object() const { return qObj.data(); } void setObject(QObject *o) { qObj = o; } -private: - QQmlQPointer<QObject> qObj; - QQmlPropertyCache *_propertyCache; - -public: - int index; }; struct QMetaObjectWrapper : FunctionObject { @@ -171,7 +171,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object static bool setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, const Value &value); static ReturnedValue wrap(ExecutionEngine *engine, QObject *object); - static void markWrapper(QObject *object, ExecutionEngine *engine); + static void markWrapper(QObject *object, MarkStack *markStack); using Object::get; @@ -189,13 +189,14 @@ protected: static ReturnedValue create(ExecutionEngine *engine, QObject *object); + static QQmlPropertyData *findProperty(ExecutionEngine *engine, QObject *o, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local); QQmlPropertyData *findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const; static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); + 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 void markObjects(Heap::Base *that, QV4::ExecutionEngine *e); + static void markObjects(Heap::Base *that, QV4::MarkStack *markStack); static void method_connect(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_disconnect(const BuiltinFunction *, Scope &scope, CallData *callData); @@ -240,8 +241,6 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject void callInternal(CallData *callData, Scope &scope) const; - static void markObjects(Heap::Base *that, QV4::ExecutionEngine *e); - static QPair<QObject *, int> extractQtMethod(const QV4::FunctionObject *function); }; @@ -294,7 +293,7 @@ public: ReturnedValue value(QObject *key) const { return QHash<QObject*, QV4::WeakValue>::value(key).value(); } Iterator erase(Iterator it); void remove(QObject *key); - void mark(QObject *key, ExecutionEngine *engine); + void mark(QObject *key, MarkStack *markStack); private Q_SLOTS: void removeDestroyedObject(QObject*); diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp index 8cb15b9d7e..162f0fba57 100644 --- a/src/qml/jsruntime/qv4regexp.cpp +++ b/src/qml/jsruntime/qv4regexp.cpp @@ -127,9 +127,3 @@ void Heap::RegExp::destroy() delete pattern; Base::destroy(); } - -void RegExp::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - Q_UNUSED(that); - Q_UNUSED(e); -} diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h index d9b536406c..998f6e3da3 100644 --- a/src/qml/jsruntime/qv4regexp_p.h +++ b/src/qml/jsruntime/qv4regexp_p.h @@ -122,8 +122,6 @@ struct RegExp : public Managed int captureCount() const { return subPatternCount() + 1; } - static void markObjects(Heap::Base *that, QV4::ExecutionEngine *e); - friend class RegExpCache; }; diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 82b0f67075..83bfe21c67 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -74,15 +74,15 @@ void Heap::RegExpObject::init() Object::init(); Scope scope(internalClass->engine); Scoped<QV4::RegExpObject> o(scope, this); - o->d()->value = QV4::RegExp::create(scope.engine, QString(), false, false, false); + value.set(scope.engine, QV4::RegExp::create(scope.engine, QString(), false, false)); o->initProperties(); } void Heap::RegExpObject::init(QV4::RegExp *value) { Object::init(); - this->value = value->d(); Scope scope(internalClass->engine); + this->value.set(scope.engine, value->d()); Scoped<QV4::RegExpObject> o(scope, this); o->initProperties(); } @@ -134,14 +134,15 @@ void Heap::RegExpObject::init(const QRegExp &re) Scope scope(internalClass->engine); Scoped<QV4::RegExpObject> o(scope, this); - o->d()->value = QV4::RegExp::create(scope.engine, pattern, re.caseSensitivity() == Qt::CaseInsensitive, false); + o->d()->value.set(scope.engine, + QV4::RegExp::create(scope.engine, pattern, re.caseSensitivity() == Qt::CaseInsensitive, false)); o->initProperties(); } void RegExpObject::initProperties() { - *propertyData(Index_LastIndex) = Primitive::fromInt32(0); + setProperty(Index_LastIndex, Primitive::fromInt32(0)); Q_ASSERT(value()); @@ -153,25 +154,10 @@ void RegExpObject::initProperties() p.replace('/', QLatin1String("\\/")); } - *propertyData(Index_Source) = engine()->newString(p); - *propertyData(Index_Global) = Primitive::fromBoolean(global()); - *propertyData(Index_IgnoreCase) = Primitive::fromBoolean(value()->ignoreCase); - *propertyData(Index_Multiline) = Primitive::fromBoolean(value()->multiLine); -} - - -void RegExpObject::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - RegExpObject::Data *re = static_cast<RegExpObject::Data *>(that); - if (re->value) - re->value->mark(e); - Object::markObjects(that, e); -} - -Value *RegExpObject::lastIndexProperty() -{ - Q_ASSERT(0 == internalClass()->find(engine()->id_lastIndex())); - return propertyData(0); + setProperty(Index_Source, engine()->newString(p)); + setProperty(Index_Global, Primitive::fromBoolean(global())); + setProperty(Index_IgnoreCase, Primitive::fromBoolean(value()->ignoreCase)); + setProperty(Index_Multiline, Primitive::fromBoolean(value()->multiLine)); } // Converts a JS RegExp to a QRegExp. @@ -225,8 +211,8 @@ void Heap::RegExpCtor::init(QV4::ExecutionContext *scope) void Heap::RegExpCtor::clearLastMatch() { - lastMatch = Primitive::nullValue(); - lastInput = internalClass->engine->id_empty()->d(); + lastMatch.set(internalClass->engine, Primitive::nullValue()); + lastInput.set(internalClass->engine, internalClass->engine->id_empty()->d()); lastMatchStart = 0; lastMatchEnd = 0; } @@ -300,15 +286,6 @@ void RegExpCtor::call(const Managed *that, Scope &scope, CallData *callData) construct(that, scope, callData); } -void RegExpCtor::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - RegExpCtor::Data *This = static_cast<RegExpCtor::Data *>(that); - This->lastMatch.mark(e); - if (This->lastInput) - This->lastInput->mark(e); - FunctionObject::markObjects(that, e); -} - void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor) { Scope scope(engine); @@ -358,9 +335,9 @@ void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallDat RETURN_UNDEFINED(); QString s = str->toQString(); - int offset = r->global() ? r->lastIndexProperty()->toInt32() : 0; + int offset = r->global() ? r->lastIndex() : 0; if (offset < 0 || offset > s.length()) { - *r->lastIndexProperty() = Primitive::fromInt32(0); + r->setLastIndex(0); RETURN_RESULT(Encode::null()); } @@ -371,7 +348,7 @@ void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallDat regExpCtor->d()->clearLastMatch(); if (result == -1) { - *r->lastIndexProperty() = Primitive::fromInt32(0); + r->setLastIndex(0); RETURN_RESULT(Encode::null()); } @@ -387,17 +364,17 @@ void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallDat array->arrayPut(i, v); } array->setArrayLengthUnchecked(len); - *array->propertyData(Index_ArrayIndex) = Primitive::fromInt32(result); - *array->propertyData(Index_ArrayInput) = str; + array->setProperty(Index_ArrayIndex, Primitive::fromInt32(result)); + array->setProperty(Index_ArrayInput, str); RegExpCtor::Data *dd = regExpCtor->d(); - dd->lastMatch = array; - dd->lastInput = str->d(); + dd->lastMatch.set(scope.engine, array); + dd->lastInput.set(scope.engine, str->d()); dd->lastMatchStart = matchOffsets[0]; dd->lastMatchEnd = matchOffsets[1]; if (r->global()) - *r->lastIndexProperty() = Primitive::fromInt32(matchOffsets[1]); + r->setLastIndex(matchOffsets[1]); scope.result = array; } @@ -429,8 +406,7 @@ void RegExpPrototype::method_compile(const BuiltinFunction *, Scope &scope, Call scope.engine->regExpCtor()->as<FunctionObject>()->construct(scope, cData); Scoped<RegExpObject> re(scope, scope.result.asReturnedValue()); - r->d()->value = re->value(); - RETURN_UNDEFINED(); + r->d()->value.set(scope.engine, re->value()); } template <int index> diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index af49a1bf5e..65055ccc81 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -55,8 +55,8 @@ #include "qv4context_p.h" #include "qv4functionobject_p.h" #include "qv4string_p.h" -#include "qv4codegen_p.h" -#include "qv4isel_p.h" +#include <qv4codegen_p.h> +#include <qv4isel_p.h> #include "qv4managed_p.h" #include "qv4property_p.h" #include "qv4objectiterator_p.h" @@ -74,20 +74,27 @@ namespace QV4 { namespace Heap { -struct RegExpObject : Object { +#define RegExpObjectMembers(class, Member) \ + Member(class, Pointer, RegExp *, value) + +DECLARE_HEAP_OBJECT(RegExpObject, Object) { + DECLARE_MARK_TABLE(RegExpObject); + void init(); void init(QV4::RegExp *value); void init(const QRegExp &re); - - Pointer<RegExp> value; }; -struct RegExpCtor : FunctionObject { +#define RegExpCtorMembers(class, Member) \ + Member(class, HeapValue, HeapValue, lastMatch) \ + Member(class, Pointer, String *, lastInput) \ + Member(class, NoMark, int, lastMatchStart) \ + Member(class, NoMark, int, lastMatchEnd) + +DECLARE_HEAP_OBJECT(RegExpCtor, FunctionObject) { + DECLARE_MARK_TABLE(RegExpCtor); + void init(QV4::ExecutionContext *scope); - Value lastMatch; - Pointer<String> lastInput; - int lastMatchStart; - int lastMatchEnd; void clearLastMatch(); }; @@ -121,14 +128,19 @@ struct RegExpObject: Object { void initProperties(); - Value *lastIndexProperty(); + int lastIndex() const { + Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex())); + return propertyData(Index_LastIndex)->toInt32(); + } + void setLastIndex(int index) { + Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex())); + return setProperty(Index_LastIndex, Primitive::fromInt32(index)); + } + QRegExp toQRegExp() const; QString toString() const; QString source() const; uint flags() const; - -protected: - static void markObjects(Heap::Base *that, ExecutionEngine *e); }; struct RegExpCtor: FunctionObject @@ -142,7 +154,6 @@ struct RegExpCtor: FunctionObject static void construct(const Managed *m, Scope &scope, CallData *callData); static void call(const Managed *that, Scope &scope, CallData *callData); - static void markObjects(Heap::Base *that, ExecutionEngine *e); }; struct RegExpPrototype: RegExpObject diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 28b344d154..4b952bcbbc 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -665,7 +665,7 @@ ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &o 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->len) + if (idx < s->values.size) if (!s->data(idx).isEmpty()) return s->data(idx).asReturnedValue(); } @@ -688,8 +688,8 @@ static Q_NEVER_INLINE void setElementFallback(ExecutionEngine *engine, const Val if (index.asArrayIndex(idx)) { if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) { Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>(); - if (idx < s->len) { - s->data(idx) = value; + if (idx < s->values.size) { + s->setData(engine, idx, value); return; } } @@ -710,8 +710,8 @@ void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, co 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->len) { - s->data(idx) = value; + if (idx < s->values.size) { + s->setData(engine, idx, value); return; } } @@ -1377,7 +1377,7 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4:: } for (uint i = 0; i < klass->size; ++i) - *o->propertyData(i) = *args++; + o->setProperty(i, *args++); if (arrayValueCount > 0) { ScopedValue entry(scope); @@ -1521,7 +1521,7 @@ ReturnedValue Runtime::method_getQmlContextObjectProperty(ExecutionEngine *engin ReturnedValue Runtime::method_getQmlSingletonQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired) { Scope scope(engine); - QV4::Scoped<QmlTypeWrapper> wrapper(scope, object); + QV4::Scoped<QQmlTypeWrapper> wrapper(scope, object); if (!wrapper) { scope.engine->throwTypeError(QStringLiteral("Cannot read property of null")); return Encode::undefined(); diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 8ce10e326d..8afc672aa2 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -78,13 +78,22 @@ static void generateWarning(QV4::ExecutionEngine *v4, const QString& description F(int, IntVector, QVector<int>, 0) \ F(qreal, RealVector, QVector<qreal>, 0.0) \ F(bool, BoolVector, QVector<bool>, false) \ + F(int, IntStdVector, std::vector<int>, 0) \ + F(qreal, RealStdVector, std::vector<qreal>, 0.0) \ + F(bool, BoolStdVector, std::vector<bool>, false) \ F(int, Int, QList<int>, 0) \ F(qreal, Real, QList<qreal>, 0.0) \ F(bool, Bool, QList<bool>, false) \ F(QString, String, QList<QString>, QString()) \ F(QString, QString, QStringList, QString()) \ + F(QString, StringVector, QVector<QString>, QString()) \ + F(QString, StringStdVector, std::vector<QString>, QString()) \ F(QUrl, Url, QList<QUrl>, QUrl()) \ + F(QUrl, UrlVector, QVector<QUrl>, QUrl()) \ + F(QUrl, UrlStdVector, std::vector<QUrl>, QUrl()) \ F(QModelIndex, QModelIndex, QModelIndexList, QModelIndex()) \ + F(QModelIndex, QModelIndexVector, QVector<QModelIndex>, QModelIndex()) \ + F(QModelIndex, QModelIndexStdVector, std::vector<QModelIndex>, QModelIndex()) \ F(QItemSelectionRange, QItemSelectionRange, QItemSelection, QItemSelectionRange()) static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QString &element) @@ -263,56 +272,54 @@ public: } loadReference(); } - qint32 signedIdx = static_cast<qint32>(index); - if (signedIdx < d()->container->count()) { + if (index < size_t(d()->container->size())) { if (hasProperty) *hasProperty = true; - return convertElementToValue(engine(), d()->container->at(signedIdx)); + return convertElementToValue(engine(), qAsConst(*(d()->container))[index]); } if (hasProperty) *hasProperty = false; return Encode::undefined(); } - void containerPutIndexed(uint index, const QV4::Value &value) + bool containerPutIndexed(uint index, const QV4::Value &value) { if (internalClass()->engine->hasException) - return; + return false; /* Qt containers have int (rather than uint) allowable indexes. */ if (index > INT_MAX) { generateWarning(engine(), QLatin1String("Index out of range during indexed set")); - return; + return false; } if (d()->isReference) { if (!d()->object) - return; + return false; loadReference(); } - qint32 signedIdx = static_cast<qint32>(index); - - int count = d()->container->count(); + size_t count = size_t(d()->container->size()); typename Container::value_type element = convertValueToElement<typename Container::value_type>(value); - if (signedIdx == count) { - d()->container->append(element); - } else if (signedIdx < count) { - (*d()->container)[signedIdx] = element; + if (index == count) { + d()->container->push_back(element); + } else if (index < count) { + (*d()->container)[index] = element; } else { /* according to ECMA262r3 we need to insert */ /* the value at the given index, increasing length to index+1. */ - d()->container->reserve(signedIdx + 1); - while (signedIdx > count++) { - d()->container->append(typename Container::value_type()); + d()->container->reserve(index + 1); + while (index > count++) { + d()->container->push_back(typename Container::value_type()); } - d()->container->append(element); + d()->container->push_back(element); } if (d()->isReference) storeReference(); + return true; } QV4::PropertyAttributes containerQueryIndexed(uint index) const @@ -327,8 +334,7 @@ public: return QV4::Attr_Invalid; loadReference(); } - qint32 signedIdx = static_cast<qint32>(index); - return (signedIdx < d()->container->count()) ? QV4::Attr_Data : QV4::Attr_Invalid; + return (index < size_t(d()->container->size())) ? QV4::Attr_Data : QV4::Attr_Invalid; } void containerAdvanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) @@ -344,7 +350,7 @@ public: loadReference(); } - if (it->arrayIndex < static_cast<uint>(d()->container->count())) { + if (it->arrayIndex < static_cast<uint>(d()->container->size())) { *index = it->arrayIndex; ++it->arrayIndex; *attrs = QV4::Attr_Data; @@ -364,14 +370,13 @@ public: return false; loadReference(); } - qint32 signedIdx = static_cast<qint32>(index); - if (signedIdx >= d()->container->count()) + if (index >= size_t(d()->container->size())) return false; /* according to ECMA262r3 it should be Undefined, */ /* but we cannot, so we insert a default-value instead. */ - d()->container->replace(signedIdx, typename Container::value_type()); + (*d()->container)[index] = typename Container::value_type(); if (d()->isReference) storeReference(); @@ -456,7 +461,7 @@ public: RETURN_RESULT(Encode(0)); This->loadReference(); } - RETURN_RESULT(Encode(This->d()->container->count())); + RETURN_RESULT(Encode(qint32(This->d()->container->size()))); } static void method_set_length(const BuiltinFunction *, Scope &scope, CallData *callData) @@ -478,8 +483,8 @@ public: This->loadReference(); } /* Determine whether we need to modify the sequence */ - qint32 newCount = static_cast<qint32>(newLength); - qint32 count = This->d()->container->count(); + quint32 newCount = static_cast<quint32>(newLength); + quint32 count = static_cast<quint32>(This->d()->container->size()); if (newCount == count) { RETURN_UNDEFINED(); } else if (newCount > count) { @@ -488,14 +493,13 @@ public: /* We cannot, so we insert default-values instead. */ This->d()->container->reserve(newCount); while (newCount > count++) { - This->d()->container->append(typename Container::value_type()); + This->d()->container->push_back(typename Container::value_type()); } } else { /* according to ECMA262r3 we need to remove */ /* elements until the sequence is the required length. */ - while (newCount < count) { - count--; - This->d()->container->removeAt(count); + if (newCount < count) { + This->d()->container->erase(This->d()->container->begin() + newCount, This->d()->container->end()); } } /* write back if required. */ @@ -516,10 +520,13 @@ public: quint32 length = array->getLength(); QV4::ScopedValue v(scope); for (quint32 i = 0; i < length; ++i) - result << convertValueToElement<typename Container::value_type>((v = array->getIndexed(i))); + result.push_back(convertValueToElement<typename Container::value_type>((v = array->getIndexed(i)))); return QVariant::fromValue(result); } + void* getRawContainerPtr() const + { return d()->container; } + void loadReference() const { Q_ASSERT(d()->object); @@ -540,8 +547,8 @@ public: static QV4::ReturnedValue getIndexed(const QV4::Managed *that, uint index, bool *hasProperty) { return static_cast<const QQmlSequence<Container> *>(that)->containerGetIndexed(index, hasProperty); } - static void putIndexed(Managed *that, uint index, const QV4::Value &value) - { static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(index, value); } + static bool putIndexed(Managed *that, uint index, const QV4::Value &value) + { return static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(index, value); } 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) @@ -594,16 +601,34 @@ typedef QQmlSequence<QVector<qreal> > QQmlRealVectorList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlRealVectorList); typedef QQmlSequence<QVector<bool> > QQmlBoolVectorList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlBoolVectorList); +typedef QQmlSequence<std::vector<int> > QQmlIntStdVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlIntStdVectorList); +typedef QQmlSequence<std::vector<qreal> > QQmlRealStdVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlRealStdVectorList); +typedef QQmlSequence<std::vector<bool> > QQmlBoolStdVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlBoolStdVectorList); typedef QQmlSequence<QStringList> QQmlQStringList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQStringList); typedef QQmlSequence<QList<QString> > QQmlStringList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlStringList); +typedef QQmlSequence<QVector<QString> > QQmlStringVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlStringVectorList); +typedef QQmlSequence<std::vector<QString> > QQmlStringStdVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlStringStdVectorList); typedef QQmlSequence<QList<int> > QQmlIntList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlIntList); typedef QQmlSequence<QList<QUrl> > QQmlUrlList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlList); +typedef QQmlSequence<QVector<QUrl> > QQmlUrlVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlVectorList); +typedef QQmlSequence<std::vector<QUrl> > QQmlUrlStdVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlStdVectorList); typedef QQmlSequence<QModelIndexList> QQmlQModelIndexList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexList); +typedef QQmlSequence<QVector<QModelIndex> > QQmlQModelIndexVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexVectorList); +typedef QQmlSequence<std::vector<QModelIndex> > QQmlQModelIndexStdVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexStdVectorList); typedef QQmlSequence<QItemSelection> QQmlQItemSelectionRangeList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQItemSelectionRangeList); typedef QQmlSequence<QList<bool> > QQmlBoolList; @@ -724,6 +749,19 @@ QVariant SequencePrototype::toVariant(const QV4::Value &array, int typeHint, boo #undef SEQUENCE_TO_VARIANT +#define SEQUENCE_GET_RAWCONTAINERPTR(ElementType, ElementTypeName, SequenceType, unused) \ + if (const QQml##ElementTypeName##List *list = [&]() -> const QQml##ElementTypeName##List* \ + { if (typeHint == qMetaTypeId<SequenceType>()) return object->as<QQml##ElementTypeName##List>(); return nullptr;}()) \ + return list->getRawContainerPtr(); \ + else + +void* SequencePrototype::getRawContainerPtr(const Object *object, int typeHint) +{ + FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_GET_RAWCONTAINERPTR) { /* else */ return nullptr; } +} + +#undef SEQUENCE_GET_RAWCONTAINERPTR + #define MAP_META_TYPE(ElementType, ElementTypeName, SequenceType, unused) \ if (object->as<QQml##ElementTypeName##List>()) { \ return qMetaTypeId<SequenceType>(); \ diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index 0879f149fa..2b8d1ea716 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -81,6 +81,7 @@ struct SequencePrototype : public QV4::Object static int metaTypeForSequence(const Object *object); static QVariant toVariant(Object *object); static QVariant toVariant(const Value &array, int typeHint, bool *succeeded); + static void* getRawContainerPtr(const Object *object, int typeHint); }; } diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index 515d61c8e4..c0183a46a7 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -54,12 +54,12 @@ using namespace QV4; DEFINE_MANAGED_VTABLE(String); -void String::markObjects(Heap::Base *that, ExecutionEngine *e) +void String::markObjects(Heap::Base *that, MarkStack *markStack) { String::Data *s = static_cast<String::Data *>(that); if (s->largestSubLength) { - s->left->mark(e); - s->right->mark(e); + s->left->mark(markStack); + s->right->mark(markStack); } } diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index f5311ae5d4..2f34dd6139 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -205,7 +205,7 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { Identifier *identifier() const { return d()->identifier; } protected: - static void markObjects(Heap::Base *that, ExecutionEngine *e); + static void markObjects(Heap::Base *that, MarkStack *markStack); static bool isEqualTo(Managed *that, Managed *o); static uint getLength(const Managed *m); #endif diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 7ccb7a762f..7c65c97d73 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -77,15 +77,15 @@ void Heap::StringObject::init() { Object::init(); Q_ASSERT(vtable() == QV4::StringObject::staticVTable()); - string = internalClass->engine->id_empty()->d(); - *propertyData(LengthPropertyIndex) = Primitive::fromInt32(0); + string.set(internalClass->engine, internalClass->engine->id_empty()->d()); + setProperty(internalClass->engine, LengthPropertyIndex, Primitive::fromInt32(0)); } void Heap::StringObject::init(const QV4::String *str) { Object::init(); - string = str->d(); - *propertyData(LengthPropertyIndex) = Primitive::fromInt32(length()); + string.set(internalClass->engine, str->d()); + setProperty(internalClass->engine, LengthPropertyIndex, Primitive::fromInt32(length())); } Heap::String *Heap::StringObject::getIndex(uint index) const @@ -145,13 +145,6 @@ void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, return Object::advanceIterator(m, it, name, index, p, attrs); } -void StringObject::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - StringObject::Data *o = static_cast<StringObject::Data *>(that); - o->string->mark(e); - Object::markObjects(that, e); -} - DEFINE_OBJECT_VTABLE(StringCtor); void Heap::StringCtor::init(QV4::ExecutionContext *scope) @@ -200,6 +193,7 @@ 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("repeat"), method_repeat, 1); defineDefaultProperty(QStringLiteral("replace"), method_replace, 2); defineDefaultProperty(QStringLiteral("search"), method_search, 1); defineDefaultProperty(QStringLiteral("slice"), method_slice, 2); @@ -458,6 +452,21 @@ void StringPrototype::method_match(const BuiltinFunction *, Scope &scope, CallDa scope.result = a; } +void StringPrototype::method_repeat(const BuiltinFunction *, Scope &scope, CallData *callData) +{ + QString value = getThisString(scope, callData); + CHECK_EXCEPTION(); + + double repeats = callData->args[0].toInteger(); + + if (repeats < 0 || qIsInf(repeats)) { + scope.result = scope.engine->throwRangeError(QLatin1String("Invalid count value")); + return; + } + + scope.result = scope.engine->newString(value.repeated(int(repeats))); +} + static void appendReplacementString(QString *result, const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) { result->reserve(result->length() + replaceValue.length()); @@ -547,7 +556,7 @@ void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, Call offset = qMax(offset + 1, matchOffsets[oldSize + 1]); } if (regExp->global()) - *regExp->lastIndexProperty() = Primitive::fromUInt32(0); + regExp->setLastIndex(0); numStringMatches = nMatchOffsets / (regExp->value()->captureCount() * 2); numCaptures = regExp->value()->captureCount(); } else { diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index 2e64364f9f..ce046f4844 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -60,14 +60,18 @@ namespace QV4 { namespace Heap { -struct StringObject : Object { +#define StringObjectMembers(class, Member) \ + Member(class, Pointer, String *, string) + +DECLARE_HEAP_OBJECT(StringObject, Object) { + DECLARE_MARK_TABLE(StringObject); + enum { LengthPropertyIndex = 0 }; void init(); void init(const QV4::String *string); - String *string; Heap::String *getIndex(uint index) const; uint length() const; @@ -96,7 +100,6 @@ struct StringObject: Object { protected: static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs); - static void markObjects(Heap::Base *that, ExecutionEngine *e); }; struct StringCtor: FunctionObject @@ -122,6 +125,7 @@ struct StringPrototype: StringObject static void method_lastIndexOf(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_localeCompare(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_match(const BuiltinFunction *, Scope &scope, CallData *callData); + static void method_repeat(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_replace(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_search(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_slice(const BuiltinFunction *, Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index 80655aded6..fe27d7c2d2 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -229,8 +229,8 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat return; } - Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type)); - array->d()->buffer = buffer->d(); + Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type)); + array->d()->buffer.set(scope.engine, buffer->d()); array->d()->byteLength = byteLength; array->d()->byteOffset = 0; @@ -252,8 +252,8 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat return; } - Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type)); - array->d()->buffer = newBuffer->d(); + Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type)); + array->d()->buffer.set(scope.engine, newBuffer->d()); array->d()->byteLength = destByteLength; array->d()->byteOffset = 0; @@ -311,8 +311,8 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat byteLength = (uint)l; } - Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type)); - array->d()->buffer = buffer->d(); + Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type)); + array->d()->buffer.set(scope.engine, buffer->d()); array->d()->byteLength = byteLength; array->d()->byteOffset = byteOffset; scope.result = array.asReturnedValue(); @@ -335,8 +335,8 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat return; } - Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type)); - array->d()->buffer = newBuffer->d(); + Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type)); + array->d()->buffer.set(scope.engine, newBuffer->d()); array->d()->byteLength = l * elementSize; array->d()->byteOffset = 0; @@ -377,12 +377,6 @@ Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type return e->memoryManager->allocObject<TypedArray>(ic, e->typedArrayPrototype + t, t); } -void TypedArray::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - static_cast<TypedArray::Data *>(that)->buffer->mark(e); - Object::markObjects(that, e); -} - ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProperty) { Scope scope(static_cast<const Object *>(m)->engine()); @@ -400,11 +394,11 @@ ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProp return a->d()->type->read(a->d()->buffer->data->data(), byteOffset); } -void TypedArray::putIndexed(Managed *m, uint index, const Value &value) +bool TypedArray::putIndexed(Managed *m, uint index, const Value &value) { ExecutionEngine *v4 = static_cast<Object *>(m)->engine(); if (v4->hasException) - return; + return false; Scope scope(v4); Scoped<TypedArray> a(scope, static_cast<TypedArray *>(m)); @@ -415,11 +409,12 @@ void TypedArray::putIndexed(Managed *m, uint index, const Value &value) goto reject; a->d()->type->write(scope.engine, a->d()->buffer->data->data(), byteOffset, value); - return; + return true; reject: if (scope.engine->current->strictMode) scope.engine->throwTypeError(); + return false; } void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor) diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index a6a74e4b9b..a472dfa607 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -72,7 +72,15 @@ struct TypedArrayOperations { namespace Heap { -struct TypedArray : Object { +#define TypedArrayMembers(class, Member) \ + Member(class, Pointer, ArrayBuffer *, buffer) \ + Member(class, NoMark, const TypedArrayOperations *, type) \ + Member(class, NoMark, uint, byteLength) \ + Member(class, NoMark, uint, byteOffset) \ + Member(class, NoMark, uint, arrayType) + +DECLARE_HEAP_OBJECT(TypedArray, Object) { + DECLARE_MARK_TABLE(TypedArray); enum Type { Int8Array, UInt8Array, @@ -87,12 +95,6 @@ struct TypedArray : Object { }; void init(Type t); - - const TypedArrayOperations *type; - Pointer<ArrayBuffer> buffer; - uint byteLength; - uint byteOffset; - Type arrayType; }; struct TypedArrayCtor : FunctionObject { @@ -128,12 +130,11 @@ struct Q_QML_PRIVATE_EXPORT TypedArray : Object } Heap::TypedArray::Type arrayType() const { - return d()->arrayType; + return static_cast<Heap::TypedArray::Type>(d()->arrayType); } - static void markObjects(Heap::Base *that, ExecutionEngine *e); static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - static void putIndexed(Managed *m, uint index, const Value &value); + static bool putIndexed(Managed *m, uint index, const Value &value); }; struct TypedArrayCtor: FunctionObject diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 9adad881a1..50cecb6598 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -487,7 +487,7 @@ public: // Section 9.12 bool sameValue(Value other) const; - inline void mark(ExecutionEngine *e); + inline void mark(MarkStack *markStack); Value &operator =(const ScopedValue &v); Value &operator=(ReturnedValue v) { _val = v; return *this; } @@ -718,7 +718,6 @@ inline unsigned int Value::toUInt32() const return (unsigned int)toInt32(); } - } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp index 5cab4c5386..f2ff5d307e 100644 --- a/src/qml/jsruntime/qv4variantobject.cpp +++ b/src/qml/jsruntime/qv4variantobject.cpp @@ -84,7 +84,7 @@ bool VariantObject::isEqualTo(Managed *m, Managed *other) return false; } -void VariantObject::addVmePropertyReference() +void VariantObject::addVmePropertyReference() const { if (d()->isScarce() && ++d()->vmePropertyReferenceCount == 1) { // remove from the ep->scarceResources list @@ -94,7 +94,7 @@ void VariantObject::addVmePropertyReference() } } -void VariantObject::removeVmePropertyReference() +void VariantObject::removeVmePropertyReference() const { if (d()->isScarce() && --d()->vmePropertyReferenceCount == 0) { // and add to the ep->scarceResources list diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h index dba14b13fb..a7c6fa320c 100644 --- a/src/qml/jsruntime/qv4variantobject_p.h +++ b/src/qml/jsruntime/qv4variantobject_p.h @@ -96,8 +96,8 @@ struct VariantObject : Object V4_PROTOTYPE(variantPrototype) V4_NEEDS_DESTROY - void addVmePropertyReference(); - void removeVmePropertyReference(); + void addVmePropertyReference() const; + void removeVmePropertyReference() const; static bool isEqualTo(Managed *m, Managed *other); }; diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 3d95353fc0..5749d0aef3 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -338,18 +338,24 @@ Param traceParam(const Param ¶m) return param; } # define VALUE(param) (*VALUEPTR(param)) -# define VALUEPTR(param) (scopes[traceParam(param).scope] + param.index) +# define VALUEPTR(param) (scopes[traceParam(param).scope].values + param.index) #else # define VALUE(param) (*VALUEPTR(param)) -# define VALUEPTR(param) (scopes[param.scope] + param.index) +# define VALUEPTR(param) (scopes[param.scope].values + param.index) #endif +// ### add write barrier here #define STOREVALUE(param, value) { \ QV4::ReturnedValue tmp = (value); \ if (engine->hasException) \ goto catchException; \ - VALUE(param) = tmp; \ - } + if (Q_LIKELY(!engine->writeBarrierActive || !scopes[param.scope].base)) { \ + VALUE(param) = tmp; \ + } else { \ + QV4::WriteBarrier::write(engine, scopes[param.scope].base, VALUEPTR(param), QV4::Value::fromReturnedValue(tmp)); \ + } \ +} + // qv4scopedvalue_p.h also defines a CHECK_EXCEPTION macro #ifdef CHECK_EXCEPTION #undef CHECK_EXCEPTION @@ -396,21 +402,29 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) } } - Q_ALLOCA_VAR(QV4::Value*, scopes, sizeof(QV4::Value *)*(2 + 2*scopeDepth)); + struct Scopes { + QV4::Value *values; + QV4::Heap::Base *base; // non 0 if a write barrier is required + }; + Q_ALLOCA_VAR(Scopes, scopes, sizeof(Scopes)*(2 + 2*scopeDepth)); { - scopes[0] = const_cast<QV4::Value *>(static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->constants); + scopes[0] = { const_cast<QV4::Value *>(static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->constants), 0 }; // stack gets setup in push instruction - scopes[1] = 0; + scopes[1] = { 0, 0 }; QV4::Heap::ExecutionContext *scope = engine->current; int i = 0; while (scope) { - if (scope->type >= QV4::Heap::ExecutionContext::Type_SimpleCallContext) { + if (scope->type == QV4::Heap::ExecutionContext::Type_SimpleCallContext) { + QV4::Heap::SimpleCallContext *cc = static_cast<QV4::Heap::SimpleCallContext *>(scope); + scopes[2*i + 2] = { cc->callData->args, 0 }; + scopes[2*i + 3] = { 0, 0 }; + } else if (scope->type == QV4::Heap::ExecutionContext::Type_CallContext) { QV4::Heap::CallContext *cc = static_cast<QV4::Heap::CallContext *>(scope); - scopes[2*i + 2] = cc->callData->args; - scopes[2*i + 3] = cc->locals; + scopes[2*i + 2] = { cc->callData->args, cc }; + scopes[2*i + 3] = { cc->locals.values, cc }; } else { - scopes[2*i + 2] = 0; - scopes[2*i + 3] = 0; + scopes[2*i + 2] = { 0, 0 }; + scopes[2*i + 3] = { 0, 0 }; } ++i; scope = scope->outer; @@ -477,7 +491,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) MOTH_BEGIN_INSTR(LoadElementLookup) QV4::Lookup *l = engine->current->lookups + instr.lookup; - STOREVALUE(instr.result, l->indexedGetter(l, VALUE(instr.base), VALUE(instr.index))); + STOREVALUE(instr.result, l->indexedGetter(l, engine, VALUE(instr.base), VALUE(instr.index))); MOTH_END_INSTR(LoadElementLookup) MOTH_BEGIN_INSTR(StoreElement) @@ -487,7 +501,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) MOTH_BEGIN_INSTR(StoreElementLookup) QV4::Lookup *l = engine->current->lookups + instr.lookup; - l->indexedSetter(l, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); + l->indexedSetter(l, engine, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(StoreElementLookup) @@ -554,7 +568,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) TRACE(inline, "stack size: %u", instr.value); stackSize = instr.value; stack = scope.alloc(stackSize); - scopes[1] = stack; + scopes[1].values = stack; MOTH_END_INSTR(Push) MOTH_BEGIN_INSTR(CallValue) |