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(); }
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; }
// Section 8.12.9 bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const Property *p, PropertyAttributes attrs) { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) return __defineOwnProperty__(engine, idx, p, attrs); Scope scope(engine); name->makeIdentifier(); uint memberIndex; if (isArrayObject() && name->equals(engine->id_length())) { Q_ASSERT(Heap::ArrayObject::LengthPropertyIndex == internalClass()->find(engine->id_length())); ScopedProperty lp(scope); PropertyAttributes cattrs; getProperty(Heap::ArrayObject::LengthPropertyIndex, lp, &cattrs); 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); engine->throwRangeError(v); return false; } succeeded = setArrayLength(l); } if (attrs.hasWritable() && !attrs.isWritable()) { cattrs.setWritable(false); InternalClass::changeMember(this, engine->id_length(), cattrs); } if (!succeeded) goto reject; return true; } // Clause 1 memberIndex = internalClass()->find(name->identifier()); if (memberIndex == UINT_MAX) { // clause 3 if (!isExtensible()) goto reject; // clause 4 ScopedProperty pd(scope); pd->copy(p, attrs); pd->fullyPopulated(&attrs); insertMember(name, pd, attrs); return true; } return __defineOwnProperty__(engine, memberIndex, name, p, attrs); reject: if (engine->current->strictMode) engine->throwTypeError(); return false; }
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; }
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); } }