bool Object::putValue(uint memberIndex, const Value &value) { QV4::InternalClass *ic = internalClass(); if (ic->engine->hasException) return false; PropertyAttributes attrs = ic->propertyData[memberIndex]; if (attrs.isAccessor()) { const FunctionObject *set = propertyData(memberIndex + SetterOffset)->as<FunctionObject>(); if (set) { Scope scope(ic->engine); ScopedFunctionObject setter(scope, set); ScopedCallData callData(scope, 1); callData->args[0] = value; callData->thisObject = this; setter->call(scope, callData); return !ic->engine->hasException; } goto reject; } if (!attrs.isWritable()) goto reject; setProperty(memberIndex, value); return true; reject: if (engine()->current->strictMode) engine()->throwTypeError(); return false; }
void Object::putValue(Property *pd, PropertyAttributes attrs, const ValueRef value) { if (internalClass->engine->hasException) return; if (attrs.isAccessor()) { if (FunctionObject *set = pd->setter()) { Scope scope(set->engine()); ScopedCallData callData(scope, 1); callData->args[0] = *value; callData->thisObject = this; set->call(callData); return; } goto reject; } if (!attrs.isWritable()) goto reject; pd->value = *value; return; reject: if (engine()->currentContext()->strictMode) engine()->currentContext()->throwTypeError(); }
void Object::insertMember(String *s, const Property *p, PropertyAttributes attributes) { uint idx; InternalClass::addMember(this, s, attributes, &idx); if (attributes.isAccessor()) { setProperty(idx + GetterOffset, p->value); setProperty(idx + SetterOffset, p->set); } else { setProperty(idx, p->value); } }
ReturnedValue Object::getValue(const ValueRef thisObject, const Property *p, PropertyAttributes attrs) { if (!attrs.isAccessor()) return p->value.asReturnedValue(); FunctionObject *getter = p->getter(); if (!getter) return Encode::undefined(); Scope scope(getter->engine()); ScopedCallData callData(scope, 0); callData->thisObject = *thisObject; return getter->call(callData); }
ReturnedValue Object::getValue(const Value &thisObject, const Value &v, PropertyAttributes attrs) { if (!attrs.isAccessor()) return v.asReturnedValue(); const QV4::FunctionObject *f = v.as<FunctionObject>(); if (!f) return Encode::undefined(); Scope scope(f->engine()); ScopedCallData callData(scope); callData->thisObject = thisObject; f->call(scope, callData); return scope.result.asReturnedValue(); }
void Object::insertMember(const StringRef s, const Property &p, PropertyAttributes attributes) { uint idx; InternalClass::addMember(this, s.getPointer(), attributes, &idx); ensureMemberIndex(internalClass->size); if (attributes.isAccessor()) { hasAccessorProperty = 1; Property *pp = propertyAt(idx); pp->value = p.value; pp->set = p.set; } else { memberData[idx] = p.value; } }
bool Object::internalPutIndexed(uint index, const Value &value) { ExecutionEngine *engine = this->engine(); if (engine->hasException) return false; PropertyAttributes attrs; ArrayData::Index arrayIndex = arrayData() ? arrayData()->getValueOrSetter(index, &attrs) : ArrayData::Index{ 0, 0 }; if (arrayIndex.isNull() && isStringObject()) { if (index < static_cast<StringObject *>(this)->length()) // not writable goto reject; } // clause 1 if (!arrayIndex.isNull()) { if (attrs.isAccessor()) { if (arrayIndex->as<FunctionObject>()) goto cont; goto reject; } else if (!attrs.isWritable()) goto reject; arrayIndex.set(engine, value); return true; } else if (!prototype()) { if (!isExtensible()) goto reject; } else { // clause 4 Scope scope(engine); arrayIndex = ScopedObject(scope, prototype())->getValueOrSetter(index, &attrs); if (!arrayIndex.isNull()) { if (attrs.isAccessor()) { if (!arrayIndex->as<FunctionObject>()) goto reject; } else if (!isExtensible() || !attrs.isWritable()) { goto reject; } } else if (!isExtensible()) { goto reject; } } cont: // Clause 5 if (!arrayIndex.isNull() && attrs.isAccessor()) { Q_ASSERT(arrayIndex->as<FunctionObject>()); Scope scope(engine); ScopedFunctionObject setter(scope, *arrayIndex); ScopedCallData callData(scope, 1); callData->args[0] = value; callData->thisObject = this; setter->call(scope, callData); return !internalClass()->engine->hasException; } arraySet(index, value); return true; reject: // ### this should be removed once everything is ported to use Object::setIndexed() if (engine->current->strictMode) engine->throwTypeError(); return false; }
// Section 8.12.5 bool Object::internalPut(String *name, const Value &value) { ExecutionEngine *engine = this->engine(); if (engine->hasException) return false; uint idx = name->asArrayIndex(); if (idx != UINT_MAX) return putIndexed(idx, value); name->makeIdentifier(); Identifier *id = name->identifier(); MemberData::Index memberIndex{0, 0}; uint member = internalClass()->find(id); PropertyAttributes attrs; if (member < UINT_MAX) { attrs = internalClass()->propertyData[member]; memberIndex = d()->writablePropertyData(attrs.isAccessor() ? member + SetterOffset : member); } // clause 1 if (!memberIndex.isNull()) { if (attrs.isAccessor()) { if (memberIndex->as<FunctionObject>()) goto cont; goto reject; } else if (!attrs.isWritable()) goto reject; else if (isArrayObject() && name->equals(engine->id_length())) { bool ok; uint l = value.asArrayLength(&ok); if (!ok) { engine->throwRangeError(value); return false; } ok = setArrayLength(l); if (!ok) goto reject; } else { memberIndex.set(engine, value); } return true; } else if (!prototype()) { if (!isExtensible()) goto reject; } else { // clause 4 Scope scope(engine); memberIndex = ScopedObject(scope, prototype())->getValueOrSetter(name, &attrs); if (!memberIndex.isNull()) { if (attrs.isAccessor()) { if (!memberIndex->as<FunctionObject>()) goto reject; } else if (!isExtensible() || !attrs.isWritable()) { goto reject; } } else if (!isExtensible()) { goto reject; } } cont: // Clause 5 if (!memberIndex.isNull() && attrs.isAccessor()) { Q_ASSERT(memberIndex->as<FunctionObject>()); Scope scope(engine); ScopedFunctionObject setter(scope, *memberIndex); ScopedCallData callData(scope, 1); callData->args[0] = value; callData->thisObject = this; setter->call(scope, callData); return !internalClass()->engine->hasException; } insertMember(name, value); return true; reject: // ### this should be removed once everything is ported to use Object::set() if (engine->current->strictMode) { QString message = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); engine->throwTypeError(message); } return false; }
void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *pd, PropertyAttributes *attrs) { Object *o = static_cast<Object *>(m); name->setM(0); *index = UINT_MAX; if (o->arrayData()) { if (!it->arrayIndex) it->arrayNode = o->sparseBegin(); // sparse arrays if (it->arrayNode) { while (it->arrayNode != o->sparseEnd()) { int k = it->arrayNode->key(); uint pidx = it->arrayNode->value; Heap::SparseArrayData *sa = o->d()->arrayData.cast<Heap::SparseArrayData>(); const Property *p = reinterpret_cast<const Property *>(sa->values.data() + pidx); it->arrayNode = it->arrayNode->nextNode(); PropertyAttributes a = sa->attrs ? sa->attrs[pidx] : Attr_Data; if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { it->arrayIndex = k + 1; *index = k; *attrs = a; pd->copy(p, a); return; } } it->arrayNode = 0; it->arrayIndex = UINT_MAX; } // dense arrays while (it->arrayIndex < o->d()->arrayData->values.size) { Heap::SimpleArrayData *sa = o->d()->arrayData.cast<Heap::SimpleArrayData>(); const Value &val = sa->data(it->arrayIndex); PropertyAttributes a = o->arrayData()->attributes(it->arrayIndex); ++it->arrayIndex; if (!val.isEmpty() && (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable())) { *index = it->arrayIndex - 1; *attrs = a; pd->value = val; return; } } } while (it->memberIndex < o->internalClass()->size) { Identifier *n = o->internalClass()->nameMap.at(it->memberIndex); if (!n) { // accessor properties have a dummy entry with n == 0 ++it->memberIndex; continue; } int idx = it->memberIndex; PropertyAttributes a = o->internalClass()->propertyData[it->memberIndex]; ++it->memberIndex; if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { name->setM(o->engine()->identifierTable->stringFromIdentifier(n)); *attrs = a; pd->value = *o->propertyData(idx); if (a.isAccessor()) pd->set = *o->propertyData(idx + SetterOffset); return; } } *attrs = PropertyAttributes(); }
bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *member, const Property *p, PropertyAttributes attrs) { // clause 5 if (attrs.isEmpty()) return true; Scope scope(engine); ScopedProperty current(scope); PropertyAttributes cattrs; if (member) { getProperty(index, current, &cattrs); cattrs = internalClass()->propertyData[index]; } else if (arrayData()) { arrayData()->getProperty(index, current, &cattrs); cattrs = arrayData()->attributes(index); } // clause 6 if (p->isSubset(attrs, current, cattrs)) return true; // clause 7 if (!cattrs.isConfigurable()) { if (attrs.isConfigurable()) goto reject; if (attrs.hasEnumerable() && attrs.isEnumerable() != cattrs.isEnumerable()) goto reject; } // clause 8 if (attrs.isGeneric() || current->value.isEmpty()) goto accept; // clause 9 if (cattrs.isData() != attrs.isData()) { // 9a if (!cattrs.isConfigurable()) goto reject; if (cattrs.isData()) { // 9b cattrs.setType(PropertyAttributes::Accessor); cattrs.clearWritable(); if (!member) { // need to convert the array and the slot initSparseArray(); Q_ASSERT(arrayData()); setArrayAttributes(index, cattrs); } current->setGetter(0); current->setSetter(0); } else { // 9c cattrs.setType(PropertyAttributes::Data); cattrs.setWritable(false); if (!member) { // need to convert the array and the slot setArrayAttributes(index, cattrs); } current->value = Primitive::undefinedValue(); } } else if (cattrs.isData() && attrs.isData()) { // clause 10 if (!cattrs.isConfigurable() && !cattrs.isWritable()) { if (attrs.isWritable() || !current->value.sameValue(p->value)) goto reject; } } else { // clause 10 Q_ASSERT(cattrs.isAccessor() && attrs.isAccessor()); if (!cattrs.isConfigurable()) { if (!p->value.isEmpty() && current->value.rawValue() != p->value.rawValue()) goto reject; if (!p->set.isEmpty() && current->set.rawValue() != p->set.rawValue()) goto reject; } } accept: current->merge(cattrs, p, attrs); if (member) { InternalClass::changeMember(this, member, cattrs); setProperty(index, current); } else { setArrayAttributes(index, cattrs); arrayData()->setProperty(scope.engine, index, current); } return true; reject: if (engine->current->strictMode) engine->throwTypeError(); return false; }
bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const StringRef member, const Property &p, PropertyAttributes attrs) { // clause 5 if (attrs.isEmpty()) return true; Property *current; PropertyAttributes cattrs; if (!member.isNull()) { current = propertyAt(index); cattrs = internalClass->propertyData[index]; } else { current = arrayData->getProperty(index); cattrs = arrayData->attributes(index); } // clause 6 if (p.isSubset(attrs, *current, cattrs)) return true; // clause 7 if (!cattrs.isConfigurable()) { if (attrs.isConfigurable()) goto reject; if (attrs.hasEnumerable() && attrs.isEnumerable() != cattrs.isEnumerable()) goto reject; } // clause 8 if (attrs.isGeneric() || current->value.isEmpty()) goto accept; // clause 9 if (cattrs.isData() != attrs.isData()) { // 9a if (!cattrs.isConfigurable()) goto reject; if (cattrs.isData()) { // 9b cattrs.setType(PropertyAttributes::Accessor); cattrs.clearWritable(); if (member.isNull()) { // need to convert the array and the slot initSparseArray(); setArrayAttributes(index, cattrs); current = arrayData->getProperty(index); } current->setGetter(0); current->setSetter(0); } else { // 9c cattrs.setType(PropertyAttributes::Data); cattrs.setWritable(false); if (member.isNull()) { // need to convert the array and the slot setArrayAttributes(index, cattrs); current = arrayData->getProperty(index); } current->value = Primitive::undefinedValue(); } } else if (cattrs.isData() && attrs.isData()) { // clause 10 if (!cattrs.isConfigurable() && !cattrs.isWritable()) { if (attrs.isWritable() || !current->value.sameValue(p.value)) goto reject; } } else { // clause 10 Q_ASSERT(cattrs.isAccessor() && attrs.isAccessor()); if (!cattrs.isConfigurable()) { if (!p.value.isEmpty() && current->value.val != p.value.val) goto reject; if (!p.set.isEmpty() && current->set.val != p.set.val) goto reject; } } accept: current->merge(cattrs, p, attrs); if (!member.isNull()) { InternalClass::changeMember(this, member.getPointer(), cattrs); } else { setArrayAttributes(index, cattrs); } if (cattrs.isAccessor()) hasAccessorProperty = 1; return true; reject: if (ctx->strictMode) ctx->throwTypeError(); return false; }
// Section 8.12.9 bool Object::__defineOwnProperty__(ExecutionContext *ctx, const StringRef name, const Property &p, PropertyAttributes attrs) { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) return __defineOwnProperty__(ctx, idx, p, attrs); name->makeIdentifier(); Scope scope(ctx); Property *current; PropertyAttributes *cattrs; uint memberIndex; if (isArrayObject() && name->equals(ctx->engine->id_length)) { assert(ArrayObject::LengthPropertyIndex == internalClass->find(ctx->engine->id_length)); Property *lp = propertyAt(ArrayObject::LengthPropertyIndex); cattrs = internalClass->propertyData.constData() + ArrayObject::LengthPropertyIndex; if (attrs.isEmpty() || p.isSubset(attrs, *lp, *cattrs)) return true; if (!cattrs->isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable()) goto reject; bool succeeded = true; if (attrs.type() == PropertyAttributes::Data) { bool ok; uint l = p.value.asArrayLength(&ok); if (!ok) { ScopedValue v(scope, p.value); ctx->throwRangeError(v); return false; } succeeded = setArrayLength(l); } if (attrs.hasWritable() && !attrs.isWritable()) cattrs->setWritable(false); if (!succeeded) goto reject; if (attrs.isAccessor()) hasAccessorProperty = 1; return true; } // Clause 1 memberIndex = internalClass->find(name.getPointer()); current = (memberIndex < UINT_MAX) ? propertyAt(memberIndex) : 0; cattrs = internalClass->propertyData.constData() + memberIndex; if (!current) { // clause 3 if (!extensible) goto reject; // clause 4 Property pd; pd.copy(p, attrs); pd.fullyPopulated(&attrs); insertMember(name, pd, attrs); return true; } return __defineOwnProperty__(ctx, memberIndex, name, p, attrs); reject: if (ctx->strictMode) ctx->throwTypeError(); return false; }
void Object::internalPutIndexed(uint index, const ValueRef value) { if (internalClass->engine->hasException) return; PropertyAttributes attrs; Property *pd = arrayData->getProperty(index); if (pd) attrs = arrayData->attributes(index); if (!pd && isStringObject()) { pd = static_cast<StringObject *>(this)->getIndex(index); if (pd) // not writable goto reject; } // clause 1 if (pd) { if (attrs.isAccessor()) { if (pd->setter()) goto cont; goto reject; } else if (!attrs.isWritable()) goto reject; else pd->value = *value; return; } else if (!prototype()) { if (!extensible) goto reject; } else { // clause 4 if ((pd = prototype()->__getPropertyDescriptor__(index, &attrs))) { if (attrs.isAccessor()) { if (!pd->setter()) goto reject; } else if (!extensible || !attrs.isWritable()) { goto reject; } } else if (!extensible) { goto reject; } } cont: // Clause 5 if (pd && attrs.isAccessor()) { assert(pd->setter() != 0); Scope scope(engine()); ScopedCallData callData(scope, 1); callData->args[0] = *value; callData->thisObject = this; pd->setter()->call(callData); return; } arraySet(index, value); return; reject: if (engine()->currentContext()->strictMode) engine()->currentContext()->throwTypeError(); }
// Section 8.12.5 void Object::internalPut(const StringRef name, const ValueRef value) { if (internalClass->engine->hasException) return; uint idx = name->asArrayIndex(); if (idx != UINT_MAX) return putIndexed(idx, value); name->makeIdentifier(); uint member = internalClass->find(name.getPointer()); Property *pd = 0; PropertyAttributes attrs; if (member < UINT_MAX) { pd = propertyAt(member); attrs = internalClass->propertyData[member]; } // clause 1 if (pd) { if (attrs.isAccessor()) { if (pd->setter()) goto cont; goto reject; } else if (!attrs.isWritable()) goto reject; else if (isArrayObject() && name->equals(engine()->id_length)) { bool ok; uint l = value->asArrayLength(&ok); if (!ok) { engine()->currentContext()->throwRangeError(value); return; } ok = setArrayLength(l); if (!ok) goto reject; } else { pd->value = *value; } return; } else if (!prototype()) { if (!extensible) goto reject; } else { // clause 4 if ((pd = prototype()->__getPropertyDescriptor__(name, &attrs))) { if (attrs.isAccessor()) { if (!pd->setter()) goto reject; } else if (!extensible || !attrs.isWritable()) { goto reject; } } else if (!extensible) { goto reject; } } cont: // Clause 5 if (pd && attrs.isAccessor()) { assert(pd->setter() != 0); Scope scope(engine()); ScopedCallData callData(scope, 1); callData->args[0] = *value; callData->thisObject = this; pd->setter()->call(callData); return; } insertMember(name, value); return; reject: if (engine()->currentContext()->strictMode) { QString message = QStringLiteral("Cannot assign to read-only property \""); message += name->toQString(); message += QLatin1Char('\"'); engine()->currentContext()->throwTypeError(message); } }