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; }
EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec) { if (!exec->argument(0).isObject()) return throwVMError(exec, createTypeError(exec, ASCIILiteral("Requested property descriptor of a value that is not an object."))); String propertyName = exec->argument(1).toString(exec)->value(exec); if (exec->hadException()) return JSValue::encode(jsNull()); JSObject* object = asObject(exec->argument(0)); PropertyDescriptor descriptor; if (!object->methodTable()->getOwnPropertyDescriptor(object, exec, Identifier(exec, propertyName), descriptor)) return JSValue::encode(jsUndefined()); if (exec->hadException()) return JSValue::encode(jsUndefined()); JSObject* description = constructEmptyObject(exec); if (!descriptor.isAccessorDescriptor()) { description->putDirect(exec->vm(), exec->propertyNames().value, descriptor.value() ? descriptor.value() : jsUndefined(), 0); description->putDirect(exec->vm(), exec->propertyNames().writable, jsBoolean(descriptor.writable()), 0); } else { ASSERT(descriptor.getter()); ASSERT(descriptor.setter()); description->putDirect(exec->vm(), exec->propertyNames().get, descriptor.getter(), 0); description->putDirect(exec->vm(), exec->propertyNames().set, descriptor.setter(), 0); } description->putDirect(exec->vm(), exec->propertyNames().enumerable, jsBoolean(descriptor.enumerable()), 0); description->putDirect(exec->vm(), exec->propertyNames().configurable, jsBoolean(descriptor.configurable()), 0); return JSValue::encode(description); }
JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec, JSObject*, JSValue, const ArgList& args) { if (!args.at(0).isObject()) return throwError(exec, TypeError, "Requested property descriptor of a value that is not an object."); UString propertyName = args.at(1).toString(exec); if (exec->hadException()) return jsNull(); JSObject* object = asObject(args.at(0)); PropertyDescriptor descriptor; if (!object->getOwnPropertyDescriptor(exec, Identifier(exec, propertyName), descriptor)) return jsUndefined(); if (exec->hadException()) return jsUndefined(); JSObject* description = constructEmptyObject(exec); if (!descriptor.isAccessorDescriptor()) { description->putDirect(exec->propertyNames().value, descriptor.value() ? descriptor.value() : jsUndefined(), 0); description->putDirect(exec->propertyNames().writable, jsBoolean(descriptor.writable()), 0); } else { description->putDirect(exec->propertyNames().get, descriptor.getter() ? descriptor.getter() : jsUndefined(), 0); description->putDirect(exec->propertyNames().set, descriptor.setter() ? descriptor.setter() : jsUndefined(), 0); } description->putDirect(exec->propertyNames().enumerable, jsBoolean(descriptor.enumerable()), 0); description->putDirect(exec->propertyNames().configurable, jsBoolean(descriptor.configurable()), 0); return description; }
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(); }
EncodedJSValue JSC_HOST_CALL objectProtoFuncLookupGetter(ExecState* exec) { JSObject* thisObject = exec->hostThisValue().toObject(exec); if (exec->hadException()) return JSValue::encode(jsUndefined()); PropertyDescriptor descriptor; if (thisObject->getPropertyDescriptor(exec, Identifier(exec, exec->argument(0).toString(exec)->value(exec)), descriptor) && descriptor.getterPresent()) return JSValue::encode(descriptor.getter()); return JSValue::encode(jsUndefined()); }
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; }