NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location) { if (JSObject* getterFunction = asGetterSetter(*location)->getter()) slot.setGetterSlot(getterFunction); else slot.setUndefined(); }
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 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 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); }
// ECMA 8.6.2.2 void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { ASSERT(value); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); if (propertyName == exec->propertyNames().underscoreProto) { // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla. if (!value.isObject() && !value.isNull()) return; if (!setPrototypeWithCycleCheck(value)) throwError(exec, createError(exec, "cyclic __proto__ value")); return; } // Check if there are any setters or getters in the prototype chain JSValue prototype; for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) { prototype = obj->prototype(); if (prototype.isNull()) { putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot); return; } } unsigned attributes; JSCell* specificValue; if ((m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly) return; for (JSObject* obj = this; ; obj = asObject(prototype)) { if (JSValue gs = obj->getDirect(propertyName)) { if (gs.isGetterSetter()) { JSObject* setterFunc = asGetterSetter(gs)->setter(); if (!setterFunc) { throwSetterError(exec); return; } CallData callData; CallType callType = setterFunc->getCallData(callData); MarkedArgumentBuffer args; args.append(value); call(exec, setterFunc, callType, callData, this, args); return; } // If there's an existing property on the object or one of its // prototypes it should be replaced, so break here. break; } prototype = obj->prototype(); if (prototype.isNull()) break; } putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot); return; }
NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location) { if (JSObject* getterFunction = asGetterSetter(*location)->getter()) { if (!structure()->isDictionary()) slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location)); else slot.setGetterSlot(getterFunction); } else slot.setUndefined(); }
EncodedJSValue DFG_OPERATION operationCallGetter(ExecState* exec, JSCell* base, JSCell* value) { JSGlobalData* globalData = &exec->globalData(); NativeCallFrameTracer tracer(globalData, exec); GetterSetter* getterSetter = asGetterSetter(value); JSObject* getter = getterSetter->getter(); if (!getter) return JSValue::encode(jsUndefined()); CallData callData; CallType callType = getter->methodTable()->getCallData(getter, callData); return JSValue::encode(call(exec, getter, callType, callData, asObject(base), ArgList())); }
void PropertyDescriptor::setDescriptor(JSValue value, unsigned attributes) { ASSERT(value); m_attributes = attributes; if (attributes & (Getter | Setter)) { GetterSetter* accessor = asGetterSetter(value); m_getter = accessor->getter(); m_setter = accessor->setter(); ASSERT(m_getter || m_setter); m_seenAttributes = EnumerablePresent | ConfigurablePresent; m_attributes &= ~ReadOnly; } else { m_value = value; m_seenAttributes = EnumerablePresent | ConfigurablePresent | WritablePresent; } }
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()); }
JSValue JSObject::lookupSetter(ExecState*, const Identifier& propertyName) { JSObject* object = this; while (true) { if (JSValue value = object->getDirect(propertyName)) { 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()); } }
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; } }
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); }
bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException) { // If we have a new property we can just put it on normally PropertyDescriptor current; if (!getOwnPropertyDescriptor(exec, propertyName, current)) return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), jsUndefined()); if (descriptor.isEmpty()) return true; if (current.equalTo(descriptor)) return true; // Filter out invalid changes if (!current.configurable()) { if (descriptor.configurable()) { if (throwException) throwError(exec, TypeError, "Attempting to configurable attribute of unconfigurable property."); return false; } if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) { if (throwException) throwError(exec, TypeError, "Attempting to change enumerable attribute of unconfigurable property."); return false; } } // A generic descriptor is simply changing the attributes of an existing property if (descriptor.isGenericDescriptor()) { if (!current.attributesEqual(descriptor)) { deleteProperty(exec, propertyName); putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value()); } return true; } // Changing between a normal property or an accessor property if (descriptor.isDataDescriptor() != current.isDataDescriptor()) { if (!current.configurable()) { if (throwException) throwError(exec, TypeError, "Attempting to change access mechanism for an unconfigurable property."); return false; } deleteProperty(exec, propertyName); return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value() ? current.value() : jsUndefined()); } // Changing the value and attributes of an existing property if (descriptor.isDataDescriptor()) { if (!current.configurable()) { if (!current.writable() && descriptor.writable()) { if (throwException) throwError(exec, TypeError, "Attempting to change writable attribute of unconfigurable property."); return false; } if (!current.writable()) { if (descriptor.value() || !JSValue::strictEqual(current.value(), descriptor.value())) { if (throwException) throwError(exec, TypeError, "Attempting to change value of a readonly property."); return false; } } } else if (current.attributesEqual(descriptor)) { if (!descriptor.value()) return true; PutPropertySlot slot; put(exec, propertyName, descriptor.value(), slot); if (exec->hadException()) return false; return true; } deleteProperty(exec, propertyName); return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value()); } // Changing the accessor functions of an existing accessor property ASSERT(descriptor.isAccessorDescriptor()); if (!current.configurable()) { if (descriptor.setterPresent() && !(current.setter() && JSValue::strictEqual(current.setter(), descriptor.setter()))) { if (throwException) throwError(exec, TypeError, "Attempting to change the setter of an unconfigurable property."); return false; } if (descriptor.getterPresent() && !(current.getter() && JSValue::strictEqual(current.getter(), descriptor.getter()))) { if (throwException) throwError(exec, TypeError, "Attempting to change the getter of an unconfigurable property."); return false; } } JSValue accessor = getDirect(propertyName); if (!accessor) return false; GetterSetter* getterSetter = asGetterSetter(accessor); if (current.attributesEqual(descriptor)) { if (descriptor.setter()) getterSetter->setSetter(asObject(descriptor.setter())); if (descriptor.getter()) getterSetter->setGetter(asObject(descriptor.getter())); return true; } deleteProperty(exec, propertyName); unsigned attrs = current.attributesWithOverride(descriptor); if (descriptor.setter()) attrs |= Setter; if (descriptor.getter()) attrs |= Getter; putDirect(propertyName, getterSetter, attrs); return true; }
// ECMA 8.6.2.2 void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue* value, PutPropertySlot& slot) { ASSERT(value); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); if (propertyName == exec->propertyNames().underscoreProto) { JSObject* proto = value->getObject(); // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla. if (!proto && !value->isNull()) return; while (proto) { if (proto == this) { throwError(exec, GeneralError, "cyclic __proto__ value"); return; } proto = proto->prototype() ? proto->prototype()->getObject() : 0; } setPrototype(value); return; } // Check if there are any setters or getters in the prototype chain JSValue* prototype; for (JSObject* obj = this; !obj->structureID()->hasGetterSetterProperties(); obj = asObject(prototype)) { prototype = obj->prototype(); if (prototype->isNull()) { putDirect(propertyName, value, 0, true, slot); return; } } unsigned attributes; if ((m_structureID->get(propertyName, attributes) != WTF::notFound) && attributes & ReadOnly) return; for (JSObject* obj = this; ; obj = asObject(prototype)) { if (JSValue* gs = obj->getDirect(propertyName)) { if (gs->isGetterSetter()) { JSObject* setterFunc = asGetterSetter(gs)->setter(); if (!setterFunc) { throwSetterError(exec); return; } CallData callData; CallType callType = setterFunc->getCallData(callData); ArgList args; args.append(value); call(exec, setterFunc, callType, callData, this, args); return; } // If there's an existing property on the object or one of its // prototypes it should be replaced, so break here. break; } prototype = obj->prototype(); if (prototype->isNull()) break; } putDirect(propertyName, value, 0, true, slot); return; }