diff options
Diffstat (limited to 'src')
33 files changed, 907 insertions, 638 deletions
diff --git a/src/v4/debugging.cpp b/src/v4/debugging.cpp index e41dba2086..30ab4a1ed9 100644 --- a/src/v4/debugging.cpp +++ b/src/v4/debugging.cpp @@ -294,8 +294,9 @@ static void realDumpValue(VM::Value v, VM::ExecutionContext *ctx, std::string pr ForEachIteratorObject it(ctx, o); for (Value name = it.nextPropertyName(); !name.isNull(); name = it.nextPropertyName()) { cout << prefix << "\t\"" << qPrintable(name.stringValue()->toQString()) << "\"" << endl; - PropertyDescriptor *d = o->__getOwnProperty__(ctx, name.stringValue()); - Value pval = o->getValue(ctx, d); + PropertyAttributes attrs; + Property *d = o->__getOwnProperty__(ctx, name.stringValue(), &attrs); + Value pval = o->getValue(ctx, d, attrs); cout << prefix << "\tvalue:" << endl; realDumpValue(pval, ctx, prefix + "\t"); } diff --git a/src/v4/qv4argumentsobject.cpp b/src/v4/qv4argumentsobject.cpp index 0378112356..21c72be460 100644 --- a/src/v4/qv4argumentsobject.cpp +++ b/src/v4/qv4argumentsobject.cpp @@ -63,22 +63,19 @@ ArgumentsObject::ArgumentsObject(CallContext *context, int formalParameterCount, for (uint i = 0; i < context->argumentCount; ++i) Object::put(context, QString::number(i), context->arguments[i]); FunctionObject *thrower = context->engine->newBuiltinFunction(context, 0, throwTypeError); - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); - pd.attrs = Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable; - __defineOwnProperty__(context, QStringLiteral("callee"), &pd); - __defineOwnProperty__(context, QStringLiteral("caller"), &pd); + Property pd = Property::fromAccessor(thrower, thrower); + __defineOwnProperty__(context, QStringLiteral("callee"), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); + __defineOwnProperty__(context, QStringLiteral("caller"), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } else { uint numAccessors = qMin(formalParameterCount, actualParameterCount); context->engine->requireArgumentsAccessors(numAccessors); for (uint i = 0; i < (uint)numAccessors; ++i) { mappedArguments.append(context->argument(i)); - __defineOwnProperty__(context, i, &context->engine->argumentsAccessors.at(i)); + __defineOwnProperty__(context, i, context->engine->argumentsAccessors.at(i), Attr_Accessor); } - PropertyDescriptor pd; - pd.attrs = Attr_Data; for (uint i = numAccessors; i < qMin((uint)actualParameterCount, context->argumentCount); ++i) { - pd.value = context->argument(i); - __defineOwnProperty__(context, i, &pd); + Property pd = Property::fromValue(context->argument(i)); + __defineOwnProperty__(context, i, pd, Attr_Data); } defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); isNonStrictArgumentsObject = true; @@ -90,34 +87,39 @@ void ArgumentsObject::destroy(Managed *that) static_cast<ArgumentsObject *>(that)->~ArgumentsObject(); } -bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc) +bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs) { - PropertyDescriptor *pd = arrayAt(index); - PropertyDescriptor map; + uint pidx = propertyIndexFromArrayIndex(index); + Property *pd = arrayData + pidx; + Property map; + PropertyAttributes mapAttrs; bool isMapped = false; if (pd && index < (uint)mappedArguments.size()) - isMapped = pd->attrs.isAccessor() && pd->get == context->engine->argumentsAccessors.at(index).get; + isMapped = arrayAttributes && arrayAttributes[pidx].isAccessor() && pd->getter() == context->engine->argumentsAccessors.at(index).getter(); if (isMapped) { map = *pd; - pd->attrs = Attr_Data; + mapAttrs = arrayAttributes[pidx]; + arrayAttributes[pidx] = Attr_Data; pd->value = mappedArguments.at(index); } isNonStrictArgumentsObject = false; bool strict = ctx->strictMode; ctx->strictMode = false; - bool result = Object::__defineOwnProperty__(ctx, index, desc); + bool result = Object::__defineOwnProperty__(ctx, index, desc, attrs); ctx->strictMode = strict; isNonStrictArgumentsObject = true; - if (isMapped && desc->attrs.isData()) { - if (desc->attrs.type() != PropertyAttributes::Generic) { - Value arg = desc->value; - map.set->call(ctx, Value::fromObject(this), &arg, 1); + if (isMapped && attrs.isData()) { + if (!attrs.isGeneric()) { + Value arg = desc.value; + map.setter()->call(ctx, Value::fromObject(this), &arg, 1); } - if (desc->attrs.isWritable()) + if (attrs.isWritable()) { *pd = map; + arrayAttributes[pidx] = mapAttrs; + } } if (ctx->strictMode && !result) diff --git a/src/v4/qv4argumentsobject.h b/src/v4/qv4argumentsobject.h index 4dcdadd2d8..6727d8759b 100644 --- a/src/v4/qv4argumentsobject.h +++ b/src/v4/qv4argumentsobject.h @@ -82,7 +82,7 @@ struct ArgumentsObject: Object { ArgumentsObject(CallContext *context, int formalParameterCount, int actualParameterCount); ~ArgumentsObject() {} - bool defineOwnProperty(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); + bool defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs); static void markObjects(Managed *that); protected: diff --git a/src/v4/qv4arrayobject.cpp b/src/v4/qv4arrayobject.cpp index 1ca9cf947c..39492ef099 100644 --- a/src/v4/qv4arrayobject.cpp +++ b/src/v4/qv4arrayobject.cpp @@ -69,7 +69,7 @@ Value ArrayCtor::construct(Managed *, ExecutionContext *ctx, Value *argv, int ar len = argc; a->arrayReserve(len); for (unsigned int i = 0; i < len; ++i) - fillDescriptor(a->arrayData + i, argv[i]); + a->arrayData[i].value = argv[i]; a->arrayDataLen = len; } a->setArrayLengthUnchecked(len); @@ -270,7 +270,9 @@ Value ArrayPrototype::method_push(SimpleCallContext *ctx) if (!instance->sparseArray) { if (len >= instance->arrayAlloc) instance->arrayReserve(len + 1); - fillDescriptor(instance->arrayData + len, v); + instance->arrayData[len].value = v; + if (instance->arrayAttributes) + instance->arrayAttributes[len] = Attr_Data; instance->arrayDataLen = len + 1; } else { uint i = instance->allocArrayValue(v); @@ -328,19 +330,12 @@ Value ArrayPrototype::method_shift(SimpleCallContext *ctx) return Value::undefinedValue(); } - PropertyDescriptor *front = 0; - if (!instance->sparseArray) { - if (instance->arrayDataLen) - front = instance->arrayData; - } else { - SparseArrayNode *n = instance->sparseArray->findNode(0); - if (n) - front = instance->arrayDecriptor(n->value); - } - if (front && front->attrs.type() == PropertyAttributes::Generic) - front = 0; + Property *front = 0; + uint pidx = instance->propertyIndexFromArrayIndex(0); + if (pidx < UINT_MAX && (!instance->arrayAttributes || !instance->arrayAttributes[0].isGeneric())) + front = instance->arrayData + pidx; - Value result = instance->getValueChecked(ctx, front); + Value result = front ? instance->getValue(ctx, front, instance->arrayAttributes ? instance->arrayAttributes[pidx] : Attr_Data) : Value::undefinedValue(); bool protoHasArray = false; Object *p = instance; @@ -355,6 +350,8 @@ Value ArrayPrototype::method_shift(SimpleCallContext *ctx) ++instance->arrayData; --instance->arrayDataLen; --instance->arrayAlloc; + if (instance->arrayAttributes) + ++instance->arrayAttributes; } } else { uint idx = instance->sparseArray->pop_front(); @@ -445,9 +442,8 @@ Value ArrayPrototype::method_splice(SimpleCallContext *ctx) uint deleteCount = (uint)qMin(qMax(ctx->argument(1).toInteger(ctx), 0.), (double)(len - start)); newArray->arrayReserve(deleteCount); - PropertyDescriptor *pd = newArray->arrayData; + Property *pd = newArray->arrayData; for (uint i = 0; i < deleteCount; ++i) { - pd->attrs = Attr_Data; pd->value = instance->getIndexed(ctx, start + i); ++pd; } @@ -511,7 +507,11 @@ Value ArrayPrototype::method_unshift(SimpleCallContext *ctx) --instance->arrayOffset; --instance->arrayData; ++instance->arrayDataLen; - fillDescriptor(instance->arrayData, v); + if (instance->arrayAttributes) { + --instance->arrayAttributes; + *instance->arrayAttributes = Attr_Data; + } + instance->arrayData->value = v; } else { uint idx = instance->allocArrayValue(v); instance->sparseArray->push_front(idx); diff --git a/src/v4/qv4context.cpp b/src/v4/qv4context.cpp index 97038090b2..24a8563c14 100644 --- a/src/v4/qv4context.cpp +++ b/src/v4/qv4context.cpp @@ -98,13 +98,10 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) if (activation->__hasProperty__(this, name)) return; - PropertyDescriptor desc; - desc.value = Value::undefinedValue(); - desc.attrs.setType(PropertyAttributes::Data); - desc.attrs.setConfigurable(deletable); - desc.attrs.setWritable(true); - desc.attrs.setEnumerable(true); - activation->__defineOwnProperty__(this, name, &desc); + Property desc = Property::fromValue(Value::undefinedValue()); + PropertyAttributes attrs(Attr_Data); + attrs.setConfigurable(deletable); + activation->__defineOwnProperty__(this, name, desc, attrs); } String * const *ExecutionContext::formals() const @@ -205,10 +202,8 @@ void CallContext::initCallContext(ExecutionEngine *engine) args->prototype = engine->objectPrototype; Value arguments = Value::fromObject(args); activation = engine->newObject(); - PropertyDescriptor desc; - desc.value = Value::fromObject(args); - desc.attrs = PropertyAttributes(Attr_NotConfigurable); - activation->__defineOwnProperty__(this, engine->id_arguments, &desc); + Property desc = Property::fromValue(Value::fromObject(args)); + activation->__defineOwnProperty__(this, engine->id_arguments, desc, Attr_NotConfigurable); } } diff --git a/src/v4/qv4engine.cpp b/src/v4/qv4engine.cpp index 54ddf4325a..7d5c79f333 100644 --- a/src/v4/qv4engine.cpp +++ b/src/v4/qv4engine.cpp @@ -109,7 +109,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) id_eval = newIdentifier(QStringLiteral("eval")); emptyClass = new InternalClass(this); - arrayClass = emptyClass->addMember(id_length, Attr_ReadOnly); + arrayClass = emptyClass->addMember(id_length, Attr_NotConfigurable|Attr_NotEnumerable); initRootContext(); objectPrototype = new (memoryManager) ObjectPrototype(this); @@ -482,9 +482,7 @@ void ExecutionEngine::requireArgumentsAccessors(int n) get->prototype = functionPrototype; FunctionObject *set = new (memoryManager) ArgumentsSetterFunction(rootContext, i); set->prototype = functionPrototype; - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(get, set); - pd.attrs.setConfigurable(true); - pd.attrs.setEnumerable(true); + Property pd = Property::fromAccessor(get, set); argumentsAccessors[i] = pd; } } @@ -499,9 +497,9 @@ void ExecutionEngine::markObjects() globalCode->mark(); for (int i = 0; i < argumentsAccessors.size(); ++i) { - const PropertyDescriptor &pd = argumentsAccessors.at(i); - pd.get->mark(); - pd.set->mark(); + const Property &pd = argumentsAccessors.at(i); + pd.getter()->mark(); + pd.setter()->mark(); } ExecutionContext *c = current; diff --git a/src/v4/qv4engine.h b/src/v4/qv4engine.h index 6170c082ad..9cf36d408d 100644 --- a/src/v4/qv4engine.h +++ b/src/v4/qv4engine.h @@ -46,6 +46,7 @@ #include "qv4object.h" #include "qv4util.h" #include "qv4context.h" +#include "qv4propertydescriptor.h" #include <setjmp.h> #include <wtf/BumpPointerAllocator.h> @@ -159,7 +160,7 @@ struct Q_V4_EXPORT ExecutionEngine EvalFunction *evalFunction; - QVector<PropertyDescriptor> argumentsAccessors; + QVector<Property> argumentsAccessors; String *id_undefined; String *id_null; diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 8393ec9de4..cff0b59f68 100644 --- a/src/v4/qv4functionobject.cpp +++ b/src/v4/qv4functionobject.cpp @@ -341,16 +341,14 @@ ScriptFunction::ScriptFunction(ExecutionContext *scope, Function *function) Object *proto = scope->engine->newObject(); proto->defineDefaultProperty(scope->engine->id_constructor, Value::fromObject(this)); - PropertyDescriptor *pd = insertMember(scope->engine->id_prototype, Attr_NotEnumerable|Attr_NotConfigurable); - pd->attrs = Attr_Data|Attr_NotEnumerable|Attr_NotConfigurable; + Property *pd = insertMember(scope->engine->id_prototype, Attr_NotEnumerable|Attr_NotConfigurable); pd->value = Value::fromObject(proto); if (scope->strictMode) { FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, throwTypeError); - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); - pd.attrs = Attr_Accessor|Attr_NotEnumerable|Attr_NotConfigurable; - __defineOwnProperty__(scope, QStringLiteral("caller"), &pd); - __defineOwnProperty__(scope, QStringLiteral("arguments"), &pd); + Property pd = Property::fromAccessor(thrower, thrower); + __defineOwnProperty__(scope, QStringLiteral("caller"), pd, Attr_Accessor|Attr_NotEnumerable|Attr_NotConfigurable); + __defineOwnProperty__(scope, QStringLiteral("arguments"), pd, Attr_Accessor|Attr_NotEnumerable|Attr_NotConfigurable); } } @@ -475,8 +473,7 @@ BoundFunction::BoundFunction(ExecutionContext *scope, FunctionObject *target, Va defineReadonlyProperty(scope->engine->id_length, Value::fromInt32(len)); FunctionObject *thrower = scope->engine->newBuiltinFunction(scope, 0, throwTypeError); - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); - pd.attrs = Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable; + Property pd = Property::fromAccessor(thrower, thrower); *insertMember(scope->engine->id_arguments, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd; *insertMember(scope->engine->id_caller, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable) = pd; } diff --git a/src/v4/qv4functionobject.h b/src/v4/qv4functionobject.h index c20eb69ee8..1be3dd5e6f 100644 --- a/src/v4/qv4functionobject.h +++ b/src/v4/qv4functionobject.h @@ -109,15 +109,17 @@ struct Lookup { uint index; String *name; - PropertyDescriptor *lookup(Object *obj) { + Property *lookup(Object *obj, PropertyAttributes *attrs) { int i = 0; while (i < level && obj && obj->internalClass == classList[i]) { obj = obj->prototype; ++i; } - if (index != UINT_MAX && obj->internalClass == classList[i]) + if (index != UINT_MAX && obj->internalClass == classList[i]) { + *attrs = obj->internalClass->propertyData.at(index); return obj->memberData + index; + } while (i < Size && obj) { classList[i] = obj->internalClass; @@ -125,6 +127,7 @@ struct Lookup { index = obj->internalClass->find(name); if (index != UINT_MAX) { level = i; + *attrs = obj->internalClass->propertyData.at(index); return obj->memberData + index; } @@ -135,22 +138,27 @@ struct Lookup { while (obj) { index = obj->internalClass->find(name); - if (index != UINT_MAX) + if (index != UINT_MAX) { + *attrs = obj->internalClass->propertyData.at(index); return obj->memberData + index; + } obj = obj->prototype; } return 0; } - PropertyDescriptor *setterLookup(Object *o) { - if (o->internalClass == classList[0]) + Property *setterLookup(Object *o, bool *writable) { + if (o->internalClass == classList[0]) { + *writable = o->internalClass->propertyData[index].isWritable(); return o->memberData + index; + } uint idx = o->internalClass->find(name); if (idx != UINT_MAX) { classList[0] = o->internalClass; index = idx; + *writable = o->internalClass->propertyData[index].isWritable(); return o->memberData + index; } return 0; diff --git a/src/v4/qv4global.h b/src/v4/qv4global.h index 928664de0a..5f3f1b6d17 100644 --- a/src/v4/qv4global.h +++ b/src/v4/qv4global.h @@ -101,18 +101,22 @@ struct PropertyAttributes PropertyAttributes() : m_all(0) {} PropertyAttributes(PropertyFlag f) : m_all(0) { - setType(f & Attr_Accessor ? Accessor : Data); - if (!(f & Attr_Accessor)) - setWritable(!(f & Attr_NotWritable)); - setEnumerable(!(f & Attr_NotEnumerable)); - setConfigurable(!(f & Attr_NotConfigurable)); + if (f != Attr_Invalid) { + setType(f & Attr_Accessor ? Accessor : Data); + if (!(f & Attr_Accessor)) + setWritable(!(f & Attr_NotWritable)); + setEnumerable(!(f & Attr_NotEnumerable)); + setConfigurable(!(f & Attr_NotConfigurable)); + } } PropertyAttributes(PropertyFlags f) : m_all(0) { - setType(f & Attr_Accessor ? Accessor : Data); - if (!(f & Attr_Accessor)) - setWritable(!(f & Attr_NotWritable)); - setEnumerable(!(f & Attr_NotEnumerable)); - setConfigurable(!(f & Attr_NotConfigurable)); + if (f != Attr_Invalid) { + setType(f & Attr_Accessor ? Accessor : Data); + if (!(f & Attr_Accessor)) + setWritable(!(f & Attr_NotWritable)); + setEnumerable(!(f & Attr_NotEnumerable)); + setConfigurable(!(f & Attr_NotConfigurable)); + } } PropertyAttributes(const PropertyAttributes &other) : m_all(other.m_all) {} PropertyAttributes & operator=(const PropertyAttributes &other) { m_all = other.m_all; return *this; } @@ -133,12 +137,9 @@ struct PropertyAttributes void setConfigurable(bool b) { m_configurable = b; configurable_set = true; } void setEnumerable(bool b) { m_enumerable = b; enumerable_set = true; } - void resolveType() { type_set = true; } - void resolveWritable() { writable_set = true; } - void resolveConfigurable() { configurable_set = true; } - void resolveEnumerable() { enumerable_set = true; } + void resolve() { m_mask = 0xf; if (m_type == Accessor) { m_writable = false; writable_set = false; } } - bool isWritable() const { return m_writable; } + bool isWritable() const { return m_type != Data || m_writable; } bool isEnumerable() const { return m_enumerable; } bool isConfigurable() const { return m_configurable; } @@ -151,6 +152,13 @@ struct PropertyAttributes bool isEmpty() const { return !m_all; } uint flags() const { return m_flags; } + + bool operator==(PropertyAttributes other) { + return m_all == other.m_all; + } + bool operator!=(PropertyAttributes other) { + return m_all != other.m_all; + } }; } diff --git a/src/v4/qv4internalclass.cpp b/src/v4/qv4internalclass.cpp index 00e891b205..4d9bb93d0d 100644 --- a/src/v4/qv4internalclass.cpp +++ b/src/v4/qv4internalclass.cpp @@ -54,14 +54,45 @@ InternalClass::InternalClass(const QQmlJS::VM::InternalClass &other) , nameMap(other.nameMap) , propertyData(other.propertyData) , transitions() + , m_sealed(0) + , m_frozen(0) , size(other.size) { } +// ### Should we build this up from the empty class to avoid duplication? +InternalClass *InternalClass::changeMember(String *string, PropertyAttributes data, uint *index) +{ +// qDebug() << "InternalClass::changeMember()" << string->toQString() << hex << (uint)data.m_all; + data.resolve(); + uint idx = find(string); + if (index) + *index = idx; + + assert(idx != UINT_MAX); + + if (data == propertyData[idx]) + return this; + + uint tid = string->identifier | (data.flags() << 27); + + QHash<int, InternalClass *>::const_iterator tit = transitions.constFind(tid); + if (tit != transitions.constEnd()) + return tit.value(); + + // create a new class and add it to the tree + InternalClass *newClass = new InternalClass(*this); + newClass->propertyData[idx] = data; + return newClass; + +} + InternalClass *InternalClass::addMember(String *string, PropertyAttributes data, uint *index) { +// qDebug() << "InternalClass::addMember()" << string->toQString() << size << hex << (uint)data.m_all << data.type(); + data.resolve(); engine->identifierCache->toIdentifier(string); - uint id = string->identifier | (data.flags() << 24); + uint id = string->identifier | (data.flags() << 27); assert(propertyTable.constFind(id) == propertyTable.constEnd()); @@ -69,18 +100,17 @@ InternalClass *InternalClass::addMember(String *string, PropertyAttributes data, if (index) *index = size; - if (tit != transitions.constEnd()) { + if (tit != transitions.constEnd()) return tit.value(); - } else { - // create a new class and add it to the tree - InternalClass *newClass = new InternalClass(*this); - newClass->propertyTable.insert(string->identifier, size); - newClass->nameMap.append(string); - newClass->propertyData.append(data); - ++newClass->size; - transitions.insert(id, newClass); - return newClass; - } + + // create a new class and add it to the tree + InternalClass *newClass = new InternalClass(*this); + newClass->propertyTable.insert(string->identifier, size); + newClass->nameMap.append(string); + newClass->propertyData.append(data); + ++newClass->size; + transitions.insert(id, newClass); + return newClass; } void InternalClass::removeMember(Object *object, uint id) @@ -120,6 +150,39 @@ uint InternalClass::find(String *string) return UINT_MAX; } +InternalClass *InternalClass::sealed() +{ + if (m_sealed) + return m_sealed; + + m_sealed = engine->emptyClass; + for (int i = 0; i < nameMap.size(); ++i) { + PropertyAttributes attrs = propertyData.at(i); + attrs.setConfigurable(false); + m_sealed = m_sealed->addMember(nameMap.at(i), attrs); + } + + m_sealed->m_sealed = m_sealed; + return m_sealed; +} + +InternalClass *InternalClass::frozen() +{ + if (m_frozen) + return m_frozen; + + m_frozen = engine->emptyClass; + for (int i = 0; i < nameMap.size(); ++i) { + PropertyAttributes attrs = propertyData.at(i); + attrs.setWritable(false); + attrs.setConfigurable(false); + m_frozen = m_frozen->addMember(nameMap.at(i), attrs); + } + + m_frozen->m_frozen = m_frozen; + return m_frozen; +} + } } diff --git a/src/v4/qv4internalclass.h b/src/v4/qv4internalclass.h index 92bf19a741..cc3b03190b 100644 --- a/src/v4/qv4internalclass.h +++ b/src/v4/qv4internalclass.h @@ -62,14 +62,22 @@ struct InternalClass { QVector<PropertyAttributes> propertyData; QHash<int, InternalClass *> transitions; // id to next class, positive means add, negative delete + + InternalClass *m_sealed; + InternalClass *m_frozen; + uint size; - InternalClass(ExecutionEngine *engine) : engine(engine), size(0) {} + InternalClass(ExecutionEngine *engine) : engine(engine), m_sealed(0), m_frozen(0), size(0) {} InternalClass *addMember(String *string, PropertyAttributes data, uint *index = 0); + InternalClass *changeMember(String *string, PropertyAttributes data, uint *index = 0); void removeMember(Object *object, uint id); uint find(String *s); + InternalClass *sealed(); + InternalClass *frozen(); + private: InternalClass(const InternalClass &other); }; diff --git a/src/v4/qv4isel_util_p.h b/src/v4/qv4isel_util_p.h index 0960e17f51..e10a9658f2 100644 --- a/src/v4/qv4isel_util_p.h +++ b/src/v4/qv4isel_util_p.h @@ -47,19 +47,11 @@ namespace QQmlJS { -inline VM::Value nonExistantValue() -{ - VM::Value v; - v.tag = VM::Value::Undefined_Type; - v.uint_32 = UINT_MAX; - return v; -} - inline VM::Value convertToValue(V4IR::Const *c) { switch (c->type) { case V4IR::MissingType: - return nonExistantValue(); + return VM::Value::deletedValue(); case V4IR::NullType: return VM::Value::nullValue(); case V4IR::UndefinedType: diff --git a/src/v4/qv4jsir.cpp b/src/v4/qv4jsir.cpp index 88f566861d..ddf6509f27 100644 --- a/src/v4/qv4jsir.cpp +++ b/src/v4/qv4jsir.cpp @@ -278,6 +278,9 @@ void Const::dump(QTextStream &out) case QQmlJS::V4IR::BoolType: out << (value ? "true" : "false"); break; + case QQmlJS::V4IR::MissingType: + out << "missing"; + break; default: out << QString::number(value, 'g', 16); break; diff --git a/src/v4/qv4jsonobject.cpp b/src/v4/qv4jsonobject.cpp index d3a71acff9..22cd8ea8d5 100644 --- a/src/v4/qv4jsonobject.cpp +++ b/src/v4/qv4jsonobject.cpp @@ -281,8 +281,7 @@ bool Parser::parseMember(Object *o) if (!parseValue(&val)) return false; - PropertyDescriptor *p = o->insertMember(context->engine->newIdentifier(key), Attr_Data); - p->attrs = Attr_Data; + Property *p = o->insertMember(context->engine->newIdentifier(key), Attr_Data); p->value = val; END; @@ -782,10 +781,11 @@ QString Stringify::JO(Object *o) while (1) { String *name; uint index; - PropertyDescriptor *pd = it.next(&name, &index); + PropertyAttributes attrs; + Property *pd = it.next(&name, &index, &attrs); if (!pd) break; - Value v = o->getValueChecked(ctx, pd); + Value v = o->getValueChecked(ctx, pd, attrs); QString key; if (name) key = name->toQString(); diff --git a/src/v4/qv4object.cpp b/src/v4/qv4object.cpp index 105af27517..45ce45e595 100644 --- a/src/v4/qv4object.cpp +++ b/src/v4/qv4object.cpp @@ -70,7 +70,7 @@ Object::Object(ExecutionEngine *engine) : prototype(0) , internalClass(engine->emptyClass) , memberDataAlloc(0), memberData(0) - , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayData(0), sparseArray(0) + , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0) , externalResource(0) { vtbl = &static_vtbl; @@ -81,7 +81,7 @@ Object::Object(ExecutionContext *context) : prototype(0) , internalClass(context->engine->emptyClass) , memberDataAlloc(0), memberData(0) - , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayData(0), sparseArray(0) + , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), arrayAttributes(0), arrayData(0), sparseArray(0) , externalResource(0) { vtbl = &static_vtbl; @@ -93,6 +93,8 @@ Object::~Object() delete externalResource; delete [] memberData; delete [] (arrayData - (sparseArray ? 0 : arrayOffset)); + if (arrayAttributes) + delete [] (arrayAttributes - (sparseArray ? 0 : arrayOffset)); delete sparseArray; _data = 0; } @@ -107,54 +109,30 @@ void Object::put(ExecutionContext *ctx, const QString &name, const Value &value) put(ctx, ctx->engine->newString(name), value); } -Value Object::getValue(const Value &thisObject, ExecutionContext *ctx, const PropertyDescriptor *p) +Value Object::getValue(const Value &thisObject, ExecutionContext *ctx, const Property *p, PropertyAttributes attrs) { - assert(p->attrs.type() != PropertyAttributes::Generic); - if (p->attrs.isData()) + if (!attrs.isAccessor()) return p->value; - if (!p->get) + FunctionObject *getter = p->getter(); + if (!getter) return Value::undefinedValue(); - return p->get->call(ctx, thisObject, 0, 0); + return getter->call(ctx, thisObject, 0, 0); } -void Object::putValue(ExecutionContext *ctx, PropertyDescriptor *pd, const Value &value) +void Object::putValue(ExecutionContext *ctx, Property *pd, PropertyAttributes attrs, const Value &value) { - if (pd->attrs.isAccessor()) { - if (pd->set) { - Value args[1]; - args[0] = value; - pd->set->call(ctx, Value::fromObject(this), args, 1); - return; - } - goto reject; - } - - if (!pd->attrs.isWritable()) + if (attrs.isAccessor()) { + if (pd->set) { + Value args[1]; + args[0] = value; + pd->set->call(ctx, Value::fromObject(this), args, 1); + return; + } goto reject; - - pd->value = value; - return; - - reject: - if (ctx->strictMode) - ctx->throwTypeError(); - -} - -void Object::putValue(ExecutionContext *ctx, PropertyDescriptor *pd, const Value &thisObject, const Value &value) -{ - if (pd->attrs.isAccessor()) { - if (pd->set) { - Value args[1]; - args[0] = value; - pd->set->call(ctx, thisObject, args, 1); - return; - } - goto reject; } - if (!pd->attrs.isWritable()) + if (!attrs.isWritable()) goto reject; pd->value = value; @@ -192,8 +170,7 @@ void Object::inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value &index, c void Object::defineDefaultProperty(String *name, Value value) { - PropertyDescriptor *pd = insertMember(name, Attr_Data|Attr_NotEnumerable); - pd->attrs = Attr_Data|Attr_NotEnumerable; + Property *pd = insertMember(name, Attr_Data|Attr_NotEnumerable); pd->value = value; } @@ -218,8 +195,7 @@ void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name void Object::defineReadonlyProperty(String *name, Value value) { - PropertyDescriptor *pd = insertMember(name, Attr_ReadOnly); - pd->attrs = Attr_ReadOnly; + Property *pd = insertMember(name, Attr_ReadOnly); pd->value = value; } @@ -230,29 +206,29 @@ void Object::markObjects(Managed *that) o->prototype->mark(); for (int i = 0; i < o->internalClass->size; ++i) { - const PropertyDescriptor &pd = o->memberData[i]; - if (pd.attrs.isData()) { + const Property &pd = o->memberData[i]; + if (o->internalClass->propertyData[i].isData()) { if (Managed *m = pd.value.asManaged()) m->mark(); - } else if (pd.attrs.isAccessor()) { - if (pd.get) - pd.get->mark(); - if (pd.set) - pd.set->mark(); + } else { + if (pd.getter()) + pd.getter()->mark(); + if (pd.setter()) + pd.setter()->mark(); } } o->markArrayObjects(); } -PropertyDescriptor *Object::insertMember(String *s, PropertyAttributes attributes) +Property *Object::insertMember(String *s, PropertyAttributes attributes) { uint idx; internalClass = internalClass->addMember(s, attributes, &idx); if (idx >= memberDataAlloc) { memberDataAlloc = qMax((uint)8, 2*memberDataAlloc); - PropertyDescriptor *newMemberData = new PropertyDescriptor[memberDataAlloc]; - memcpy(newMemberData, memberData, sizeof(PropertyDescriptor)*idx); + Property *newMemberData = new Property[memberDataAlloc]; + memcpy(newMemberData, memberData, sizeof(Property)*idx); delete [] memberData; memberData = newMemberData; } @@ -260,32 +236,52 @@ PropertyDescriptor *Object::insertMember(String *s, PropertyAttributes attribute } // Section 8.12.1 -PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, String *name) +Property *Object::__getOwnProperty__(ExecutionContext *ctx, String *name, PropertyAttributes *attrs) { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) - return __getOwnProperty__(ctx, idx); + return __getOwnProperty__(ctx, idx, attrs); uint member = internalClass->find(name); - if (member < UINT_MAX) + if (member < UINT_MAX) { + if (attrs) + *attrs = internalClass->propertyData[member]; return memberData + member; + } + if (attrs) + *attrs = Attr_Invalid; return 0; } -PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, uint index) +Property *Object::__getOwnProperty__(ExecutionContext *ctx, uint index, PropertyAttributes *attrs) { - PropertyDescriptor *p = arrayAt(index); - if (p && p->attrs.type() != PropertyAttributes::Generic) - return p; - if (isStringObject()) + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + Property *p = arrayData + pidx; + if (!arrayAttributes || arrayAttributes[pidx].isData()) { + if (attrs) + *attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + return p; + } else if (arrayAttributes[pidx].isAccessor()) { + if (attrs) + *attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Accessor); + return p; + } + } + if (isStringObject()) { + if (attrs) + *attrs = Attr_NotConfigurable|Attr_NotWritable; return static_cast<StringObject *>(this)->getIndex(ctx, index); + } + if (attrs) + *attrs = Attr_Invalid; return 0; } // Section 8.12.2 -PropertyDescriptor *Object::__getPropertyDescriptor__(const ExecutionContext *ctx, String *name) const +Property *Object::__getPropertyDescriptor__(const ExecutionContext *ctx, String *name, PropertyAttributes *attrs) const { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) @@ -295,28 +291,44 @@ PropertyDescriptor *Object::__getPropertyDescriptor__(const ExecutionContext *ct const Object *o = this; while (o) { uint idx = o->internalClass->find(name); - if (idx < UINT_MAX) + 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; } -PropertyDescriptor *Object::__getPropertyDescriptor__(const ExecutionContext *ctx, uint index) const +Property *Object::__getPropertyDescriptor__(const ExecutionContext *ctx, uint index, PropertyAttributes *attrs) const { const Object *o = this; while (o) { - PropertyDescriptor *p = o->arrayAt(index); - if (p && p->attrs.type() != PropertyAttributes::Generic) - return p; + uint pidx = o->propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + Property *p = o->arrayData + pidx; + if (!o->arrayAttributes || !o->arrayAttributes[pidx].isGeneric()) { + if (attrs) + *attrs = o->arrayAttributes ? o->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + return p; + } + } if (o->isStringObject()) { - p = static_cast<const StringObject *>(o)->getIndex(ctx, index); - if (p) + Property *p = static_cast<const StringObject *>(o)->getIndex(ctx, index); + if (p) { + if (attrs) + *attrs = (Attr_NotWritable|Attr_NotConfigurable); return p; + } } o = o->prototype; } + if (attrs) + *attrs = Attr_Invalid; return 0; } @@ -342,20 +354,39 @@ void Object::putIndexed(Managed *m, ExecutionContext *ctx, uint index, const Val PropertyAttributes Object::query(Managed *m, ExecutionContext *ctx, String *name) { - Object *that = static_cast<Object *>(m); - PropertyDescriptor *pd = that->__getPropertyDescriptor__(ctx, name); - if (!pd || pd->attrs.type() == PropertyAttributes::Generic) - return Attr_Invalid; - return pd->attrs; + uint idx = name->asArrayIndex(); + if (idx != UINT_MAX) + return queryIndexed(m, ctx, idx); + + const Object *o = static_cast<Object *>(m); + while (o) { + uint idx = o->internalClass->find(name); + if (idx < UINT_MAX) + return o->internalClass->propertyData[idx]; + + o = o->prototype; + } + return Attr_Invalid; } PropertyAttributes Object::queryIndexed(Managed *m, ExecutionContext *ctx, uint index) { - Object *that = static_cast<Object *>(m); - PropertyDescriptor *pd = that->__getPropertyDescriptor__(ctx, index); - if (!pd || pd->attrs.type() == PropertyAttributes::Generic) - return Attr_Invalid; - return pd->attrs; + const Object *o = static_cast<Object *>(m); + while (o) { + uint pidx = o->propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + if (o->arrayAttributes) + return o->arrayAttributes[pidx]; + return Attr_Data; + } + if (o->isStringObject()) { + Property *p = static_cast<const StringObject *>(o)->getIndex(ctx, index); + if (p) + return Attr_Data; + } + o = o->prototype; + } + return Attr_Invalid; } bool Object::deleteProperty(Managed *m, ExecutionContext *ctx, String *name) @@ -390,7 +421,7 @@ Value Object::internalGet(ExecutionContext *ctx, String *name, bool *hasProperty if (idx < UINT_MAX) { if (hasProperty) *hasProperty = true; - return getValue(ctx, o->memberData + idx); + return getValue(ctx, o->memberData + idx, o->internalClass->propertyData.at(idx)); } o = o->prototype; @@ -403,18 +434,23 @@ Value Object::internalGet(ExecutionContext *ctx, String *name, bool *hasProperty Value Object::internalGetIndexed(ExecutionContext *ctx, uint index, bool *hasProperty) { - PropertyDescriptor *pd = 0; + Property *pd = 0; + PropertyAttributes attrs = Attr_Data; Object *o = this; while (o) { - PropertyDescriptor *p = o->arrayAt(index); - if (p && p->attrs.type() != PropertyAttributes::Generic) { - pd = p; - break; + uint pidx = o->propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + if (!o->arrayAttributes || !o->arrayAttributes[pidx].isGeneric()) { + pd = o->arrayData + pidx; + if (o->arrayAttributes) + attrs = o->arrayAttributes[pidx]; + break; + } } if (o->isStringObject()) { - p = static_cast<StringObject *>(o)->getIndex(ctx, index); - if (p) { - pd = p; + pd = static_cast<StringObject *>(o)->getIndex(ctx, index); + if (pd) { + attrs = (Attr_NotWritable|Attr_NotConfigurable); break; } } @@ -424,7 +460,7 @@ Value Object::internalGetIndexed(ExecutionContext *ctx, uint index, bool *hasPro if (pd) { if (hasProperty) *hasProperty = true; - return getValue(ctx, pd); + return getValue(ctx, pd, attrs); } if (hasProperty) @@ -443,15 +479,20 @@ void Object::internalPut(ExecutionContext *ctx, String *name, const Value &value name->makeIdentifier(ctx); uint member = internalClass->find(name); - PropertyDescriptor *pd = (member < UINT_MAX) ? memberData + member : 0; + Property *pd = 0; + PropertyAttributes attrs; + if (member < UINT_MAX) { + pd = memberData + member; + attrs = internalClass->propertyData[member]; + } // clause 1 if (pd) { - if (pd->attrs.isAccessor()) { - if (pd->set) - goto cont; - goto reject; - } else if (!pd->attrs.isWritable()) + if (attrs.isAccessor()) { + if (pd->setter()) + goto cont; + goto reject; + } else if (!attrs.isWritable()) goto reject; else if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { bool ok; @@ -469,15 +510,16 @@ void Object::internalPut(ExecutionContext *ctx, String *name, const Value &value if (!extensible) goto reject; } else { - if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, name)) { - if (p->attrs.isAccessor()) { - if (p->set) + PropertyAttributes attrs; + if (Property *p = prototype->__getPropertyDescriptor__(ctx, name, &attrs)) { + if (attrs.isAccessor()) { + if (p->setter()) goto cont; goto reject; } if (!extensible) goto reject; - if (!p->attrs.isWritable()) + if (!attrs.isWritable()) goto reject; } else { if (!extensible) @@ -489,22 +531,22 @@ void Object::internalPut(ExecutionContext *ctx, String *name, const Value &value // clause 4 + // ### should be able to remove these two lines (see call 15 lines above) if (!pd && prototype) - pd = prototype->__getPropertyDescriptor__(ctx, name); + pd = prototype->__getPropertyDescriptor__(ctx, name, &attrs); // Clause 5 - if (pd && pd->attrs.isAccessor()) { - assert(pd->set != 0); + if (pd && attrs.isAccessor()) { + assert(pd->setter() != 0); Value args[1]; args[0] = value; - pd->set->call(ctx, Value::fromObject(this), args, 1); + pd->setter()->call(ctx, Value::fromObject(this), args, 1); return; } { - PropertyDescriptor *p = insertMember(name, Attr_Data); - p->attrs = Attr_Data; + Property *p = insertMember(name, Attr_Data); p->value = value; return; } @@ -516,19 +558,33 @@ void Object::internalPut(ExecutionContext *ctx, String *name, const Value &value void Object::internalPutIndexed(ExecutionContext *ctx, uint index, const Value &value) { - PropertyDescriptor *pd = arrayAt(index); - if (pd && pd->attrs.type() == PropertyAttributes::Generic) - pd = 0; - if (!pd && isStringObject()) + Property *pd = 0; + PropertyAttributes attrs; + + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX) { + if (arrayAttributes && arrayAttributes[pidx].isGeneric()) { + pidx = UINT_MAX; + } else { + pd = arrayData + pidx; + attrs = arrayAttributes ? arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + } + } + + if (!pd && isStringObject()) { pd = static_cast<StringObject *>(this)->getIndex(ctx, index); + if (pd) + // not writable + goto reject; + } // clause 1 if (pd) { - if (pd->attrs.isAccessor()) { - if (pd->set) - goto cont; - goto reject; - } else if (!pd->attrs.isWritable()) + if (attrs.isAccessor()) { + if (pd->setter()) + goto cont; + goto reject; + } else if (!attrs.isWritable()) goto reject; else pd->value = value; @@ -537,15 +593,16 @@ void Object::internalPutIndexed(ExecutionContext *ctx, uint index, const Value & if (!extensible) goto reject; } else { - if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, index)) { - if (p->attrs.isAccessor()) { - if (p->set) + PropertyAttributes attrs; + if (Property *p = prototype->__getPropertyDescriptor__(ctx, index, &attrs)) { + if (attrs.isAccessor()) { + if (p->setter()) goto cont; goto reject; } if (!extensible) goto reject; - if (!p->attrs.isWritable()) + if (!attrs.isWritable()) goto reject; } else { if (!extensible) @@ -556,16 +613,17 @@ void Object::internalPutIndexed(ExecutionContext *ctx, uint index, const Value & cont: // clause 4 + // ### remove and replace with 15 lines above... if (!pd && prototype) - pd = prototype->__getPropertyDescriptor__(ctx, index); + pd = prototype->__getPropertyDescriptor__(ctx, index, &attrs); // Clause 5 - if (pd && pd->attrs.isAccessor()) { - assert(pd->set != 0); + if (pd && attrs.isAccessor()) { + assert(pd->setter() != 0); Value args[1]; args[0] = value; - pd->set->call(ctx, Value::fromObject(this), args, 1); + pd->setter()->call(ctx, Value::fromObject(this), args, 1); return; } @@ -588,10 +646,9 @@ bool Object::internalDeleteProperty(ExecutionContext *ctx, String *name) uint memberIdx = internalClass->find(name); if (memberIdx != UINT_MAX) { - PropertyDescriptor &pd = memberData[memberIdx]; - if (pd.attrs.isConfigurable()) { + if (internalClass->propertyData[memberIdx].isConfigurable()) { internalClass->removeMember(this, name->identifier); - memmove(memberData + memberIdx, memberData + memberIdx + 1, (internalClass->size - memberIdx)*sizeof(PropertyDescriptor)); + memmove(memberData + memberIdx, memberData + memberIdx + 1, (internalClass->size - memberIdx)*sizeof(Property)); return true; } if (ctx->strictMode) @@ -604,25 +661,20 @@ bool Object::internalDeleteProperty(ExecutionContext *ctx, String *name) bool Object::internalDeleteIndexedProperty(ExecutionContext *ctx, uint index) { - PropertyDescriptor *pd = 0; - if (!sparseArray) { - if (index >= arrayDataLen) - return true; - pd = arrayAt(index); - } else { - SparseArrayNode *n = sparseArray->findNode(index); - if (n) - pd = arrayDecriptor(n->value); - } - if (!pd || pd->attrs.type() == PropertyAttributes::Generic) + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx == UINT_MAX) + return true; + if (arrayAttributes && arrayAttributes[pidx].isGeneric()) return true; - if (pd->attrs.isConfigurable()) { - pd->attrs.clear(); - pd->value = Value::undefinedValue(); + if (!arrayAttributes || arrayAttributes[pidx].isConfigurable()) { + arrayData[pidx].value = Value::undefinedValue(); + if (!arrayAttributes) + ensureArrayAttributes(); + arrayAttributes[pidx].clear(); if (sparseArray) { - pd->value.int_32 = arrayFreeList; - arrayFreeList = pd - arrayData; + arrayData[pidx].value.int_32 = arrayFreeList; + arrayFreeList = pidx; } return true; } @@ -633,33 +685,35 @@ bool Object::internalDeleteIndexedProperty(ExecutionContext *ctx, uint index) } // Section 8.12.9 -bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc) +bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Property &p, PropertyAttributes attrs) { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) - return __defineOwnProperty__(ctx, idx, desc); + return __defineOwnProperty__(ctx, idx, p, attrs); name->makeIdentifier(ctx); - PropertyDescriptor *current; + Property *current; + PropertyAttributes *cattrs; if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) { - PropertyDescriptor *lp = memberData + ArrayObject::LengthPropertyIndex; - assert(0 == internalClass->find(ctx->engine->id_length)); - if (desc->attrs.isEmpty() || desc->isSubset(desc->attrs, lp)) + assert(ArrayObject::LengthPropertyIndex == internalClass->find(ctx->engine->id_length)); + Property *lp = memberData + ArrayObject::LengthPropertyIndex; + cattrs = internalClass->propertyData.data() + ArrayObject::LengthPropertyIndex; + if (attrs.isEmpty() || p.isSubset(attrs, *lp, *cattrs)) return true; - if (!lp->attrs.isWritable() || desc->attrs.type() == PropertyAttributes::Accessor || desc->attrs.isConfigurable() || desc->attrs.isEnumerable()) + if (!cattrs->isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable()) goto reject; bool succeeded = true; - if (desc->attrs.type() == PropertyAttributes::Data) { + if (attrs.type() == PropertyAttributes::Data) { bool ok; - uint l = desc->value.asArrayLength(ctx, &ok); + uint l = p.value.asArrayLength(ctx, &ok); if (!ok) - ctx->throwRangeError(desc->value); + ctx->throwRangeError(p.value); succeeded = setArrayLength(l); } - if (desc->attrs.hasWritable() && !desc->attrs.isWritable()) - lp->attrs.setWritable(false); + if (attrs.hasWritable() && !attrs.isWritable()) + cattrs->setWritable(false); if (!succeeded) goto reject; return true; @@ -669,6 +723,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Pr { uint member = internalClass->find(name); current = (member < UINT_MAX) ? memberData + member : 0; + cattrs = internalClass->propertyData.data() + member; } if (!current) { @@ -676,112 +731,128 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Pr if (!extensible) goto reject; // clause 4 - PropertyDescriptor *pd = insertMember(name, desc->attrs); - *pd = *desc; - pd->fullyPopulated(&pd->attrs); + Property *pd = insertMember(name, attrs); + *pd = p; + pd->fullyPopulated(&attrs); return true; } - return __defineOwnProperty__(ctx, current, desc); + return __defineOwnProperty__(ctx, current, name, p, attrs); reject: if (ctx->strictMode) ctx->throwTypeError(); return false; } -bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc) +bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs) { - PropertyDescriptor *current; + Property *current = 0; // 15.4.5.1, 4b - if (isArrayObject() && index >= arrayLength() && !memberData[ArrayObject::LengthPropertyIndex].attrs.isWritable()) + if (isArrayObject() && index >= arrayLength() && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) goto reject; if (isNonStrictArgumentsObject) - return static_cast<ArgumentsObject *>(this)->defineOwnProperty(ctx, index, desc); + return static_cast<ArgumentsObject *>(this)->defineOwnProperty(ctx, index, p, attrs); // Clause 1 - current = arrayAt(index); - if (current && current->attrs.type() == PropertyAttributes::Generic) - current = 0; - if (!current && isStringObject()) - current = static_cast<StringObject *>(this)->getIndex(ctx, index); + { + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx < UINT_MAX && (!arrayAttributes || !arrayAttributes[pidx].isGeneric())) + current = arrayData + pidx; + if (!current && isStringObject()) + current = static_cast<StringObject *>(this)->getIndex(ctx, index); + } if (!current) { // clause 3 if (!extensible) goto reject; // clause 4 - PropertyDescriptor *pd = arrayInsert(index); - *pd = *desc; - pd->fullyPopulated(&pd->attrs); + Property *pd = arrayInsert(index, attrs); + *pd = p; + pd->fullyPopulated(&attrs); return true; } - return __defineOwnProperty__(ctx, current, desc); + return __defineOwnProperty__(ctx, current, 0 /*member*/, p, attrs); reject: if (ctx->strictMode) ctx->throwTypeError(); return false; } -bool Object::__defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc) +bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, String *member, const Property &p, PropertyAttributes attrs) { // clause 5 - if (desc->attrs.isEmpty()) + if (attrs.isEmpty()) return true; + PropertyAttributes cattrs = Attr_Data; + if (member) + cattrs = internalClass->propertyData[current - memberData]; + else if (arrayAttributes) + cattrs = arrayAttributes[current - arrayData]; + // clause 6 - if (desc->isSubset(desc->attrs, current)) + if (p.isSubset(attrs, *current, cattrs)) return true; // clause 7 - if (!current->attrs.isConfigurable()) { - if (desc->attrs.isConfigurable()) + if (!cattrs.isConfigurable()) { + if (attrs.isConfigurable()) goto reject; - if (desc->attrs.hasEnumerable() && desc->attrs.isEnumerable() != current->attrs.isEnumerable()) + if (attrs.hasEnumerable() && attrs.isEnumerable() != cattrs.isEnumerable()) goto reject; } // clause 8 - if (desc->attrs.isGeneric()) + if (attrs.isGeneric()) goto accept; // clause 9 - if (current->attrs.isData() != desc->attrs.isData()) { + if (cattrs.isData() != attrs.isData()) { // 9a - if (!current->attrs.isConfigurable()) + if (!cattrs.isConfigurable()) goto reject; - if (current->attrs.isData()) { + if (cattrs.isData()) { // 9b - current->attrs.setType(PropertyAttributes::Accessor); - current->attrs.clearWritable(); - current->get = 0; - current->set = 0; + cattrs.setType(PropertyAttributes::Accessor); + cattrs.clearWritable(); + current->setGetter(0); + current->setSetter(0); } else { // 9c - current->attrs.setType(PropertyAttributes::Data); - current->attrs.setWritable(false); + cattrs.setType(PropertyAttributes::Data); + cattrs.setWritable(false); current->value = Value::undefinedValue(); } - } else if (current->attrs.isData() && desc->attrs.isData()) { // clause 10 - if (!current->attrs.isConfigurable() && !current->attrs.isWritable()) { - if (desc->attrs.isWritable() || !current->value.sameValue(desc->value)) + } 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(current->attrs.isAccessor() && desc->attrs.isAccessor()); - if (!current->attrs.isConfigurable()) { - if (desc->get && !(current->get == desc->get || (!current->get && (quintptr)desc->get == 0x1))) + assert(cattrs.isAccessor() && attrs.isAccessor()); + if (!cattrs.isConfigurable()) { + if (p.getter() && !(current->getter() == p.getter() || (!current->getter() && (quintptr)p.getter() == 0x1))) goto reject; - if (desc->set && !(current->set == desc->set || (!current->set && (quintptr)desc->set == 0x1))) + if (p.setter() && !(current->setter() == p.setter() || (!current->setter() && (quintptr)p.setter() == 0x1))) goto reject; } } accept: - current->merge(current->attrs, *desc); + current->merge(cattrs, p, attrs); + if (member) { + internalClass = internalClass->changeMember(member, cattrs); + } else { + if (cattrs != Attr_Data) + ensureArrayAttributes(); + if (arrayAttributes) + arrayAttributes[current - arrayData] = cattrs; + } return true; reject: if (ctx->strictMode) @@ -790,9 +861,9 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *cu } -bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc) +bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, const Property &p, PropertyAttributes attrs) { - return __defineOwnProperty__(ctx, ctx->engine->newString(name), desc); + return __defineOwnProperty__(ctx, ctx->engine->newString(name), p, attrs); } @@ -800,7 +871,7 @@ void Object::copyArrayData(Object *other) { arrayReserve(other->arrayDataLen); arrayDataLen = other->arrayDataLen; - memcpy(arrayData, other->arrayData, arrayDataLen*sizeof(PropertyDescriptor)); + memcpy(arrayData, other->arrayData, arrayDataLen*sizeof(Property)); arrayOffset = 0; if (other->sparseArray) { sparseArray = new SparseArray(*other->sparseArray); @@ -819,7 +890,7 @@ Value Object::arrayIndexOf(Value v, uint fromIndex, uint endIndex, ExecutionCont if (p->arrayDataLen) protoHasArray = true; - if (protoHasArray) { + if (protoHasArray || o->arrayAttributes) { // lets be safe and slow for (uint i = fromIndex; i < endIndex; ++i) { bool exists; @@ -830,19 +901,19 @@ Value Object::arrayIndexOf(Value v, uint fromIndex, uint endIndex, ExecutionCont } else if (sparseArray) { for (SparseArrayNode *n = sparseArray->lowerBound(fromIndex); n != sparseArray->end() && n->key() < endIndex; n = n->nextNode()) { bool exists; - Value value = o->getValueChecked(ctx, arrayDecriptor(n->value), &exists); + Value value = o->getValueChecked(ctx, arrayData + n->value, arrayAttributes ? arrayAttributes[n->value] : Attr_Data, &exists); if (exists && __qmljs_strict_equal(value, v, ctx)) return Value::fromDouble(n->key()); } } else { if ((int) endIndex > arrayDataLen) endIndex = arrayDataLen; - PropertyDescriptor *pd = arrayData; - PropertyDescriptor *end = pd + endIndex; + Property *pd = arrayData; + Property *end = pd + endIndex; pd += fromIndex; while (pd < end) { bool exists; - Value value = o->getValueChecked(ctx, pd, &exists); + Value value = o->getValueChecked(ctx, pd, arrayAttributes ? arrayAttributes[pd - arrayData] : Attr_Data, &exists); if (exists && __qmljs_strict_equal(value, v, ctx)) return Value::fromDouble(pd - arrayData); ++pd; @@ -856,14 +927,17 @@ void Object::arrayConcat(const ArrayObject *other) int newLen = arrayDataLen + other->arrayLength(); if (other->sparseArray) initSparse(); + // ### copy attributes as well! if (sparseArray) { if (other->sparseArray) { for (const SparseArrayNode *it = other->sparseArray->begin(); it != other->sparseArray->end(); it = it->nextNode()) - arraySet(arrayDataLen + it->key(), other->arrayDecriptor(it->value)); + arraySet(arrayDataLen + it->key(), other->arrayData + it->value); } else { int oldSize = arrayDataLen; arrayReserve(oldSize + other->arrayLength()); - memcpy(arrayData + oldSize, other->arrayData, other->arrayLength()*sizeof(PropertyDescriptor)); + memcpy(arrayData + oldSize, other->arrayData, other->arrayLength()*sizeof(Property)); + if (arrayAttributes) + std::fill(arrayAttributes + oldSize, arrayAttributes + oldSize + other->arrayLength(), PropertyAttributes(Attr_Data)); for (uint i = 0; i < other->arrayLength(); ++i) { SparseArrayNode *n = sparseArray->insert(arrayDataLen + i); n->value = oldSize + i; @@ -873,13 +947,26 @@ void Object::arrayConcat(const ArrayObject *other) int oldSize = arrayLength(); arrayReserve(oldSize + other->arrayDataLen); if (oldSize > arrayDataLen) { - PropertyDescriptor generic; - generic.attrs.clear(); - generic.value = Value::undefinedValue(); - std::fill(arrayData + arrayDataLen, arrayData + oldSize, generic); + ensureArrayAttributes(); + std::fill(arrayAttributes + arrayDataLen, arrayAttributes + oldSize, PropertyAttributes()); } arrayDataLen = oldSize + other->arrayDataLen; - memcpy(arrayData + oldSize, other->arrayData, other->arrayDataLen*sizeof(PropertyDescriptor)); + if (other->arrayAttributes) { + for (int i = 0; i < arrayDataLen; ++i) { + bool exists; + arrayData[oldSize + i].value = const_cast<ArrayObject *>(other)->getIndexed(internalClass->engine->current, i, &exists); + if (arrayAttributes) + arrayAttributes[oldSize + i] = Attr_Data; + if (!exists) { + ensureArrayAttributes(); + arrayAttributes[oldSize + i].clear(); + } + } + } else { + memcpy(arrayData + oldSize, other->arrayData, other->arrayDataLen*sizeof(Property)); + if (arrayAttributes) + std::fill(arrayAttributes + oldSize, arrayAttributes + oldSize + other->arrayDataLen, PropertyAttributes(Attr_Data)); + } } setArrayLengthUnchecked(newLen); } @@ -892,14 +979,34 @@ void Object::arraySort(ExecutionContext *context, Object *thisObject, const Valu if (sparseArray) { context->throwUnimplemented("Object::sort unimplemented for sparse arrays"); return; - delete sparseArray; } - ArrayElementLessThan lessThan(context, thisObject, comparefn); if (len > arrayDataLen) len = arrayDataLen; - PropertyDescriptor *begin = arrayData; + // The spec says the sorting goes through a series of get,put and delete operations. + // this implies that the attributes don't get sorted around. + // behavior of accessor properties is implementation defined. We simply turn them all + // into data properties and then sort. This is in line with the sentence above. + if (arrayAttributes) { + for (uint i = 0; i < len; i++) { + if (arrayAttributes[i].isGeneric()) { + while (--len > i) + if (!arrayAttributes[len].isGeneric()) + break; + arrayData[i].value = getValue(context, arrayData + len, arrayAttributes[len]); + arrayAttributes[i] = Attr_Data; + arrayAttributes[len].clear(); + } else if (arrayAttributes[i].isAccessor()) { + arrayData[i].value = getValue(context, arrayData + i, arrayAttributes[i]); + arrayAttributes[i] = Attr_Data; + } + } + } + + ArrayElementLessThan lessThan(context, thisObject, comparefn); + + Property *begin = arrayData; std::sort(begin, begin + len, lessThan); } @@ -909,7 +1016,7 @@ void Object::initSparse() if (!sparseArray) { sparseArray = new SparseArray; for (int i = 0; i < arrayDataLen; ++i) { - if (arrayData[i].attrs.type() != PropertyAttributes::Generic) { + if (!arrayAttributes || !arrayAttributes[i].isGeneric()) { SparseArrayNode *n = sparseArray->insert(i); n->value = i + arrayOffset; } @@ -924,14 +1031,11 @@ void Object::initSparse() arrayAlloc += off; int o = off; for (int i = 0; i < o - 1; ++i) { - arrayData[i].attrs.clear(); arrayData[i].value = Value::fromInt32(i + 1); } - arrayData[o - 1].attrs.clear(); arrayData[o - 1].value = Value::fromInt32(arrayDataLen + off); } for (int i = arrayDataLen + off; i < arrayAlloc; ++i) { - arrayData[i].attrs.clear(); arrayData[i].value = Value::fromInt32(i + 1); } } @@ -952,28 +1056,52 @@ void Object::arrayReserve(uint n) off = arrayOffset; } arrayAlloc = qMax(n, 2*arrayAlloc); - PropertyDescriptor *newArrayData = new PropertyDescriptor[arrayAlloc]; + Property *newArrayData = new Property[arrayAlloc]; if (arrayData) { - memcpy(newArrayData, arrayData, sizeof(PropertyDescriptor)*arrayDataLen); + memcpy(newArrayData, arrayData, sizeof(Property)*arrayDataLen); delete [] (arrayData - off); } arrayData = newArrayData; if (sparseArray) { for (uint i = arrayFreeList; i < arrayAlloc; ++i) { - arrayData[i].attrs.clear(); + arrayData[i].value = Value::deletedValue(); arrayData[i].value = Value::fromInt32(i + 1); } } else { arrayOffset = 0; } + + if (arrayAttributes) { + PropertyAttributes *newAttrs = new PropertyAttributes[arrayAlloc]; + memcpy(newAttrs, arrayAttributes, sizeof(PropertyAttributes)*arrayDataLen); + delete [] (arrayAttributes - off); + + arrayAttributes = newAttrs; + if (sparseArray) { + for (uint i = arrayFreeList; i < arrayAlloc; ++i) + arrayAttributes[i] = Attr_Invalid; + } + } } } +void Object::ensureArrayAttributes() +{ + if (arrayAttributes) + return; + + arrayAttributes = new PropertyAttributes[arrayAlloc]; + for (uint i = 0; i < arrayDataLen; ++i) + arrayAttributes[i] = Attr_Data; + for (uint i = arrayDataLen; i < arrayAlloc; ++i) + arrayAttributes[i] = Attr_Invalid; +} + bool Object::setArrayLength(uint newLen) { assert(isArrayObject()); - const PropertyDescriptor *lengthProperty = memberData + ArrayObject::LengthPropertyIndex; - if (lengthProperty && !lengthProperty->attrs.isWritable()) + const Property *lengthProperty = memberData + ArrayObject::LengthPropertyIndex; + if (lengthProperty && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable()) return false; uint oldLen = arrayLength(); bool ok = true; @@ -983,14 +1111,17 @@ bool Object::setArrayLength(uint newLen) { if (begin != sparseArray->end()) { SparseArrayNode *it = sparseArray->end()->previousNode(); while (1) { - PropertyDescriptor &pd = arrayData[it->value]; - if (pd.attrs.type() != PropertyAttributes::Generic && !pd.attrs.isConfigurable()) { - ok = false; - newLen = it->key() + 1; - break; + Property &pd = arrayData[it->value]; + if (arrayAttributes) { + if (!arrayAttributes[it->value].isConfigurable()) { + ok = false; + newLen = it->key() + 1; + break; + } else { + arrayAttributes[it->value].clear(); + } } - pd.attrs.clear(); - pd.value.tag = Value::_Undefined_Type; + pd.value.tag = Value::_Deleted_Type; pd.value.int_32 = arrayFreeList; arrayFreeList = it->value; bool brk = (it == begin); @@ -1002,13 +1133,18 @@ bool Object::setArrayLength(uint newLen) { } } } else { - PropertyDescriptor *it = arrayData + arrayDataLen; - const PropertyDescriptor *begin = arrayData + newLen; + Property *it = arrayData + arrayDataLen; + const Property *begin = arrayData + newLen; while (--it >= begin) { - if (it->attrs.type() != PropertyAttributes::Generic && !it->attrs.isConfigurable()) { - ok = false; - newLen = it - arrayData + 1; - break; + if (arrayAttributes) { + if (!arrayAttributes[it - arrayData].isEmpty() && !arrayAttributes[it - arrayData].isConfigurable()) { + ok = false; + newLen = it - arrayData + 1; + break; + } else { + arrayAttributes[it - arrayData].clear(); + } + it->value = Value::deletedValue(); } } arrayDataLen = newLen; @@ -1024,15 +1160,15 @@ bool Object::setArrayLength(uint newLen) { void Object::markArrayObjects() const { for (uint i = 0; i < arrayDataLen; ++i) { - const PropertyDescriptor &pd = arrayData[i]; - if (pd.attrs.isData()) { + const Property &pd = arrayData[i]; + if (!arrayAttributes || arrayAttributes[i].isData()) { if (Managed *m = pd.value.asManaged()) m->mark(); - } else if (pd.attrs.isAccessor()) { - if (pd.get) - pd.get->mark(); - if (pd.set) - pd.set->mark(); + } else if (arrayAttributes[i].isAccessor()) { + if (pd.getter()) + pd.getter()->mark(); + if (pd.setter()) + pd.setter()->mark(); } } } @@ -1042,10 +1178,8 @@ void ArrayObject::init(ExecutionContext *context) type = Type_ArrayObject; internalClass = context->engine->arrayClass; - memberData = new PropertyDescriptor[4]; - PropertyDescriptor *pd = memberData + LengthPropertyIndex; - pd->attrs = Attr_NotEnumerable|Attr_NotConfigurable; - pd->value = Value::fromInt32(0); + memberData = new Property[4]; + memberData[LengthPropertyIndex].value = Value::fromInt32(0); } diff --git a/src/v4/qv4object.h b/src/v4/qv4object.h index 8c35a5a0cc..9919ce3ade 100644 --- a/src/v4/qv4object.h +++ b/src/v4/qv4object.h @@ -111,7 +111,7 @@ struct Q_V4_EXPORT Object: Managed { Object *prototype; InternalClass *internalClass; uint memberDataAlloc; - PropertyDescriptor *memberData; + Property *memberData; union { uint arrayFreeList; @@ -119,7 +119,8 @@ struct Q_V4_EXPORT Object: Managed { }; uint arrayDataLen; uint arrayAlloc; - PropertyDescriptor *arrayData; + PropertyAttributes *arrayAttributes; + Property *arrayData; SparseArray *sparseArray; ExternalResource *externalResource; @@ -127,50 +128,46 @@ struct Q_V4_EXPORT Object: Managed { Object(ExecutionContext *context); ~Object(); - PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); - PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, uint index); + Property *__getOwnProperty__(ExecutionContext *ctx, String *name, PropertyAttributes *attrs = 0); + Property *__getOwnProperty__(ExecutionContext *ctx, uint index, PropertyAttributes *attrs = 0); - PropertyDescriptor *__getPropertyDescriptor__(const ExecutionContext *ctx, String *name) const; - PropertyDescriptor *__getPropertyDescriptor__(const ExecutionContext *ctx, uint index) const; + Property *__getPropertyDescriptor__(const ExecutionContext *ctx, String *name, PropertyAttributes *attrs = 0) const; + Property *__getPropertyDescriptor__(const ExecutionContext *ctx, uint index, PropertyAttributes *attrs = 0) const; bool __hasProperty__(const ExecutionContext *ctx, String *name) const { - PropertyDescriptor *pd = __getPropertyDescriptor__(ctx, name); - return pd && pd->attrs.type() != PropertyAttributes::Generic; + return __getPropertyDescriptor__(ctx, name); } bool __hasProperty__(const ExecutionContext *ctx, uint index) const { - PropertyDescriptor *pd = __getPropertyDescriptor__(ctx, index); - return pd && pd->attrs.type() != PropertyAttributes::Generic; + return __getPropertyDescriptor__(ctx, index); } - bool __defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc); - bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc); - bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); - bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc); + bool __defineOwnProperty__(ExecutionContext *ctx, Property *current, String *member, const Property &p, PropertyAttributes attrs); + bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const Property &p, PropertyAttributes attrs); + bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs); + bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const Property &p, PropertyAttributes attrs); // // helpers // void put(ExecutionContext *ctx, const QString &name, const Value &value); - static Value getValue(const Value &thisObject, ExecutionContext *ctx, const PropertyDescriptor *p); - Value getValue(ExecutionContext *ctx, const PropertyDescriptor *p) const { - return getValue(Value::fromObject(const_cast<Object *>(this)), ctx, p); + static Value getValue(const Value &thisObject, ExecutionContext *ctx, const Property *p, PropertyAttributes attrs); + Value getValue(ExecutionContext *ctx, const Property *p, PropertyAttributes attrs) const { + return getValue(Value::fromObject(const_cast<Object *>(this)), ctx, p, attrs); } - Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p) const { - if (!p || p->attrs.type() == PropertyAttributes::Generic) + Value getValueChecked(ExecutionContext *ctx, const Property *p, PropertyAttributes attrs) const { + if (!p || attrs.isGeneric()) return Value::undefinedValue(); - return getValue(Value::fromObject(const_cast<Object *>(this)), ctx, p); + return getValue(Value::fromObject(const_cast<Object *>(this)), ctx, p, attrs); } - Value getValueChecked(ExecutionContext *ctx, const PropertyDescriptor *p, bool *exists) const { - *exists = p && p->attrs.type() != PropertyAttributes::Generic; + Value getValueChecked(ExecutionContext *ctx, const Property *p, PropertyAttributes attrs, bool *exists) const { + *exists = p && !attrs.isGeneric(); if (!*exists) return Value::undefinedValue(); - return getValue(Value::fromObject(const_cast<Object *>(this)), ctx, p); + return getValue(Value::fromObject(const_cast<Object *>(this)), ctx, p, attrs); } - - void putValue(ExecutionContext *ctx, PropertyDescriptor *pd, const Value &value); - void putValue(ExecutionContext *ctx, PropertyDescriptor *pd, const Value &thisObject, const Value &value); + void putValue(ExecutionContext *ctx, Property *pd, PropertyAttributes attrs, const Value &value); void inplaceBinOp(ExecutionContext *ctx, BinOp op, String *name, const Value &rhs); void inplaceBinOp(ExecutionContext *ctx, BinOp op, const Value &index, const Value &rhs); @@ -183,52 +180,48 @@ struct Q_V4_EXPORT Object: Managed { void defineReadonlyProperty(ExecutionEngine *engine, const QString &name, Value value); void defineReadonlyProperty(String *name, Value value); - PropertyDescriptor *insertMember(String *s, PropertyAttributes attributes); + Property *insertMember(String *s, PropertyAttributes attributes); // Array handling - static void fillDescriptor(PropertyDescriptor *pd, Value v) - { - pd->attrs = PropertyAttributes(Attr_Data); - pd->value = v; - } - uint allocArrayValue() { uint idx = arrayFreeList; if (arrayAlloc <= arrayFreeList) arrayReserve(arrayAlloc + 1); - arrayFreeList = arrayData[arrayFreeList].value.integerValue(); + arrayFreeList = arrayData[arrayFreeList].value.uint_32; + if (arrayAttributes) + arrayAttributes[idx].setType(PropertyAttributes::Data); return idx; } uint allocArrayValue(Value v) { uint idx = allocArrayValue(); - PropertyDescriptor *pd = &arrayData[idx]; - fillDescriptor(pd, v); + Property *pd = &arrayData[idx]; + pd->value = v; return idx; } void freeArrayValue(int idx) { - PropertyDescriptor &pd = arrayData[idx]; - pd.attrs.clear(); - pd.value.tag = Value::_Undefined_Type; + Property &pd = arrayData[idx]; + pd.value.tag = Value::_Deleted_Type; pd.value.int_32 = arrayFreeList; arrayFreeList = idx; - } - - PropertyDescriptor *arrayDecriptor(uint index) { - return arrayData + index; - } - const PropertyDescriptor *arrayDecriptor(uint index) const { - return arrayData + index; + if (arrayAttributes) + arrayAttributes[idx].clear(); } void getArrayHeadRoom() { assert(!sparseArray && !arrayOffset); arrayOffset = qMax(arrayDataLen >> 2, (uint)16); - PropertyDescriptor *newArray = new PropertyDescriptor[arrayOffset + arrayAlloc]; - memcpy(newArray + arrayOffset, arrayData, arrayDataLen*sizeof(PropertyDescriptor)); + Property *newArray = new Property[arrayOffset + arrayAlloc]; + memcpy(newArray + arrayOffset, arrayData, arrayDataLen*sizeof(Property)); delete [] arrayData; arrayData = newArray + arrayOffset; + if (arrayAttributes) { + PropertyAttributes *newAttrs = new PropertyAttributes[arrayOffset + arrayAlloc]; + memcpy(newAttrs + arrayOffset, arrayAttributes, arrayDataLen*sizeof(PropertyAttributes)); + delete [] arrayAttributes; + arrayAttributes = newAttrs + arrayOffset; + } } public: @@ -240,54 +233,68 @@ public: void setArrayLengthUnchecked(uint l); - PropertyDescriptor *arrayInsert(uint index) { - PropertyDescriptor *pd; + Property *arrayInsert(uint index, PropertyAttributes attributes = Attr_Data) { + + Property *pd; if (!sparseArray && (index < 0x1000 || index < arrayDataLen + (arrayDataLen >> 2))) { if (index >= arrayAlloc) arrayReserve(index + 1); if (index >= arrayDataLen) { - for (uint i = arrayDataLen; i < index; ++i) { - arrayData[i].attrs.clear(); - arrayData[i].value.tag = Value::_Undefined_Type; - } + ensureArrayAttributes(); + for (uint i = arrayDataLen; i < index; ++i) + arrayAttributes[i].clear(); arrayDataLen = index + 1; } - pd = arrayDecriptor(index); + pd = arrayData + index; } else { initSparse(); SparseArrayNode *n = sparseArray->insert(index); if (n->value == UINT_MAX) n->value = allocArrayValue(); - pd = arrayDecriptor(n->value); + pd = arrayData + n->value; } if (index >= arrayLength()) setArrayLengthUnchecked(index + 1); + if (arrayAttributes || attributes != Attr_Data) { + if (!arrayAttributes) + ensureArrayAttributes(); + attributes.resolve(); + arrayAttributes[pd - arrayData] = attributes; + } return pd; } - void arraySet(uint index, const PropertyDescriptor *pd) { + void arraySet(uint index, const Property *pd) { *arrayInsert(index) = *pd; } void arraySet(uint index, Value value) { - PropertyDescriptor *pd = arrayInsert(index); - fillDescriptor(pd, value); + Property *pd = arrayInsert(index); + pd->value = value; } - PropertyDescriptor *arrayAt(uint index) const { + uint propertyIndexFromArrayIndex(uint index) const + { if (!sparseArray) { if (index >= arrayDataLen) - return 0; - return arrayData + index; + return UINT_MAX; + return index; } else { SparseArrayNode *n = sparseArray->findNode(index); if (!n) - return 0; - return arrayData + n->value; + return UINT_MAX; + return n->value; } } - PropertyDescriptor *nonSparseArrayAt(uint index) const { + Property *arrayAt(uint index) const { + uint pidx = propertyIndexFromArrayIndex(index); + if (pidx == UINT_MAX) + return 0; + return arrayData + pidx; + } + + Property *nonSparseArrayAt(uint index) const { if (sparseArray) return 0; if (index >= arrayDataLen) @@ -302,7 +309,7 @@ public: if (!sparseArray) { if (idx >= arrayAlloc) arrayReserve(idx + 1); - fillDescriptor(arrayData + idx, v); + arrayData[idx].value = v; arrayDataLen = idx + 1; } else { uint idx = allocArrayValue(v); @@ -319,6 +326,7 @@ public: Value arrayIndexOf(Value v, uint fromIndex, uint arrayDataLen, ExecutionContext *ctx, Object *o); void arrayReserve(uint n); + void ensureArrayAttributes(); using Managed::get; using Managed::getIndexed; @@ -401,7 +409,7 @@ inline void Object::setArrayLengthUnchecked(uint l) { if (isArrayObject()) { // length is always the first property of an array - PropertyDescriptor &lengthProperty = memberData[ArrayObject::LengthPropertyIndex]; + Property &lengthProperty = memberData[ArrayObject::LengthPropertyIndex]; lengthProperty.value = Value::fromUInt32(l); } } diff --git a/src/v4/qv4objectiterator.cpp b/src/v4/qv4objectiterator.cpp index 29b0c96482..f7c211365e 100644 --- a/src/v4/qv4objectiterator.cpp +++ b/src/v4/qv4objectiterator.cpp @@ -52,20 +52,18 @@ ObjectIterator::ObjectIterator(ExecutionContext *context, Object *o, uint flags) , current(o) , arrayNode(0) , arrayIndex(0) - , currentClass(0) , memberIndex(0) , flags(flags) { if (current) { - currentClass = current->internalClass; if (current->asStringObject()) this->flags |= CurrentIsString; } } -PropertyDescriptor *ObjectIterator::next(String **name, uint *index) +Property *ObjectIterator::next(String **name, uint *index, PropertyAttributes *attrs) { - PropertyDescriptor *p = 0; + Property *p = 0; *name = 0; *index = UINT_MAX; while (1) { @@ -78,6 +76,8 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index) while (arrayIndex < slen) { *index = arrayIndex; ++arrayIndex; + if (attrs) + *attrs = s->arrayAttributes ? s->arrayAttributes[arrayIndex] : PropertyAttributes(Attr_NotWritable|Attr_NotConfigurable); return s->__getOwnProperty__(context, *index); } flags &= ~CurrentIsString; @@ -94,11 +94,15 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index) if (arrayNode) { while (arrayNode != current->sparseArrayEnd()) { int k = arrayNode->key(); - p = current->arrayAt(k); + uint pidx = arrayNode->value; + p = current->arrayData + pidx; arrayNode = arrayNode->nextNode(); - if (p && (!(flags & EnumberableOnly) || p->attrs.isEnumerable())) { + PropertyAttributes a = current->arrayAttributes ? current->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); + if (!(flags & EnumberableOnly) || a.isEnumerable()) { arrayIndex = k + 1; *index = k; + if (attrs) + *attrs = a; return p; } } @@ -107,15 +111,20 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index) } // dense arrays while (arrayIndex < current->arrayDataLen) { - p = current->arrayAt(arrayIndex); + uint pidx = current->propertyIndexFromArrayIndex(arrayIndex); + p = current->arrayData + pidx; + PropertyAttributes a = current->arrayAttributes ? current->arrayAttributes[pidx] : PropertyAttributes(Attr_Data); ++arrayIndex; - if (p && p->attrs.type() != PropertyAttributes::Generic && (!(flags & EnumberableOnly) || p->attrs.isEnumerable())) { + if ((!current->arrayAttributes || !current->arrayAttributes[pidx].isGeneric()) + && (!(flags & EnumberableOnly) || a.isEnumerable())) { *index = arrayIndex - 1; + if (attrs) + *attrs = a; return p; } } - if (memberIndex == currentClass->size) { + if (memberIndex == current->internalClass->size) { if (flags & WithProtoChain) current = current->prototype; else @@ -128,18 +137,19 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index) arrayIndex = 0; memberIndex = 0; - if (current) - currentClass = current->internalClass; continue; } - String *n = currentClass->nameMap.at(memberIndex); + String *n = current->internalClass->nameMap.at(memberIndex); assert(n); // ### check that it's not a repeated attribute p = current->memberData + memberIndex; + PropertyAttributes a = current->internalClass->propertyData[memberIndex]; ++memberIndex; - if (!(flags & EnumberableOnly) || p->attrs.isEnumerable()) { + if (!(flags & EnumberableOnly) || a.isEnumerable()) { *name = n; + if (attrs) + *attrs = a; return p; } } diff --git a/src/v4/qv4objectiterator.h b/src/v4/qv4objectiterator.h index 4f23b7e417..06c6ee80ab 100644 --- a/src/v4/qv4objectiterator.h +++ b/src/v4/qv4objectiterator.h @@ -43,6 +43,7 @@ #include "qv4value.h" #include <qv4internalclass.h> +#include <qv4propertydescriptor.h> QT_BEGIN_NAMESPACE @@ -51,7 +52,6 @@ namespace VM { struct SparseArrayNode; struct Object; -struct PropertyDescriptor; struct ObjectIterator { @@ -67,12 +67,11 @@ struct ObjectIterator Object *current; SparseArrayNode *arrayNode; uint arrayIndex; - InternalClass *currentClass; uint memberIndex; uint flags; ObjectIterator(ExecutionContext *context, Object *o, uint flags); - PropertyDescriptor *next(String **name, uint *index); + Property *next(String **name, uint *index, PropertyAttributes *attributes = 0); Value nextPropertyName(); Value nextPropertyNameAsString(); }; diff --git a/src/v4/qv4objectproto.cpp b/src/v4/qv4objectproto.cpp index 0721aa1b4a..b818d28821 100644 --- a/src/v4/qv4objectproto.cpp +++ b/src/v4/qv4objectproto.cpp @@ -145,8 +145,9 @@ Value ObjectPrototype::method_getOwnPropertyDescriptor(SimpleCallContext *ctx) ctx->throwTypeError(); String *name = ctx->argument(1).toString(ctx); - PropertyDescriptor *desc = O.objectValue()->__getOwnProperty__(ctx, name); - return fromPropertyDescriptor(ctx, desc); + PropertyAttributes attrs; + Property *desc = O.objectValue()->__getOwnProperty__(ctx, name, &attrs); + return fromPropertyDescriptor(ctx, desc, attrs); } Value ObjectPrototype::method_getOwnPropertyNames(SimpleCallContext *context) @@ -193,10 +194,11 @@ Value ObjectPrototype::method_defineProperty(SimpleCallContext *ctx) String *name = ctx->argument(1).toString(ctx); Value attributes = ctx->argument(2); - PropertyDescriptor pd; - toPropertyDescriptor(ctx, attributes, &pd); + Property pd; + PropertyAttributes attrs; + toPropertyDescriptor(ctx, attributes, &pd, &attrs); - if (!O.objectValue()->__defineOwnProperty__(ctx, name, &pd)) + if (!O.objectValue()->__defineOwnProperty__(ctx, name, pd, attrs)) ctx->throwTypeError(); return O; @@ -214,16 +216,18 @@ Value ObjectPrototype::method_defineProperties(SimpleCallContext *ctx) while (1) { uint index; String *name; - PropertyDescriptor *pd = it.next(&name, &index); + PropertyAttributes attrs; + Property *pd = it.next(&name, &index, &attrs); if (!pd) break; - PropertyDescriptor n; - toPropertyDescriptor(ctx, o->getValue(ctx, pd), &n); + Property n; + PropertyAttributes nattrs; + toPropertyDescriptor(ctx, o->getValue(ctx, pd, attrs), &n, &nattrs); bool ok; if (name) - ok = O.objectValue()->__defineOwnProperty__(ctx, name, &n); + ok = O.objectValue()->__defineOwnProperty__(ctx, name, n, nattrs); else - ok = O.objectValue()->__defineOwnProperty__(ctx, index, &n); + ok = O.objectValue()->__defineOwnProperty__(ctx, index, n, nattrs); if (!ok) ctx->throwTypeError(); } @@ -239,15 +243,14 @@ Value ObjectPrototype::method_seal(SimpleCallContext *ctx) Object *o = ctx->argument(0).objectValue(); o->extensible = false; - ObjectIterator it(ctx, o, ObjectIterator::NoFlags); - while (1) { - uint index; - String *name; - PropertyDescriptor *pd = it.next(&name, &index); - if (!pd) - break; - pd->attrs.setConfigurable(false); + o->internalClass = o->internalClass->sealed(); + + o->ensureArrayAttributes(); + for (uint i = 0; i < o->arrayDataLen; ++i) { + if (!o->arrayAttributes[i].isGeneric()) + o->arrayAttributes[i].setConfigurable(false); } + return ctx->argument(0); } @@ -259,16 +262,14 @@ Value ObjectPrototype::method_freeze(SimpleCallContext *ctx) Object *o = ctx->argument(0).objectValue(); o->extensible = false; - ObjectIterator it(ctx, o, ObjectIterator::NoFlags); - while (1) { - uint index; - String *name; - PropertyDescriptor *pd = it.next(&name, &index); - if (!pd) - break; - if (pd->attrs.type() == PropertyAttributes::Data) - pd->attrs.setWritable(false); - pd->attrs.setConfigurable(false); + o->internalClass = o->internalClass->frozen(); + + o->ensureArrayAttributes(); + for (uint i = 0; i < o->arrayDataLen; ++i) { + if (!o->arrayAttributes[i].isGeneric()) + o->arrayAttributes[i].setConfigurable(false); + if (o->arrayAttributes[i].isData()) + o->arrayAttributes[i].setWritable(false); } return ctx->argument(0); } @@ -292,16 +293,21 @@ Value ObjectPrototype::method_isSealed(SimpleCallContext *ctx) if (o->extensible) return Value::fromBoolean(false); - ObjectIterator it(ctx, o, ObjectIterator::NoFlags); - while (1) { - uint index; - String *name; - PropertyDescriptor *pd = it.next(&name, &index); - if (!pd) - break; - if (pd->attrs.isConfigurable()) - return Value::fromBoolean(false); + if (o->internalClass != o->internalClass->sealed()) + return Value::fromBoolean(false); + + if (!o->arrayDataLen) + return Value::fromBoolean(true); + + if (!o->arrayAttributes) + return Value::fromBoolean(false); + + for (uint i = 0; i < o->arrayDataLen; ++i) { + if (!o->arrayAttributes[i].isGeneric()) + if (o->arrayAttributes[i].isConfigurable()) + return Value::fromBoolean(false); } + return Value::fromBoolean(true); } @@ -314,16 +320,21 @@ Value ObjectPrototype::method_isFrozen(SimpleCallContext *ctx) if (o->extensible) return Value::fromBoolean(false); - ObjectIterator it(ctx, o, ObjectIterator::NoFlags); - while (1) { - uint index; - String *name; - PropertyDescriptor *pd = it.next(&name, &index); - if (!pd) - break; - if (pd->attrs.isWritable() || pd->attrs.isConfigurable()) - return Value::fromBoolean(false); + if (o->internalClass != o->internalClass->frozen()) + return Value::fromBoolean(false); + + if (!o->arrayDataLen) + return Value::fromBoolean(true); + + if (!o->arrayAttributes) + return Value::fromBoolean(false); + + for (uint i = 0; i < o->arrayDataLen; ++i) { + if (!o->arrayAttributes[i].isGeneric()) + if (o->arrayAttributes[i].isConfigurable() || o->arrayAttributes[i].isWritable()) + return Value::fromBoolean(false); } + return Value::fromBoolean(true); } @@ -349,7 +360,7 @@ Value ObjectPrototype::method_keys(SimpleCallContext *ctx) while (1) { uint index; String *name; - PropertyDescriptor *pd = it.next(&name, &index); + Property *pd = it.next(&name, &index); if (!pd) break; Value key; @@ -422,8 +433,9 @@ Value ObjectPrototype::method_propertyIsEnumerable(SimpleCallContext *ctx) String *p = ctx->argument(0).toString(ctx); Object *o = ctx->thisObject.toObject(ctx); - PropertyDescriptor *pd = o->__getOwnProperty__(ctx, p); - return Value::fromBoolean(pd && pd->attrs.isEnumerable()); + PropertyAttributes attrs; + o->__getOwnProperty__(ctx, p, &attrs); + return Value::fromBoolean(attrs.isEnumerable()); } Value ObjectPrototype::method_defineGetter(SimpleCallContext *ctx) @@ -438,10 +450,8 @@ Value ObjectPrototype::method_defineGetter(SimpleCallContext *ctx) Object *o = ctx->thisObject.toObject(ctx); - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(f, 0); - pd.attrs.setConfigurable(true); - pd.attrs.setEnumerable(true); - o->__defineOwnProperty__(ctx, prop, &pd); + Property pd = Property::fromAccessor(f, 0); + o->__defineOwnProperty__(ctx, prop, pd, Attr_Accessor); return Value::undefinedValue(); } @@ -457,75 +467,75 @@ Value ObjectPrototype::method_defineSetter(SimpleCallContext *ctx) Object *o = ctx->thisObject.toObject(ctx); - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(0, f); - pd.attrs.setConfigurable(true); - pd.attrs.setEnumerable(true); - o->__defineOwnProperty__(ctx, prop, &pd); + Property pd = Property::fromAccessor(0, f); + o->__defineOwnProperty__(ctx, prop, pd, Attr_Accessor); return Value::undefinedValue(); } -void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, PropertyDescriptor *desc) +void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Property *desc, PropertyAttributes *attrs) { if (!v.isObject()) ctx->throwTypeError(); Object *o = v.objectValue(); - desc->attrs.clear(); - desc->get = 0; - desc->set = 0; + attrs->clear(); + desc->setGetter(0); + desc->setSetter(0); if (o->__hasProperty__(ctx, ctx->engine->id_enumerable)) - desc->attrs.setEnumerable(o->get(ctx, ctx->engine->id_enumerable).toBoolean()); + attrs->setEnumerable(o->get(ctx, ctx->engine->id_enumerable).toBoolean()); if (o->__hasProperty__(ctx, ctx->engine->id_configurable)) - desc->attrs.setConfigurable(o->get(ctx, ctx->engine->id_configurable).toBoolean()); + attrs->setConfigurable(o->get(ctx, ctx->engine->id_configurable).toBoolean()); if (o->__hasProperty__(ctx, ctx->engine->id_get)) { Value get = o->get(ctx, ctx->engine->id_get); FunctionObject *f = get.asFunctionObject(); if (f) { - desc->get = f; + desc->setGetter(f); } else if (get.isUndefined()) { - desc->get = (FunctionObject *)0x1; + desc->setGetter((FunctionObject *)0x1); } else { ctx->throwTypeError(); } - desc->attrs.setType(PropertyAttributes::Accessor); + attrs->setType(PropertyAttributes::Accessor); } if (o->__hasProperty__(ctx, ctx->engine->id_set)) { Value set = o->get(ctx, ctx->engine->id_set); FunctionObject *f = set.asFunctionObject(); if (f) { - desc->set = f; + desc->setSetter(f); } else if (set.isUndefined()) { - desc->set = (FunctionObject *)0x1; + desc->setSetter((FunctionObject *)0x1); } else { ctx->throwTypeError(); } - desc->attrs.setType(PropertyAttributes::Accessor); + attrs->setType(PropertyAttributes::Accessor); } if (o->__hasProperty__(ctx, ctx->engine->id_writable)) { - if (desc->attrs.isAccessor()) + if (attrs->isAccessor()) ctx->throwTypeError(); - desc->attrs.setWritable(o->get(ctx, ctx->engine->id_writable).toBoolean()); + attrs->setWritable(o->get(ctx, ctx->engine->id_writable).toBoolean()); // writable forces it to be a data descriptor desc->value = Value::undefinedValue(); } if (o->__hasProperty__(ctx, ctx->engine->id_value)) { - if (desc->attrs.isAccessor()) + if (attrs->isAccessor()) ctx->throwTypeError(); desc->value = o->get(ctx, ctx->engine->id_value); - desc->attrs.setType(PropertyAttributes::Data); + attrs->setType(PropertyAttributes::Data); } + if (attrs->isGeneric()) + desc->value = Value::deletedValue(); } -Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc) +Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const Property *desc, PropertyAttributes attrs) { if (!desc) return Value::undefinedValue(); @@ -534,24 +544,22 @@ Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const Prope // Let obj be the result of creating a new object as if by the expression new Object() where Object is the standard built-in constructor with that name. Object *o = engine->newObject(); - PropertyDescriptor pd; - pd.attrs = Attr_Data; - - if (desc->attrs.isData()) { + Property pd; + if (attrs.isData()) { pd.value = desc->value; - o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("value")), &pd); - pd.value = Value::fromBoolean(desc->attrs.isWritable()); - o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("writable")), &pd); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("value")), pd, Attr_Data); + pd.value = Value::fromBoolean(attrs.isWritable()); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("writable")), pd, Attr_Data); } else { - pd.value = desc->get ? Value::fromObject(desc->get) : Value::undefinedValue(); - o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("get")), &pd); - pd.value = desc->set ? Value::fromObject(desc->set) : Value::undefinedValue(); - o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("set")), &pd); + pd.value = desc->getter() ? Value::fromObject(desc->getter()) : Value::undefinedValue(); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("get")), pd, Attr_Data); + pd.value = desc->setter() ? Value::fromObject(desc->setter()) : Value::undefinedValue(); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("set")), pd, Attr_Data); } - pd.value = Value::fromBoolean(desc->attrs.isEnumerable()); - o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("enumerable")), &pd); - pd.value = Value::fromBoolean(desc->attrs.isConfigurable()); - o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("configurable")), &pd); + pd.value = Value::fromBoolean(attrs.isEnumerable()); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("enumerable")), pd, Attr_Data); + pd.value = Value::fromBoolean(attrs.isConfigurable()); + o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("configurable")), pd, Attr_Data); return Value::fromObject(o); } diff --git a/src/v4/qv4objectproto.h b/src/v4/qv4objectproto.h index 1abd8d13a6..0a19f45b06 100644 --- a/src/v4/qv4objectproto.h +++ b/src/v4/qv4objectproto.h @@ -91,8 +91,8 @@ struct ObjectPrototype: Object static Value method_defineGetter(SimpleCallContext *ctx); static Value method_defineSetter(SimpleCallContext *ctx); - static void toPropertyDescriptor(ExecutionContext *ctx, Value v, PropertyDescriptor *desc); - static Value fromPropertyDescriptor(ExecutionContext *ctx, const PropertyDescriptor *desc); + static void toPropertyDescriptor(ExecutionContext *ctx, Value v, Property *desc, PropertyAttributes *attrs); + static Value fromPropertyDescriptor(ExecutionContext *ctx, const Property *desc, PropertyAttributes attrs); }; diff --git a/src/v4/qv4propertydescriptor.h b/src/v4/qv4propertydescriptor.h index 0d77067743..afb5ede277 100644 --- a/src/v4/qv4propertydescriptor.h +++ b/src/v4/qv4propertydescriptor.h @@ -51,7 +51,6 @@ namespace QQmlJS { namespace VM { struct FunctionObject; -struct PropertyDescriptor; struct Property { union { @@ -65,87 +64,86 @@ struct Property { // Section 8.10 inline void fullyPopulated(PropertyAttributes *attrs) { if (!attrs->hasType()) { - attrs->setType(PropertyAttributes::Data); value = Value::undefinedValue(); } - if (attrs->type() == PropertyAttributes::Data) { - attrs->resolveWritable(); - } else { + if (attrs->type() == PropertyAttributes::Accessor) { attrs->clearWritable(); - if ((quintptr)get == 0x1) + if (get == (FunctionObject *)0x1) get = 0; - if ((quintptr)set == 0x1) + if (set == (FunctionObject *)0x1) set = 0; } - attrs->resolveEnumerable(); - attrs->resolveConfigurable(); + attrs->resolve(); } - inline bool isSubset(const PropertyAttributes &attrs, PropertyDescriptor *other) const; - inline void merge(PropertyAttributes &attrs, const PropertyDescriptor &other); -}; - -struct PropertyDescriptor : public Property -{ - PropertyAttributes attrs; - - static inline PropertyDescriptor fromValue(Value v) { - PropertyDescriptor pd; + static inline Property fromValue(Value v) { + Property pd; pd.value = v; - pd.attrs.setType(PropertyAttributes::Data); return pd; } - static inline PropertyDescriptor fromAccessor(FunctionObject *getter, FunctionObject *setter) { - PropertyDescriptor pd; + static inline Property fromAccessor(FunctionObject *getter, FunctionObject *setter) { + Property pd; pd.get = getter; pd.set = setter; - pd.attrs.setType(PropertyAttributes::Accessor); return pd; } + + static Property genericDescriptor() { + Property pd; + pd.value = Value::deletedValue(); + return pd; + } + + inline bool isSubset(const PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) const; + inline void merge(PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs); + + inline FunctionObject *getter() const { return get; } + inline FunctionObject *setter() const { return set; } + inline void setGetter(FunctionObject *g) { get = g; } + inline void setSetter(FunctionObject *s) { set = s; } }; -inline bool Property::isSubset(const PropertyAttributes &attrs, PropertyDescriptor *other) const +inline bool Property::isSubset(const PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) const { - if (attrs.type() != PropertyAttributes::Generic && attrs.type() != other->attrs.type()) + if (attrs.type() != PropertyAttributes::Generic && attrs.type() != otherAttrs.type()) return false; - if (attrs.hasEnumerable() && attrs.isEnumerable() != other->attrs.isEnumerable()) + if (attrs.hasEnumerable() && attrs.isEnumerable() != otherAttrs.isEnumerable()) return false; - if (attrs.hasConfigurable() && attrs.isConfigurable() != other->attrs.isConfigurable()) + if (attrs.hasConfigurable() && attrs.isConfigurable() != otherAttrs.isConfigurable()) return false; - if (attrs.hasWritable() && attrs.isWritable() != other->attrs.isWritable()) + if (attrs.hasWritable() && attrs.isWritable() != otherAttrs.isWritable()) return false; - if (attrs.type() == PropertyAttributes::Data && !value.sameValue(other->value)) + if (attrs.type() == PropertyAttributes::Data && !value.sameValue(other.value)) return false; if (attrs.type() == PropertyAttributes::Accessor) { - if (get != other->get) + if (get != other.get) return false; - if (set != other->set) + if (set != other.set) return false; } return true; } -inline void Property::merge(PropertyAttributes &attrs, const PropertyDescriptor &other) +inline void Property::merge(PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) { - if (other.attrs.hasEnumerable()) - attrs.setEnumerable(other.attrs.isEnumerable()); - if (other.attrs.hasConfigurable()) - attrs.setConfigurable(other.attrs.isConfigurable()); - if (other.attrs.hasWritable()) - attrs.setWritable(other.attrs.isWritable()); - if (other.attrs.type() == PropertyAttributes::Accessor) { + if (otherAttrs.hasEnumerable()) + attrs.setEnumerable(otherAttrs.isEnumerable()); + if (otherAttrs.hasConfigurable()) + attrs.setConfigurable(otherAttrs.isConfigurable()); + if (otherAttrs.hasWritable()) + attrs.setWritable(otherAttrs.isWritable()); + if (otherAttrs.type() == PropertyAttributes::Accessor) { attrs.setType(PropertyAttributes::Accessor); if (other.get) - get = ((quintptr)other.get == 0x1) ? 0 : other.get; + get = (other.get == (FunctionObject *)0x1) ? 0 : other.get; if (other.set) - set = ((quintptr)other.set == 0x1) ? 0 : other.set; - } else if (other.attrs.type() == PropertyAttributes::Data){ + set = (other.set == (FunctionObject *)0x1) ? 0 : other.set; + } else if (otherAttrs.type() == PropertyAttributes::Data){ attrs.setType(PropertyAttributes::Data); value = other.value; } } - } // namespace VM } // namespace QQmlJS diff --git a/src/v4/qv4regexpobject.cpp b/src/v4/qv4regexpobject.cpp index 3717da6e57..cc1d84747a 100644 --- a/src/v4/qv4regexpobject.cpp +++ b/src/v4/qv4regexpobject.cpp @@ -73,9 +73,8 @@ RegExpObject::RegExpObject(ExecutionEngine *engine, RegExp* value, bool global) vtbl = &static_vtbl; type = Type_RegExpObject; - PropertyDescriptor *lastIndexProperty = insertMember(engine->newIdentifier(QStringLiteral("lastIndex")), + Property *lastIndexProperty = insertMember(engine->newIdentifier(QStringLiteral("lastIndex")), Attr_NotEnumerable|Attr_NotConfigurable); - lastIndexProperty->attrs = Attr_NotEnumerable|Attr_NotConfigurable; lastIndexProperty->value = Value::fromInt32(0); if (!this->value) return; @@ -98,7 +97,7 @@ void RegExpObject::markObjects(Managed *that) Object::markObjects(that); } -PropertyDescriptor *RegExpObject::lastIndexProperty(ExecutionContext *ctx) +Property *RegExpObject::lastIndexProperty(ExecutionContext *ctx) { assert(0 == internalClass->find(ctx->engine->newIdentifier(QStringLiteral("lastIndex")))); return &memberData[0]; diff --git a/src/v4/qv4regexpobject.h b/src/v4/qv4regexpobject.h index b949953504..9447c0516a 100644 --- a/src/v4/qv4regexpobject.h +++ b/src/v4/qv4regexpobject.h @@ -66,7 +66,7 @@ namespace VM { struct RegExpObject: Object { RegExp* value; - PropertyDescriptor *lastIndexProperty(ExecutionContext *ctx); + Property *lastIndexProperty(ExecutionContext *ctx); bool global; RegExpObject(ExecutionEngine *engine, RegExp* value, bool global); ~RegExpObject() {} diff --git a/src/v4/qv4runtime.cpp b/src/v4/qv4runtime.cpp index 083bfea206..fcc2b4f5a1 100644 --- a/src/v4/qv4runtime.cpp +++ b/src/v4/qv4runtime.cpp @@ -613,11 +613,13 @@ void __qmljs_get_element(ExecutionContext *ctx, Value *result, const Value &obje } if (idx < UINT_MAX) { - const PropertyDescriptor *p = o->nonSparseArrayAt(idx); - if (p && p->attrs.type() == PropertyAttributes::Data) { - if (result) - *result = p->value; - return; + uint pidx = o->propertyIndexFromArrayIndex(idx); + if (pidx < UINT_MAX) { + if (!o->arrayAttributes || o->arrayAttributes[pidx].isData()) { + if (result) + *result = o->arrayData[pidx].value; + return; + } } Value res = o->getIndexed(ctx, idx); @@ -638,10 +640,33 @@ void __qmljs_set_element(ExecutionContext *ctx, const Value &object, const Value uint idx = index.asArrayIndex(); if (idx < UINT_MAX) { - PropertyDescriptor *p = o->nonSparseArrayAt(idx); - if (p && p->attrs.type() == PropertyAttributes::Data && p->attrs.isWritable()) { - p->value = value; - return; + uint pidx = o->propertyIndexFromArrayIndex(idx); + if (pidx < UINT_MAX) { + if (o->arrayAttributes && !o->arrayAttributes[pidx].isEmpty() && !o->arrayAttributes[pidx].isWritable()) { + if (ctx->strictMode) + ctx->throwTypeError(); + return; + } + + Property *p = o->arrayData + pidx; + if (!o->arrayAttributes || o->arrayAttributes[pidx].isData()) { + p->value = value; + return; + } + + if (o->arrayAttributes[pidx].isAccessor()) { + FunctionObject *setter = p->setter(); + if (!setter) { + if (ctx->strictMode) + ctx->throwTypeError(); + return; + } + + Value args[1]; + args[0] = value; + setter->call(ctx, Value::fromObject(o), args, 1); + return; + } } o->putIndexed(ctx, idx, value); return; @@ -700,9 +725,10 @@ void __qmljs_get_property_lookup(ExecutionContext *ctx, Value *result, const Val Value res; Lookup *l = ctx->lookups + lookupIndex; if (Object *o = object.asObject()) { - PropertyDescriptor *p = l->lookup(o); + PropertyAttributes attrs; + Property *p = l->lookup(o, &attrs); if (p) - res = p->attrs.type() == PropertyAttributes::Data ? p->value : o->getValue(ctx, p); + res = attrs.isData() ? p->value : o->getValue(ctx, p, attrs); else res = Value::undefinedValue(); } else { @@ -722,9 +748,10 @@ void __qmljs_set_property_lookup(ExecutionContext *ctx, const Value &object, int Object *o = object.toObject(ctx); Lookup *l = ctx->lookups + lookupIndex; - PropertyDescriptor *p = l->setterLookup(o); + bool writable; + Property *p = l->setterLookup(o, &writable); if (p && (l->index != ArrayObject::LengthPropertyIndex || !o->isArrayObject())) { - o->putValue(ctx, p, value); + o->putValue(ctx, p, o->internalClass->propertyData[l->index], value); return; } @@ -861,10 +888,11 @@ void __qmljs_call_property_lookup(ExecutionContext *context, Value *result, cons else baseObject = __qmljs_convert_to_object(context, thisObject); - PropertyDescriptor *p = l->lookup(baseObject); + PropertyAttributes attrs; + Property *p = l->lookup(baseObject, &attrs); if (!p) context->throwTypeError(); - Value func = p->attrs.type() == PropertyAttributes::Data ? p->value : baseObject->getValue(context, p); + Value func = attrs.isData() ? p->value : baseObject->getValue(context, p, attrs); FunctionObject *o = func.asFunctionObject(); if (!o) context->throwTypeError(); @@ -1210,9 +1238,8 @@ void __qmljs_builtin_define_property(ExecutionContext *ctx, const Value &object, assert(o); uint idx = name->asArrayIndex(); - PropertyDescriptor *pd = (idx != UINT_MAX) ? o->arrayInsert(idx) : o->insertMember(name, Attr_Data); + Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx) : o->insertMember(name, Attr_Data); pd->value = val ? *val : Value::undefinedValue(); - pd->attrs = PropertyAttributes(Attr_Data); } void __qmljs_builtin_define_array(ExecutionContext *ctx, Value *array, Value *values, uint length) @@ -1223,18 +1250,18 @@ void __qmljs_builtin_define_array(ExecutionContext *ctx, Value *array, Value *va // This should rather be done when required a->arrayReserve(length); if (length) { - PropertyDescriptor *pd = a->arrayData; + a->arrayDataLen = length; + Property *pd = a->arrayData; for (uint i = 0; i < length; ++i) { - if (values[i].isUndefined() && values[i].uint_32 == UINT_MAX) { + if (values[i].isDeleted()) { + a->ensureArrayAttributes(); pd->value = Value::undefinedValue(); - pd->attrs.clear(); + a->arrayAttributes[i].clear(); } else { pd->value = values[i]; - pd->attrs = PropertyAttributes(Attr_Data); } ++pd; } - a->arrayDataLen = length; a->setArrayLengthUnchecked(length); } *array = Value::fromObject(a); @@ -1246,10 +1273,9 @@ void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const Value &ob assert(o); uint idx = name->asArrayIndex(); - PropertyDescriptor *pd = (idx != UINT_MAX) ? o->arrayInsert(idx) : o->insertMember(name, Attr_Accessor); - pd->get = getter ? getter->asFunctionObject() : 0; - pd->set = setter ? setter->asFunctionObject() : 0; - pd->attrs = Attr_Accessor; + Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx, Attr_Accessor) : o->insertMember(name, Attr_Accessor); + pd->setGetter(getter ? getter->asFunctionObject() : 0); + pd->setSetter(setter ? setter->asFunctionObject() : 0); } void __qmljs_increment(ExecutionContext *ctx, Value *result, const Value &value) diff --git a/src/v4/qv4runtime.h b/src/v4/qv4runtime.h index 09581c9f4f..8640c829df 100644 --- a/src/v4/qv4runtime.h +++ b/src/v4/qv4runtime.h @@ -77,7 +77,6 @@ enum TypeHint { struct Function; struct Object; struct String; -struct PropertyDescriptor; struct ExecutionContext; struct FunctionObject; struct BooleanObject; diff --git a/src/v4/qv4sparsearray.cpp b/src/v4/qv4sparsearray.cpp index 33aab75b1c..b1ac830010 100644 --- a/src/v4/qv4sparsearray.cpp +++ b/src/v4/qv4sparsearray.cpp @@ -53,14 +53,10 @@ namespace QQmlJS { namespace VM { -bool ArrayElementLessThan::operator()(const PropertyDescriptor &p1, const PropertyDescriptor &p2) const +bool ArrayElementLessThan::operator()(const Property &p1, const Property &p2) const { - if (p1.attrs.type() == PropertyAttributes::Generic) - return false; - if (p2.attrs.type() == PropertyAttributes::Generic) - return true; - Value v1 = thisObject->getValue(m_context, &p1); - Value v2 = thisObject->getValue(m_context, &p2); + Value v1 = p1.value; + Value v2 = p2.value; if (v1.isUndefined()) return false; diff --git a/src/v4/qv4sparsearray.h b/src/v4/qv4sparsearray.h index 40ee7ba06c..c0a79b20a0 100644 --- a/src/v4/qv4sparsearray.h +++ b/src/v4/qv4sparsearray.h @@ -67,7 +67,7 @@ public: inline ArrayElementLessThan(ExecutionContext *context, Object *thisObject, const Value &comparefn) : m_context(context), thisObject(thisObject), m_comparefn(comparefn) {} - bool operator()(const PropertyDescriptor &v1, const PropertyDescriptor &v2) const; + bool operator()(const Property &v1, const Property &v2) const; private: ExecutionContext *m_context; diff --git a/src/v4/qv4string.cpp b/src/v4/qv4string.cpp index 382359fd35..1fd50f4eab 100644 --- a/src/v4/qv4string.cpp +++ b/src/v4/qv4string.cpp @@ -106,15 +106,16 @@ Value String::get(Managed *m, ExecutionContext *ctx, String *name, bool *hasProp *hasProperty = true; return Value::fromInt32(that->_text.length()); } - PropertyDescriptor *pd = ctx->engine->stringPrototype->__getPropertyDescriptor__(ctx, name); - if (!pd || pd->attrs.type() == PropertyAttributes::Generic) { + PropertyAttributes attrs; + Property *pd = ctx->engine->stringPrototype->__getPropertyDescriptor__(ctx, name, &attrs); + if (!pd || attrs.isGeneric()) { if (hasProperty) *hasProperty = false; return Value::undefinedValue(); } if (hasProperty) *hasProperty = true; - return ctx->engine->stringPrototype->getValue(Value::fromString(that), ctx, pd); + return ctx->engine->stringPrototype->getValue(Value::fromString(that), ctx, pd, attrs); } Value String::getIndexed(Managed *m, ExecutionContext *ctx, uint index, bool *hasProperty) @@ -125,15 +126,16 @@ Value String::getIndexed(Managed *m, ExecutionContext *ctx, uint index, bool *ha *hasProperty = true; return Value::fromString(ctx, that->toQString().mid(index, 1)); } - PropertyDescriptor *pd = ctx->engine->stringPrototype->__getPropertyDescriptor__(ctx, index); - if (!pd || pd->attrs.type() == PropertyAttributes::Generic) { + PropertyAttributes attrs; + Property *pd = ctx->engine->stringPrototype->__getPropertyDescriptor__(ctx, index, &attrs); + if (!pd || attrs.isGeneric()) { if (hasProperty) *hasProperty = false; return Value::undefinedValue(); } if (hasProperty) *hasProperty = true; - return ctx->engine->stringPrototype->getValue(Value::fromString(that), ctx, pd); + return ctx->engine->stringPrototype->getValue(Value::fromString(that), ctx, pd, attrs); } void String::put(Managed *m, ExecutionContext *ctx, String *name, const Value &value) diff --git a/src/v4/qv4stringobject.cpp b/src/v4/qv4stringobject.cpp index 129c806f77..6003f4a4e3 100644 --- a/src/v4/qv4stringobject.cpp +++ b/src/v4/qv4stringobject.cpp @@ -83,14 +83,13 @@ StringObject::StringObject(ExecutionContext *ctx, const Value &value) vtbl = &static_vtbl; type = Type_StringObject; - tmpProperty.attrs = Attr_NotWritable|Attr_NotConfigurable; tmpProperty.value = Value::undefinedValue(); assert(value.isString()); defineReadonlyProperty(ctx->engine->id_length, Value::fromUInt32(value.stringValue()->toQString().length())); } -PropertyDescriptor *StringObject::getIndex(const ExecutionContext *ctx, uint index) const +Property *StringObject::getIndex(const ExecutionContext *ctx, uint index) const { QString str = value.stringValue()->toQString(); if (index >= (uint)str.length()) diff --git a/src/v4/qv4stringobject.h b/src/v4/qv4stringobject.h index 73dc740d0c..54f9130a49 100644 --- a/src/v4/qv4stringobject.h +++ b/src/v4/qv4stringobject.h @@ -52,10 +52,10 @@ namespace VM { struct StringObject: Object { Value value; - mutable PropertyDescriptor tmpProperty; + mutable Property tmpProperty; StringObject(ExecutionContext *ctx, const Value &value); - PropertyDescriptor *getIndex(const ExecutionContext *ctx, uint index) const; + Property *getIndex(const ExecutionContext *ctx, uint index) const; protected: static const ManagedVTable static_vtbl; diff --git a/src/v4/qv4v8.cpp b/src/v4/qv4v8.cpp index e4ed24a0e8..db4c84e041 100644 --- a/src/v4/qv4v8.cpp +++ b/src/v4/qv4v8.cpp @@ -923,9 +923,9 @@ bool Object::SetAccessor(Handle<String> name, AccessorGetter getter, AccessorSet PropertyAttributes attrs = Attr_Accessor; attrs.setConfigurable(!(attribute & DontDelete)); attrs.setEnumerable(!(attribute & DontEnum)); - VM::PropertyDescriptor *pd = o->insertMember(name->asVMString(), attrs); - *pd = VM::PropertyDescriptor::fromAccessor(wrappedGetter, wrappedSetter); - pd->attrs = attrs; + VM::Property *pd = o->insertMember(name->asVMString(), attrs); + pd->setGetter(wrappedGetter); + pd->setSetter(wrappedSetter); return true; } @@ -1428,10 +1428,9 @@ public: PropertyAttributes attrs = Attr_Accessor; attrs.setConfigurable(!(acc.attribute & DontDelete)); attrs.setEnumerable(!(acc.attribute & DontEnum)); - VM::PropertyDescriptor *pd = this->insertMember(acc.name->asVMString(), attrs); - *pd = VM::PropertyDescriptor::fromAccessor(acc.getter->vmValue().asFunctionObject(), - acc.setter->vmValue().asFunctionObject()); - pd->attrs = attrs; + VM::Property *pd = this->insertMember(acc.name->asVMString(), attrs); + *pd = VM::Property::fromAccessor(acc.getter->vmValue().asFunctionObject(), + acc.setter->vmValue().asFunctionObject()); } initProperties(m_template.get()); @@ -1444,9 +1443,8 @@ public: attrs.setConfigurable(!(p.attributes & DontDelete)); attrs.setEnumerable(!(p.attributes & DontEnum)); attrs.setWritable(!(p.attributes & ReadOnly)); - VM::PropertyDescriptor *pd = this->insertMember(p.name->asVMString(), attrs); - *pd = VM::PropertyDescriptor::fromValue(p.value->vmValue()); - pd->attrs = attrs; + VM::Property *pd = this->insertMember(p.name->asVMString(), attrs); + *pd = VM::Property::fromValue(p.value->vmValue()); } } @@ -1523,9 +1521,10 @@ protected: if (!result.IsEmpty()) return; } - PropertyDescriptor *pd = that->__getOwnProperty__(ctx, name); + PropertyAttributes attrs; + Property *pd = that->__getOwnProperty__(ctx, name, &attrs); if (pd) - that->putValue(ctx, pd, value); + that->putValue(ctx, pd, attrs, value); else if (that->m_template->m_fallbackPropertySetter) that->m_template->m_fallbackPropertySetter(String::New(name), v8Value, that->fallbackAccessorInfo()); else diff --git a/src/v4/qv4value.h b/src/v4/qv4value.h index 0fb3464c02..929e4561b4 100644 --- a/src/v4/qv4value.h +++ b/src/v4/qv4value.h @@ -97,6 +97,7 @@ struct Q_V4_EXPORT Value NotDouble_Mask = 0xfffc0000, Type_Mask = 0xffff8000, Immediate_Mask = NotDouble_Mask | 0x00008000, + Special_Mask = Immediate_Mask | 0x20000, Tag_Shift = 32 }; enum ValueType { @@ -105,7 +106,8 @@ struct Q_V4_EXPORT Value Boolean_Type = Immediate_Mask | 0x20000, Integer_Type = Immediate_Mask | 0x30000, Object_Type = NotDouble_Mask | 0x00000, - String_Type = NotDouble_Mask | 0x10000 + String_Type = NotDouble_Mask | 0x10000, + Deleted_Type = NotDouble_Mask | 0x30000, }; enum ImmediateFlags { @@ -114,6 +116,7 @@ struct Q_V4_EXPORT Value enum ValueTypeInternal { _Undefined_Type = Undefined_Type, + _Deleted_Type = Deleted_Type, _Null_Type = Null_Type | ConvertibleToInt, _Boolean_Type = Boolean_Type | ConvertibleToInt, _Integer_Type = Integer_Type | ConvertibleToInt, @@ -126,6 +129,9 @@ struct Q_V4_EXPORT Value return tag & Type_Mask; } + // used internally in property + inline bool isDeleted() const { return tag == _Deleted_Type; } + inline bool isUndefined() const { return tag == _Undefined_Type; } inline bool isNull() const { return tag == _Null_Type; } inline bool isBoolean() const { return tag == _Boolean_Type; } @@ -185,6 +191,7 @@ struct Q_V4_EXPORT Value return val; } + static Value deletedValue(); static Value undefinedValue(); static Value nullValue(); static Value fromBoolean(Bool b); @@ -294,6 +301,15 @@ inline Value Value::nullValue() return v; } +inline VM::Value Value::deletedValue() +{ + VM::Value v; + v.tag = VM::Value::_Deleted_Type; + v.uint_32 = 0; + return v; +} + + inline Value Value::fromBoolean(Bool b) { Value v; |