From d0af8986b32aa4cccdc2ff7d8dc5e66e9d53ed77 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 10 Apr 2013 10:46:23 +0200 Subject: [PATCH] Move property attributes out of the property arrays Rather large change that fully separates the property attributes from the property data. This saves quite some memory. Since the property data for members is saves in the class structure it'll also allow optimizations to the lookup code. Change-Id: I9ba1d372bb756695bef8188d1b86275562a1b219 Reviewed-by: Simon Hausmann --- src/v4/debugging.cpp | 5 +- src/v4/qv4argumentsobject.cpp | 42 +-- src/v4/qv4argumentsobject.h | 2 +- src/v4/qv4arrayobject.cpp | 34 +-- src/v4/qv4context.cpp | 17 +- src/v4/qv4engine.cpp | 12 +- src/v4/qv4engine.h | 3 +- src/v4/qv4functionobject.cpp | 13 +- src/v4/qv4functionobject.h | 18 +- src/v4/qv4global.h | 38 ++- src/v4/qv4internalclass.cpp | 87 +++++- src/v4/qv4internalclass.h | 10 +- src/v4/qv4isel_util_p.h | 10 +- src/v4/qv4jsir.cpp | 3 + src/v4/qv4jsonobject.cpp | 8 +- src/v4/qv4object.cpp | 626 +++++++++++++++++++++++++---------------- src/v4/qv4object.h | 142 +++++----- src/v4/qv4objectiterator.cpp | 36 ++- src/v4/qv4objectiterator.h | 5 +- src/v4/qv4objectproto.cpp | 186 ++++++------ src/v4/qv4objectproto.h | 4 +- src/v4/qv4propertydescriptor.h | 84 +++--- src/v4/qv4regexpobject.cpp | 5 +- src/v4/qv4regexpobject.h | 2 +- src/v4/qv4runtime.cpp | 78 +++-- src/v4/qv4runtime.h | 1 - src/v4/qv4sparsearray.cpp | 10 +- src/v4/qv4sparsearray.h | 2 +- src/v4/qv4string.cpp | 14 +- src/v4/qv4stringobject.cpp | 3 +- src/v4/qv4stringobject.h | 4 +- src/v4/qv4v8.cpp | 23 +- src/v4/qv4value.h | 18 +- tests/TestExpectations | 6 +- 34 files changed, 908 insertions(+), 643 deletions(-) diff --git a/src/v4/debugging.cpp b/src/v4/debugging.cpp index e41dba2..30ab4a1 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 0378112..21c72be 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(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 4dcdadd..6727d87 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 1ca9cf9..39492ef 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 9703809..24a8563 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 54ddf43..7d5c79f 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 6170c08..9cf36d4 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 #include @@ -159,7 +160,7 @@ struct Q_V4_EXPORT ExecutionEngine EvalFunction *evalFunction; - QVector argumentsAccessors; + QVector argumentsAccessors; String *id_undefined; String *id_null; diff --git a/src/v4/qv4functionobject.cpp b/src/v4/qv4functionobject.cpp index 8393ec9..cff0b59 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 c20eb69..1be3dd5 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 928664d..5f3f1b6 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 00e891b..4d9bb93 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::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 92bf19a..cc3b031 100644 --- a/src/v4/qv4internalclass.h +++ b/src/v4/qv4internalclass.h @@ -62,14 +62,22 @@ struct InternalClass { QVector propertyData; QHash 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 0960e17..e10a965 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 88f5668..ddf6509 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 d3a71ac..22cd8ea 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 105af27..45ce45e5 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(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(o)->getIndex(ctx, index); - if (p) + Property *p = static_cast(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(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(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(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(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(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(o)->getIndex(ctx, index); - if (p) { - pd = p; + pd = static_cast(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(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(this)->defineOwnProperty(ctx, index, desc); + return static_cast(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(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(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(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 8c35a5a..9919ce3 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(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(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(this)), ctx, p); + return getValue(Value::fromObject(const_cast(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(this)), ctx, p); + return getValue(Value::fromObject(const_cast(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 29b0c96..f7c2113 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 4f23b7e..06c6ee8 100644 --- a/src/v4/qv4objectiterator.h +++ b/src/v4/qv4objectiterator.h @@ -43,6 +43,7 @@ #include "qv4value.h" #include +#include 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 0721aa1..b818d28 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 1abd8d1..0a19f45 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 0d77067..afb5ede 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 3717da6..cc1d847 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 b949953..9447c05 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 083bfea..fcc2b4f 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 09581c9..8640c82 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 33aab75..b1ac830 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 40ee7ba..c0a79b2 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 382359f..1fd50f4 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 129c806..6003f4a 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 73dc740..54f9130 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 e4ed24a..db4c84e 100644 --- a/src/v4/qv4v8.cpp +++ b/src/v4/qv4v8.cpp @@ -923,9 +923,9 @@ bool Object::SetAccessor(Handle 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 0fb3464..929e456 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; diff --git a/tests/TestExpectations b/tests/TestExpectations index 3a06a5c..b602e08 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -11,10 +11,6 @@ S11.8.6_A5_T2 failing S13_A15_T4 failing S13.2.3_A1 failing -15.4.4.14-9-a-10 failing -15.4.4.14-9-a-17 failing -15.4.4.14-9-a-7 failing -15.4.4.14-9-a-9 failing S15.4.4.3_A1_T1 failing S15.4.4.3_A3_T1 failing 15.4.4.4-5-c-i-1 failing @@ -22,4 +18,4 @@ S15.4.4.4_A2_T1 failing S15.4.4.4_A2_T2 failing S15.4.4.4_A3_T1 failing S15.5.4.11_A5_T1 failing -S15.2.4.4_A14 failing +S15.2.4.4_A14 failing \ No newline at end of file -- 2.7.4