void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction) { JSValue* object = getDirect(propertyName); if (object && object->isGetterSetter()) { ASSERT(m_structureID->hasGetterSetterProperties()); asGetterSetter(object)->setSetter(setterFunction); return; } PutPropertySlot slot; GetterSetter* getterSetter = new (exec) GetterSetter; putDirect(propertyName, getterSetter, None, true, slot); // putDirect will change our StructureID if we add a new property. For // getters and setters, though, we also need to change our StructureID // if we override an existing non-getter or non-setter. if (slot.type() != PutPropertySlot::NewProperty) { if (!m_structureID->isDictionary()) { RefPtr<StructureID> structureID = StructureID::getterSetterTransition(m_structureID); setStructureID(structureID.release()); } } m_structureID->setHasGetterSetterProperties(true); getterSetter->setSetter(setterFunction); }
void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes) { JSValue object = getDirect(propertyName); if (object && object.isGetterSetter()) { ASSERT(m_structure->hasGetterSetterProperties()); asGetterSetter(object)->setSetter(setterFunction); return; } PutPropertySlot slot; GetterSetter* getterSetter = new (exec) GetterSetter(exec); putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot); // putDirect will change our Structure if we add a new property. For // getters and setters, though, we also need to change our Structure // if we override an existing non-getter or non-setter. if (slot.type() != PutPropertySlot::NewProperty) { if (!m_structure->isDictionary()) { RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure); setStructure(structure.release()); } } m_structure->setHasGetterSetterProperties(true); getterSetter->setSetter(setterFunction); }
void SparseArrayEntry::put(ExecState* exec, JSValue thisValue, SparseArrayValueMap* map, JSValue value, bool shouldThrow) { if (!(attributes & Accessor)) { if (attributes & ReadOnly) { if (shouldThrow) throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return; } set(exec->vm(), map, value); return; } JSValue accessor = Base::get(); ASSERT(accessor.isGetterSetter()); JSObject* setter = asGetterSetter(accessor)->setter(); if (!setter) { if (shouldThrow) throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return; } CallData callData; CallType callType = setter->methodTable()->getCallData(setter, callData); MarkedArgumentBuffer args; args.append(value); if (thisValue.isObject()) thisValue = asObject(thisValue)->methodTable()->toThisObject(asObject(thisValue), exec); call(exec, setter, callType, callData, thisValue, args); }
void PropertyDescriptor::setDescriptor(JSValue value, unsigned attributes) { ASSERT(value); ASSERT(value.isGetterSetter() == !!(attributes & Accessor)); m_attributes = attributes; if (value.isGetterSetter()) { m_attributes &= ~ReadOnly; // FIXME: we should be able to ASSERT this! GetterSetter* accessor = asGetterSetter(value); m_getter = accessor->getter() ? accessor->getter() : jsUndefined(); m_setter = accessor->setter() ? accessor->setter() : jsUndefined(); m_seenAttributes = EnumerablePresent | ConfigurablePresent; } else { m_value = value; m_seenAttributes = EnumerablePresent | ConfigurablePresent | WritablePresent; } }
// ECMA 8.7.2 bool JSValue::putToPrimitive(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); if (Optional<uint32_t> index = parseIndex(propertyName)) return putToPrimitiveByIndex(exec, index.value(), value, slot.isStrictMode()); // Check if there are any setters or getters in the prototype chain JSObject* obj = synthesizePrototype(exec); if (UNLIKELY(!obj)) return false; JSValue prototype; if (propertyName != exec->propertyNames().underscoreProto) { for (; !obj->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) { prototype = obj->getPrototypeDirect(); if (prototype.isNull()) { if (slot.isStrictMode()) throwTypeError(exec, scope, StrictModeReadonlyPropertyWriteError); return false; } } } for (; ; obj = asObject(prototype)) { unsigned attributes; PropertyOffset offset = obj->structure()->get(vm, propertyName, attributes); if (offset != invalidOffset) { if (attributes & ReadOnly) { if (slot.isStrictMode()) throwTypeError(exec, scope, StrictModeReadonlyPropertyWriteError); return false; } JSValue gs = obj->getDirect(offset); if (gs.isGetterSetter()) return callSetter(exec, *this, gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode); if (gs.isCustomGetterSetter()) return callCustomSetter(exec, gs, attributes & CustomAccessor, obj, slot.thisValue(), value); // If there's an existing property on the object or one of its // prototypes it should be replaced, so break here. break; } prototype = obj->getPrototype(vm, exec); if (vm.exception()) return false; if (prototype.isNull()) break; } if (slot.isStrictMode()) throwTypeError(exec, scope, StrictModeReadonlyPropertyWriteError); return false; }
JSValue SparseArrayEntry::get(ExecState* exec, JSObject* array) const { JSValue value = Base::get(); ASSERT(value); if (LIKELY(!value.isGetterSetter())) return value; return callGetter(exec, array, jsCast<GetterSetter*>(value)); }
void SparseArrayEntry::get(JSObject* thisObject, PropertySlot& slot) const { JSValue value = Base::get(); ASSERT(value); if (LIKELY(!value.isGetterSetter())) { slot.setValue(thisObject, attributes, value); return; } slot.setGetterSlot(thisObject, attributes, jsCast<GetterSetter*>(value)); }
JSValue SparseArrayEntry::get(ExecState* exec, JSObject* array) const { JSValue result = Base::get(); ASSERT(result); if (LIKELY(!result.isGetterSetter())) return result; JSObject* getter = asGetterSetter(result)->getter(); if (!getter) return jsUndefined(); CallData callData; CallType callType = getter->methodTable()->getCallData(getter, callData); return call(exec, getter, callType, callData, array->methodTable()->toThisObject(array, exec), exec->emptyList()); }
void SparseArrayEntry::get(PropertySlot& slot) const { JSValue value = Base::get(); ASSERT(value); if (LIKELY(!value.isGetterSetter())) { slot.setValue(value); return; } JSObject* getter = asGetterSetter(value)->getter(); if (!getter) { slot.setUndefined(); return; } slot.setGetterSlot(getter); }
JSValue* JSObject::lookupSetter(ExecState*, const Identifier& propertyName) { JSObject* object = this; while (true) { JSValue* value = object->getDirect(propertyName); if (value) { if (!value->isGetterSetter()) return jsUndefined(); JSObject* functionObject = asGetterSetter(value)->setter(); if (!functionObject) return jsUndefined(); return functionObject; } if (!object->prototype() || !object->prototype()->isObject()) return jsUndefined(); object = asObject(object->prototype()); } }
static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName) { JSValue function = object->get(exec, propertyName); CallData callData; CallType callType = function.getCallData(callData); if (callType == CallTypeNone) return exec->exception(); // Prevent "toString" and "valueOf" from observing execution if an exception // is pending. if (exec->hadException()) return exec->exception(); JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList()); ASSERT(!result.isGetterSetter()); if (exec->hadException()) return exec->exception(); if (result.isObject()) return JSValue(); return result; }
void PropertyDescriptor::setDescriptor(JSValue value, unsigned attributes) { ASSERT(value); // We need to mask off the PropertyAttribute::CustomValue bit because // PropertyDescriptor::attributesEqual() does an equivalent test on // m_attributes, and a property that has a CustomValue should be indistinguishable // from a property that has a normal value as far as JS code is concerned. // PropertyAttribute does not need knowledge of the underlying implementation // actually being a CustomValue. So, we'll just mask it off up front here. m_attributes = attributes & ~PropertyAttribute::CustomValue; if (value.isGetterSetter()) { m_attributes &= ~PropertyAttribute::ReadOnly; // FIXME: we should be able to ASSERT this! GetterSetter* accessor = jsCast<GetterSetter*>(value); m_getter = !accessor->isGetterNull() ? accessor->getter() : jsUndefined(); m_setter = !accessor->isSetterNull() ? accessor->setter() : jsUndefined(); m_seenAttributes = EnumerablePresent | ConfigurablePresent; } else { m_value = value; m_seenAttributes = EnumerablePresent | ConfigurablePresent | WritablePresent; } }