/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qv4object_p.h" #include "qv4jsir_p.h" #include "qv4isel_p.h" #include "qv4objectproto_p.h" #include "qv4stringobject_p.h" #include "qv4argumentsobject_p.h" #include "qv4mm_p.h" #include "qv4lookup_p.h" #include "qv4scopedvalue_p.h" #include #include #include #include #include #include #include "private/qlocale_tools_p.h" #include #include #include #include #include #include #include "qv4alloca_p.h" using namespace QV4; DEFINE_MANAGED_VTABLE(Object); Object::Object(ExecutionEngine *engine) : Managed(engine->objectClass) , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) { } Object::Object(InternalClass *ic) : Managed(ic) , memberDataAlloc(InlinePropertySize), memberData(inlineProperties) { Q_ASSERT(internalClass->vtable && internalClass->vtable != &Managed::static_vtbl); if (internalClass->size >= memberDataAlloc) { memberDataAlloc = internalClass->size; memberData = new Property[memberDataAlloc]; } } Object::~Object() { if (memberData != inlineProperties) delete [] memberData; if (arrayData) arrayData->free(); _data = 0; } bool Object::setPrototype(Object *proto) { Object *pp = proto; while (pp) { if (pp == this) return false; pp = pp->prototype(); } internalClass = internalClass->changePrototype(proto); return true; } void Object::destroy(Managed *that) { static_cast(that)->~Object(); } void Object::put(ExecutionContext *ctx, const QString &name, const ValueRef value) { Scope scope(ctx); ScopedString n(scope, ctx->engine->newString(name)); put(n, value); } ReturnedValue Object::getValue(const ValueRef thisObject, const Property *p, PropertyAttributes attrs) { if (!attrs.isAccessor()) return p->value.asReturnedValue(); FunctionObject *getter = p->getter(); if (!getter) return Encode::undefined(); Scope scope(getter->engine()); ScopedCallData callData(scope, 0); callData->thisObject = *thisObject; return getter->call(callData); } void Object::putValue(Property *pd, PropertyAttributes attrs, const ValueRef value) { if (internalClass->engine->hasException) return; if (attrs.isAccessor()) { if (pd->set) { Scope scope(pd->set->engine()); ScopedCallData callData(scope, 1); callData->args[0] = *value; callData->thisObject = this; pd->set->call(callData); return; } goto reject; } if (!attrs.isWritable()) goto reject; pd->value = *value; return; reject: if (engine()->currentContext()->strictMode) engine()->currentContext()->throwTypeError(); } void Object::defineDefaultProperty(const QString &name, ValueRef value) { ExecutionEngine *e = engine(); Scope scope(e); ScopedString s(scope, e->newIdentifier(name)); defineDefaultProperty(s, value); } void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(CallContext *), int argumentCount) { ExecutionEngine *e = engine(); Scope scope(e); ScopedString s(scope, e->newIdentifier(name)); Scoped function(scope, e->newBuiltinFunction(e->rootContext, s, code)); function->defineReadonlyProperty(e->id_length, Primitive::fromInt32(argumentCount)); defineDefaultProperty(s, function); } void Object::defineDefaultProperty(const StringRef name, ReturnedValue (*code)(CallContext *), int argumentCount) { ExecutionEngine *e = engine(); Scope scope(e); Scoped function(scope, e->newBuiltinFunction(e->rootContext, name, code)); function->defineReadonlyProperty(e->id_length, Primitive::fromInt32(argumentCount)); defineDefaultProperty(name, function); } void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *)) { ExecutionEngine *e = engine(); Scope scope(e); Scoped s(scope, e->newIdentifier(name)); defineAccessorProperty(s, getter, setter); } void Object::defineAccessorProperty(const StringRef name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *)) { ExecutionEngine *v4 = engine(); Property p; p.setGetter(getter ? v4->newBuiltinFunction(v4->rootContext, name, getter)->getPointer() : 0); p.setSetter(setter ? v4->newBuiltinFunction(v4->rootContext, name, setter)->getPointer() : 0); insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); } void Object::defineReadonlyProperty(const QString &name, ValueRef value) { QV4::ExecutionEngine *e = engine(); Scope scope(e); ScopedString s(scope, e->newIdentifier(name)); defineReadonlyProperty(s, value); } void Object::defineReadonlyProperty(const StringRef name, ValueRef value) { insertMember(name, value, Attr_ReadOnly); } void Object::markObjects(Managed *that, ExecutionEngine *e) { Object *o = static_cast(that); if (!o->hasAccessorProperty) { for (uint i = 0; i < o->internalClass->size; ++i) o->memberData[i].value.mark(e); } else { for (uint i = 0; i < o->internalClass->size; ++i) { const Property &pd = o->memberData[i]; if (o->internalClass->propertyData[i].isAccessor()) { if (pd.getter()) pd.getter()->mark(e); if (pd.setter()) pd.setter()->mark(e); } else { pd.value.mark(e); } } } if (o->arrayData) o->arrayData->markObjects(e); } void Object::ensureMemberIndex(uint idx) { if (idx >= memberDataAlloc) { memberDataAlloc = qMax((uint)8, 2*memberDataAlloc); Property *newMemberData = new Property[memberDataAlloc]; memcpy(newMemberData, memberData, sizeof(Property)*idx); memset(newMemberData + idx, 0, sizeof(Property)*(memberDataAlloc - idx)); if (memberData != inlineProperties) delete [] memberData; memberData = newMemberData; } } void Object::insertMember(const StringRef s, const Property &p, PropertyAttributes attributes) { uint idx; internalClass = internalClass->addMember(s.getPointer(), attributes, &idx); if (attributes.isAccessor()) hasAccessorProperty = 1; ensureMemberIndex(idx); memberData[idx] = p; } // Section 8.12.1 Property *Object::__getOwnProperty__(const StringRef name, PropertyAttributes *attrs) { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) return __getOwnProperty__(idx, attrs); uint member = internalClass->find(name); if (member < UINT_MAX) { if (attrs) *attrs = internalClass->propertyData[member]; return memberData + member; } if (attrs) *attrs = Attr_Invalid; return 0; } Property *Object::__getOwnProperty__(uint index, PropertyAttributes *attrs) { Property *p = arrayData->getProperty(index); if (p) { if (attrs) *attrs = arrayData->attributes(index); return p; } if (isStringObject()) { if (attrs) *attrs = Attr_NotConfigurable|Attr_NotWritable; return static_cast(this)->getIndex(index); } if (attrs) *attrs = Attr_Invalid; return 0; } // Section 8.12.2 Property *Object::__getPropertyDescriptor__(const StringRef name, PropertyAttributes *attrs) const { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) return __getPropertyDescriptor__(idx); const Object *o = this; while (o) { uint idx = o->internalClass->find(name.getPointer()); if (idx < UINT_MAX) { if (attrs) *attrs = o->internalClass->propertyData[idx]; return o->memberData + idx; } o = o->prototype(); } if (attrs) *attrs = Attr_Invalid; return 0; } Property *Object::__getPropertyDescriptor__(uint index, PropertyAttributes *attrs) const { const Object *o = this; while (o) { Property *p = o->arrayData->getProperty(index); if (p) { if (attrs) *attrs = o->arrayData->attributes(index); return p; } if (o->isStringObject()) { Property *p = static_cast(o)->getIndex(index); if (p) { if (attrs) *attrs = (Attr_NotWritable|Attr_NotConfigurable); return p; } } o = o->prototype(); } if (attrs) *attrs = Attr_Invalid; return 0; } bool Object::__hasProperty__(const StringRef name) const { if (__getPropertyDescriptor__(name)) return true; const Object *o = this; while (o) { if (!o->query(name).isEmpty()) return true; o = o->prototype(); } return false; } bool Object::__hasProperty__(uint index) const { if (__getPropertyDescriptor__(index)) return true; const Object *o = this; while (o) { if (!o->queryIndexed(index).isEmpty()) return true; o = o->prototype(); } return false; } bool Object::hasOwnProperty(const StringRef name) const { return const_cast(this)->__getOwnProperty__(name) != 0; } bool Object::hasOwnProperty(uint index) const { return const_cast(this)->__getOwnProperty__(index) != 0; } ReturnedValue Object::get(Managed *m, const StringRef name, bool *hasProperty) { return static_cast(m)->internalGet(name, hasProperty); } ReturnedValue Object::getIndexed(Managed *m, uint index, bool *hasProperty) { return static_cast(m)->internalGetIndexed(index, hasProperty); } void Object::put(Managed *m, const StringRef name, const ValueRef value) { static_cast(m)->internalPut(name, value); } void Object::putIndexed(Managed *m, uint index, const ValueRef value) { static_cast(m)->internalPutIndexed(index, value); } PropertyAttributes Object::query(const Managed *m, StringRef name) { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) return queryIndexed(m, idx); const Object *o = static_cast(m); idx = o->internalClass->find(name.getPointer()); if (idx < UINT_MAX) return o->internalClass->propertyData[idx]; return Attr_Invalid; } PropertyAttributes Object::queryIndexed(const Managed *m, uint index) { const Object *o = static_cast(m); if (o->arrayData->get(index) != Primitive::emptyValue().asReturnedValue()) return o->arrayData->attributes(index); if (o->isStringObject()) { Property *p = static_cast(o)->getIndex(index); if (p) return Attr_Data; } return Attr_Invalid; } bool Object::deleteProperty(Managed *m, const StringRef name) { return static_cast(m)->internalDeleteProperty(name); } bool Object::deleteIndexedProperty(Managed *m, uint index) { return static_cast(m)->internalDeleteIndexedProperty(index); } ReturnedValue Object::getLookup(Managed *m, Lookup *l) { Object *o = static_cast(m); PropertyAttributes attrs; Property *p = l->lookup(o, &attrs); if (p) { if (attrs.isData()) { if (l->level == 0) l->getter = Lookup::getter0; else if (l->level == 1) l->getter = Lookup::getter1; else if (l->level == 2) l->getter = Lookup::getter2; return p->value.asReturnedValue(); } else { if (l->level == 0) l->getter = Lookup::getterAccessor0; else if (l->level == 1) l->getter = Lookup::getterAccessor1; else if (l->level == 2) l->getter = Lookup::getterAccessor2; return o->getValue(p, attrs); } } return Encode::undefined(); } void Object::setLookup(Managed *m, Lookup *l, const ValueRef value) { Scope scope(m->engine()); ScopedObject o(scope, static_cast(m)); InternalClass *c = o->internalClass; uint idx = c->find(l->name); if (!o->isArrayObject() || idx != ArrayObject::LengthPropertyIndex) { if (idx != UINT_MAX && o->internalClass->propertyData[idx].isData() && o->internalClass->propertyData[idx].isWritable()) { l->classList[0] = o->internalClass; l->index = idx; l->setter = Lookup::setter0; o->memberData[idx].value = *value; return; } if (idx != UINT_MAX) { o->putValue(o->memberData + idx, o->internalClass->propertyData[idx], value); return; } } ScopedString s(scope, l->name); o->put(s, value); if (o->internalClass == c) return; idx = o->internalClass->find(l->name); if (idx == UINT_MAX) return; l->classList[0] = c; l->classList[3] = o->internalClass; l->index = idx; if (!o->prototype()) { l->setter = Lookup::setterInsert0; return; } o = o->prototype(); l->classList[1] = o->internalClass; if (!o->prototype()) { l->setter = Lookup::setterInsert1; return; } o = o->prototype(); l->classList[2] = o->internalClass; if (!o->prototype()) l->setter = Lookup::setterInsert2; } Property *Object::advanceIterator(Managed *m, ObjectIterator *it, StringRef name, uint *index, PropertyAttributes *attrs) { Object *o = static_cast(m); name = (String *)0; *index = UINT_MAX; if (o->arrayData) { if (!it->arrayIndex) it->arrayNode = o->sparseBegin(); // sparse arrays if (it->arrayNode) { while (it->arrayNode != o->sparseEnd()) { int k = it->arrayNode->key(); uint pidx = it->arrayNode->value; Property *p = o->arrayData->data + pidx; it->arrayNode = it->arrayNode->nextNode(); PropertyAttributes a = o->arrayData->attributes(k); if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { it->arrayIndex = k + 1; *index = k; if (attrs) *attrs = a; return p; } } it->arrayNode = 0; it->arrayIndex = UINT_MAX; } // dense arrays while (it->arrayIndex < o->arrayData->length()) { Property *p = o->arrayData->data + it->arrayIndex; PropertyAttributes a = o->arrayData->attributes(it->arrayIndex); ++it->arrayIndex; if (!p->value.isEmpty() && (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable())) { *index = it->arrayIndex - 1; if (attrs) *attrs = a; return p; } } } while (it->memberIndex < o->internalClass->size) { String *n = o->internalClass->nameMap.at(it->memberIndex); assert(n); Property *p = o->memberData + it->memberIndex; PropertyAttributes a = o->internalClass->propertyData[it->memberIndex]; ++it->memberIndex; if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { name = n; if (attrs) *attrs = a; return p; } } return 0; } // Section 8.12.3 ReturnedValue Object::internalGet(const StringRef name, bool *hasProperty) { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) return getIndexed(idx, hasProperty); name->makeIdentifier(); Object *o = this; while (o) { uint idx = o->internalClass->find(name.getPointer()); if (idx < UINT_MAX) { if (hasProperty) *hasProperty = true; return getValue(o->memberData + idx, o->internalClass->propertyData.at(idx)); } o = o->prototype(); } if (hasProperty) *hasProperty = false; return Encode::undefined(); } ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) { Property *pd = 0; PropertyAttributes attrs; Object *o = this; while (o) { Property *p = o->arrayData->getProperty(index); if (p) { pd = p; attrs = o->arrayData->attributes(index); break; } if (o->isStringObject()) { pd = static_cast(o)->getIndex(index); if (pd) { attrs = (Attr_NotWritable|Attr_NotConfigurable); break; } } o = o->prototype(); } if (pd) { if (hasProperty) *hasProperty = true; return getValue(pd, attrs); } if (hasProperty) *hasProperty = false; return Encode::undefined(); } // Section 8.12.5 void Object::internalPut(const StringRef name, const ValueRef value) { if (internalClass->engine->hasException) return; uint idx = name->asArrayIndex(); if (idx != UINT_MAX) return putIndexed(idx, value); name->makeIdentifier(); uint member = internalClass->find(name.getPointer()); Property *pd = 0; PropertyAttributes attrs; if (member < UINT_MAX) { pd = memberData + member; attrs = internalClass->propertyData[member]; } // clause 1 if (pd) { if (attrs.isAccessor()) { if (pd->setter()) goto cont; goto reject; } else if (!attrs.isWritable()) goto reject; else if (isArrayObject() && name->equals(engine()->id_length)) { bool ok; uint l = value->asArrayLength(&ok); if (!ok) { engine()->currentContext()->throwRangeError(value); return; } ok = setArrayLength(l); if (!ok) goto reject; } else { pd->value = *value; } return; } else if (!prototype()) { if (!extensible) goto reject; } else { // clause 4 if ((pd = prototype()->__getPropertyDescriptor__(name, &attrs))) { if (attrs.isAccessor()) { if (!pd->setter()) goto reject; } else if (!extensible || !attrs.isWritable()) { goto reject; } } else if (!extensible) { goto reject; } } cont: // Clause 5 if (pd && attrs.isAccessor()) { assert(pd->setter() != 0); Scope scope(engine()); ScopedCallData callData(scope, 1); callData->args[0] = *value; callData->thisObject = this; pd->setter()->call(callData); return; } insertMember(name, value); return; reject: if (engine()->currentContext()->strictMode) { QString message = QStringLiteral("Cannot assign to read-only property \""); message += name->toQString(); message += QLatin1Char('\"'); engine()->currentContext()->throwTypeError(message); } } void Object::internalPutIndexed(uint index, const ValueRef value) { if (internalClass->engine->hasException) return; PropertyAttributes attrs; Property *pd = arrayData->getProperty(index); if (pd) attrs = arrayData->attributes(index); if (!pd && isStringObject()) { pd = static_cast(this)->getIndex(index); if (pd) // not writable goto reject; } // clause 1 if (pd) { if (attrs.isAccessor()) { if (pd->setter()) goto cont; goto reject; } else if (!attrs.isWritable()) goto reject; else pd->value = *value; return; } else if (!prototype()) { if (!extensible) goto reject; } else { // clause 4 if ((pd = prototype()->__getPropertyDescriptor__(index, &attrs))) { if (attrs.isAccessor()) { if (!pd->setter()) goto reject; } else if (!extensible || !attrs.isWritable()) { goto reject; } } else if (!extensible) { goto reject; } } cont: // Clause 5 if (pd && attrs.isAccessor()) { assert(pd->setter() != 0); Scope scope(engine()); ScopedCallData callData(scope, 1); callData->args[0] = *value; callData->thisObject = this; pd->setter()->call(callData); return; } arraySet(index, value); return; reject: if (engine()->currentContext()->strictMode) engine()->currentContext()->throwTypeError(); } // Section 8.12.7 bool Object::internalDeleteProperty(const StringRef name) { if (internalClass->engine->hasException) return false; uint idx = name->asArrayIndex(); if (idx != UINT_MAX) return deleteIndexedProperty(idx); name->makeIdentifier(); uint memberIdx = internalClass->find(name); if (memberIdx != UINT_MAX) { if (internalClass->propertyData[memberIdx].isConfigurable()) { internalClass->removeMember(this, name->identifier); memmove(memberData + memberIdx, memberData + memberIdx + 1, (internalClass->size - memberIdx)*sizeof(Property)); return true; } if (engine()->currentContext()->strictMode) engine()->currentContext()->throwTypeError(); return false; } return true; } bool Object::internalDeleteIndexedProperty(uint index) { if (internalClass->engine->hasException) return false; if (!arrayData || arrayData->deleteIndex(index)) return true; if (engine()->currentContext()->strictMode) engine()->currentContext()->throwTypeError(); return false; } // Section 8.12.9 bool Object::__defineOwnProperty__(ExecutionContext *ctx, const StringRef name, const Property &p, PropertyAttributes attrs) { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) return __defineOwnProperty__(ctx, idx, p, attrs); name->makeIdentifier(); Scope scope(ctx); Property *current; PropertyAttributes *cattrs; if (isArrayObject() && name->equals(ctx->engine->id_length)) { assert(ArrayObject::LengthPropertyIndex == internalClass->find(ctx->engine->id_length)); Property *lp = memberData + ArrayObject::LengthPropertyIndex; cattrs = internalClass->propertyData.constData() + ArrayObject::LengthPropertyIndex; if (attrs.isEmpty() || p.isSubset(attrs, *lp, *cattrs)) return true; if (!cattrs->isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable()) goto reject; bool succeeded = true; if (attrs.type() == PropertyAttributes::Data) { bool ok; uint l = p.value.asArrayLength(&ok); if (!ok) { ScopedValue v(scope, p.value); ctx->throwRangeError(v); return false; } succeeded = setArrayLength(l); } if (attrs.hasWritable() && !attrs.isWritable()) cattrs->setWritable(false); if (!succeeded) goto reject; if (attrs.isAccessor()) hasAccessorProperty = 1; return true; } // Clause 1 { uint member = internalClass->find(name.getPointer()); current = (member < UINT_MAX) ? memberData + member : 0; cattrs = internalClass->propertyData.constData() + member; } if (!current) { // clause 3 if (!extensible) goto reject; // clause 4 Property pd = p; pd.fullyPopulated(&attrs); insertMember(name, pd, attrs); return true; } return __defineOwnProperty__(ctx, current, name, p, attrs); reject: if (ctx->strictMode) ctx->throwTypeError(); return false; } bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs) { // 15.4.5.1, 4b if (isArrayObject() && index >= getLength() && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) goto reject; if (ArgumentsObject::isNonStrictArgumentsObject(this)) return static_cast(this)->defineOwnProperty(ctx, index, p, attrs); return defineOwnProperty2(ctx, index, p, attrs); reject: if (ctx->strictMode) ctx->throwTypeError(); return false; } bool Object::defineOwnProperty2(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs) { Property *current = 0; // Clause 1 { current = arrayData->getProperty(index); if (!current && isStringObject()) current = static_cast(this)->getIndex(index); } if (!current) { // clause 3 if (!extensible) goto reject; // clause 4 Property pp(p); pp.fullyPopulated(&attrs); if (attrs == Attr_Data) { Scope scope(ctx); ScopedValue v(scope, pp.value); arraySet(index, v); } else { arraySet(index, pp, attrs); } return true; } return __defineOwnProperty__(ctx, current, StringRef::null(), p, attrs); reject: if (ctx->strictMode) ctx->throwTypeError(); return false; } bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, const StringRef member, const Property &p, PropertyAttributes attrs) { // clause 5 if (attrs.isEmpty()) return true; PropertyAttributes cattrs; if (!member.isNull()) cattrs = internalClass->propertyData[current - memberData]; else cattrs = arrayData->attrs ? arrayData->attrs[current - arrayData->data] : Attr_Data; // clause 6 if (p.isSubset(attrs, *current, cattrs)) return true; // clause 7 if (!cattrs.isConfigurable()) { if (attrs.isConfigurable()) goto reject; if (attrs.hasEnumerable() && attrs.isEnumerable() != cattrs.isEnumerable()) goto reject; } // clause 8 if (attrs.isGeneric() || current->value.isEmpty()) goto accept; // clause 9 if (cattrs.isData() != attrs.isData()) { // 9a if (!cattrs.isConfigurable()) goto reject; if (cattrs.isData()) { // 9b cattrs.setType(PropertyAttributes::Accessor); cattrs.clearWritable(); current->setGetter(0); current->setSetter(0); } else { // 9c cattrs.setType(PropertyAttributes::Data); cattrs.setWritable(false); current->value = Primitive::undefinedValue(); } } else if (cattrs.isData() && attrs.isData()) { // clause 10 if (!cattrs.isConfigurable() && !cattrs.isWritable()) { if (attrs.isWritable() || !current->value.sameValue(p.value)) goto reject; } } else { // clause 10 assert(cattrs.isAccessor() && attrs.isAccessor()); if (!cattrs.isConfigurable()) { if (p.getter() && !(current->getter() == p.getter() || (!current->getter() && (quintptr)p.getter() == 0x1))) goto reject; if (p.setter() && !(current->setter() == p.setter() || (!current->setter() && (quintptr)p.setter() == 0x1))) goto reject; } } accept: current->merge(cattrs, p, attrs); if (!member.isNull()) { internalClass = internalClass->changeMember(member.getPointer(), cattrs); } else { arrayData->setAttributes(current - arrayData->data, cattrs); } if (attrs.isAccessor()) hasAccessorProperty = 1; return true; reject: if (ctx->strictMode) ctx->throwTypeError(); return false; } bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, const Property &p, PropertyAttributes attrs) { Scope scope(ctx); ScopedString s(scope, ctx->engine->newString(name)); return __defineOwnProperty__(ctx, s, p, attrs); } void Object::copyArrayData(Object *other) { Q_ASSERT(isArrayObject()); Scope scope(engine()); if (other->protoHasArray() || other->hasAccessorProperty) { uint len = other->getLength(); Q_ASSERT(len); ScopedValue v(scope); for (uint i = 0; i < len; ++i) { arraySet(i, (v = other->getIndexed(i))); } } else { Q_ASSERT(!arrayData && other->arrayData); if (other->arrayType() == ArrayData::Sparse) { SparseArrayData *od = static_cast(other->arrayData); SparseArrayData *dd = new SparseArrayData; dd->type = ArrayData::Sparse; dd->sparse = new SparseArray(*od->sparse); dd->freeList = od->freeList; arrayData = dd; } arrayReserve(other->arrayData->len); arrayData->len = other->arrayData->len; // ### correctly deal with accessor properties memcpy(arrayData->data, other->arrayData->data, arrayData->len*sizeof(Property)); arrayData->offset = 0; } setArrayLengthUnchecked(other->getLength()); } uint Object::getLength(const Managed *m) { Scope scope(m->engine()); ScopedValue v(scope, const_cast(m)->get(scope.engine->id_length)); return v->toUInt32(); } bool Object::setArrayLength(uint newLen) { Q_ASSERT(isArrayObject()); const Property *lengthProperty = memberData + ArrayObject::LengthPropertyIndex; if (lengthProperty && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) return false; uint oldLen = getLength(); bool ok = true; if (newLen < oldLen) { uint l = arrayData->truncate(newLen); if (l != newLen) ok = false; newLen = l; } else { if (newLen >= 0x100000) initSparseArray(); } setArrayLengthUnchecked(newLen); return ok; } void Object::initSparseArray() { if (arrayType() == ArrayData::Sparse) return; SparseArrayData *data = new SparseArrayData; data->type = ArrayData::Sparse; data->sparse = new SparseArray; if (!arrayData) { arrayData = data; return; } uint oldOffset = arrayData->offset; data->data = arrayData->data - arrayData->offset; data->attrs = arrayData->attrs; data->len = arrayData->len; data->alloc = arrayData->alloc; data->offset = 0; arrayData->data = 0; arrayData->attrs = 0; delete arrayData; uint *lastFree = &data->freeList; for (uint i = 0; i < oldOffset; ++i) { *lastFree = i; lastFree = &data->data[i].value.uint_32; } for (uint i = 0; i < data->len; ++i) { if (!data->data[i + oldOffset].value.isEmpty()) { SparseArrayNode *n = data->sparse->insert(i); n->value = i + oldOffset; } else { *lastFree = i + oldOffset; lastFree = &data->data[i + oldOffset].value.uint_32; } } for (uint i = data->len + oldOffset; i < data->alloc; ++i) { *lastFree = i; lastFree = &data->data[i].value.uint_32; } arrayData = data; } DEFINE_MANAGED_VTABLE(ArrayObject); ArrayObject::ArrayObject(ExecutionEngine *engine, const QStringList &list) : Object(engine->arrayClass) { init(engine); Scope scope(engine); ScopedValue protectThis(scope, this); // Converts a QStringList to JS. // The result is a new Array object with length equal to the length // of the QStringList, and the elements being the QStringList's // elements converted to JS Strings. int len = list.count(); arrayReserve(len); ScopedValue v(scope); for (int ii = 0; ii < len; ++ii) { arrayData->put(ii, (v = engine->newString(list.at(ii)))); arrayData->setLength(ii + 1); } setArrayLengthUnchecked(len); } void ArrayObject::init(ExecutionEngine *engine) { Q_UNUSED(engine); memberData[LengthPropertyIndex].value = Primitive::fromInt32(0); } ReturnedValue ArrayObject::getLookup(Managed *m, Lookup *l) { if (l->name->equals(m->engine()->id_length)) { // special case, as the property is on the object itself l->getter = Lookup::arrayLengthGetter; ArrayObject *a = static_cast(m); return a->memberData[ArrayObject::LengthPropertyIndex].value.asReturnedValue(); } return Object::getLookup(m, l); } uint ArrayObject::getLength(const Managed *m) { const ArrayObject *a = static_cast(m); if (a->memberData[ArrayObject::LengthPropertyIndex].value.isInteger()) return a->memberData[ArrayObject::LengthPropertyIndex].value.integerValue(); return Primitive::toUInt32(a->memberData[ArrayObject::LengthPropertyIndex].value.doubleValue()); } QStringList ArrayObject::toQStringList() const { QStringList result; QV4::ExecutionEngine *engine = internalClass->engine; Scope scope(engine); ScopedValue v(scope); uint32_t length = getLength(); for (uint32_t i = 0; i < length; ++i) { v = const_cast(this)->getIndexed(i); result.append(v->toQStringNoThrow()); } return result; }