EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState* exec) { // 1. If Type(O) is not Object throw a TypeError exception. JSValue obj = exec->argument(0); if (!obj.isObject()) return throwVMError(exec, createTypeError(exec, ASCIILiteral("Object.isFrozen can only be called on Objects."))); JSObject* object = asObject(obj); if (isJSFinalObject(object)) return JSValue::encode(jsBoolean(object->isFrozen(exec->vm()))); // 2. For each named own property name P of O, PropertyNameArray properties(exec); object->methodTable()->getOwnPropertyNames(object, exec, properties, IncludeDontEnumProperties); PropertyNameArray::const_iterator end = properties.end(); for (PropertyNameArray::const_iterator iter = properties.begin(); iter != end; ++iter) { // a. Let desc be the result of calling the [[GetOwnProperty]] internal method of O with P. PropertyDescriptor desc; if (!object->methodTable()->getOwnPropertyDescriptor(object, exec, *iter, desc)) continue; // b. If IsDataDescriptor(desc) is true then // i. If desc.[[Writable]] is true, return false. c. If desc.[[Configurable]] is true, then return false. if ((desc.isDataDescriptor() && desc.writable()) || desc.configurable()) return JSValue::encode(jsBoolean(false)); } // 3. If the [[Extensible]] internal property of O is false, then return true. // 4. Otherwise, return false. return JSValue::encode(jsBoolean(!object->isExtensible())); }
static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* properties) { PropertyNameArray propertyNames(exec); asObject(properties)->methodTable()->getOwnPropertyNames(asObject(properties), exec, propertyNames, ExcludeDontEnumProperties); size_t numProperties = propertyNames.size(); Vector<PropertyDescriptor> descriptors; MarkedArgumentBuffer markBuffer; for (size_t i = 0; i < numProperties; i++) { PropertySlot slot; JSValue prop = properties->get(exec, propertyNames[i]); if (exec->hadException()) return jsNull(); PropertyDescriptor descriptor; if (!toPropertyDescriptor(exec, prop, descriptor)) return jsNull(); descriptors.append(descriptor); // Ensure we mark all the values that we're accumulating if (descriptor.isDataDescriptor() && descriptor.value()) markBuffer.append(descriptor.value()); if (descriptor.isAccessorDescriptor()) { if (descriptor.getter()) markBuffer.append(descriptor.getter()); if (descriptor.setter()) markBuffer.append(descriptor.setter()); } } for (size_t i = 0; i < numProperties; i++) { object->methodTable()->defineOwnProperty(object, exec, propertyNames[i], descriptors[i], true); if (exec->hadException()) return jsNull(); } return object; }
static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, JSValue oldValue) { if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) { target->putWithAttributes(exec, propertyName, descriptor.value() ? descriptor.value() : oldValue, attributes & ~(Getter | Setter)); return true; } attributes &= ~ReadOnly; if (descriptor.getter() && descriptor.getter().isObject()) target->defineGetter(exec, propertyName, asObject(descriptor.getter()), attributes); if (exec->hadException()) return false; if (descriptor.setter() && descriptor.setter().isObject()) target->defineSetter(exec, propertyName, asObject(descriptor.setter()), attributes); return !exec->hadException(); }
unsigned PropertyDescriptor::attributesWithOverride(const PropertyDescriptor& other) const { unsigned mismatch = other.m_attributes ^ m_attributes; unsigned sharedSeen = other.m_seenAttributes & m_seenAttributes; unsigned newAttributes = m_attributes & defaultAttributes; if (sharedSeen & WritablePresent && mismatch & ReadOnly) newAttributes ^= ReadOnly; if (sharedSeen & ConfigurablePresent && mismatch & DontDelete) newAttributes ^= DontDelete; if (sharedSeen & EnumerablePresent && mismatch & DontEnum) newAttributes ^= DontEnum; if (isAccessorDescriptor() && other.isDataDescriptor()) newAttributes |= ReadOnly; return newAttributes; }
EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState* exec) { // 1. If Type(O) is not Object throw a TypeError exception. JSValue obj = exec->argument(0); if (!obj.isObject()) return throwVMError(exec, createTypeError(exec, ASCIILiteral("Object.freeze can only be called on Objects."))); JSObject* object = asObject(obj); if (isJSFinalObject(object) && !hasIndexedProperties(object->structure()->indexingType())) { object->freeze(exec->vm()); return JSValue::encode(obj); } // 2. For each named own property name P of O, PropertyNameArray properties(exec); object->methodTable()->getOwnPropertyNames(object, exec, properties, IncludeDontEnumProperties); PropertyNameArray::const_iterator end = properties.end(); for (PropertyNameArray::const_iterator iter = properties.begin(); iter != end; ++iter) { // a. Let desc be the result of calling the [[GetOwnProperty]] internal method of O with P. PropertyDescriptor desc; if (!object->methodTable()->getOwnPropertyDescriptor(object, exec, *iter, desc)) continue; // b. If IsDataDescriptor(desc) is true, then // i. If desc.[[Writable]] is true, set desc.[[Writable]] to false. if (desc.isDataDescriptor()) desc.setWritable(false); // c. If desc.[[Configurable]] is true, set desc.[[Configurable]] to false. desc.setConfigurable(false); // d. Call the [[DefineOwnProperty]] internal method of O with P, desc, and true as arguments. object->methodTable()->defineOwnProperty(object, exec, *iter, desc, true); if (exec->hadException()) return JSValue::encode(obj); } // 3. Set the [[Extensible]] internal property of O to false. object->preventExtensions(exec->vm()); // 4. Return O. return JSValue::encode(obj); }
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; }