1/* 2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) 3 * Copyright (C) 2008 Apple Inc. All rights reserved. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 * 19 */ 20 21#include "config.h" 22#include "ObjectConstructor.h" 23 24#include "Error.h" 25#include "JSFunction.h" 26#include "JSArray.h" 27#include "JSGlobalObject.h" 28#include "ObjectPrototype.h" 29#include "PropertyDescriptor.h" 30#include "PropertyNameArray.h" 31#include "PrototypeFunction.h" 32 33namespace JSC { 34 35ASSERT_CLASS_FITS_IN_CELL(ObjectConstructor); 36 37static JSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState*, JSObject*, JSValue, const ArgList&); 38static JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState*, JSObject*, JSValue, const ArgList&); 39static JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState*, JSObject*, JSValue, const ArgList&); 40static JSValue JSC_HOST_CALL objectConstructorKeys(ExecState*, JSObject*, JSValue, const ArgList&); 41static JSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState*, JSObject*, JSValue, const ArgList&); 42static JSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState*, JSObject*, JSValue, const ArgList&); 43static JSValue JSC_HOST_CALL objectConstructorCreate(ExecState*, JSObject*, JSValue, const ArgList&); 44 45ObjectConstructor::ObjectConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, ObjectPrototype* objectPrototype, Structure* prototypeFunctionStructure) 46: InternalFunction(&exec->globalData(), structure, Identifier(exec, "Object")) 47{ 48 // ECMA 15.2.3.1 49 putDirectWithoutTransition(exec->propertyNames().prototype, objectPrototype, DontEnum | DontDelete | ReadOnly); 50 51 // no. of arguments for constructor 52 putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 1), ReadOnly | DontEnum | DontDelete); 53 54 putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().getPrototypeOf, objectConstructorGetPrototypeOf), DontEnum); 55 putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().getOwnPropertyDescriptor, objectConstructorGetOwnPropertyDescriptor), DontEnum); 56 putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().getOwnPropertyNames, objectConstructorGetOwnPropertyNames), DontEnum); 57 putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().keys, objectConstructorKeys), DontEnum); 58 putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 3, exec->propertyNames().defineProperty, objectConstructorDefineProperty), DontEnum); 59 putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().defineProperties, objectConstructorDefineProperties), DontEnum); 60 putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().create, objectConstructorCreate), DontEnum); 61} 62 63// ECMA 15.2.2 64static ALWAYS_INLINE JSObject* constructObject(ExecState* exec, const ArgList& args) 65{ 66 JSValue arg = args.at(0); 67 if (arg.isUndefinedOrNull()) 68 return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure()); 69 return arg.toObject(exec); 70} 71 72static JSObject* constructWithObjectConstructor(ExecState* exec, JSObject*, const ArgList& args) 73{ 74 return constructObject(exec, args); 75} 76 77ConstructType ObjectConstructor::getConstructData(ConstructData& constructData) 78{ 79 constructData.native.function = constructWithObjectConstructor; 80 return ConstructTypeHost; 81} 82 83static JSValue JSC_HOST_CALL callObjectConstructor(ExecState* exec, JSObject*, JSValue, const ArgList& args) 84{ 85 return constructObject(exec, args); 86} 87 88CallType ObjectConstructor::getCallData(CallData& callData) 89{ 90 callData.native.function = callObjectConstructor; 91 return CallTypeHost; 92} 93 94JSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec, JSObject*, JSValue, const ArgList& args) 95{ 96 if (!args.at(0).isObject()) 97 return throwError(exec, TypeError, "Requested prototype of a value that is not an object."); 98 return asObject(args.at(0))->prototype(); 99} 100 101JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec, JSObject*, JSValue, const ArgList& args) 102{ 103 if (!args.at(0).isObject()) 104 return throwError(exec, TypeError, "Requested property descriptor of a value that is not an object."); 105 UString propertyName = args.at(1).toString(exec); 106 if (exec->hadException()) 107 return jsNull(); 108 JSObject* object = asObject(args.at(0)); 109 PropertyDescriptor descriptor; 110 if (!object->getOwnPropertyDescriptor(exec, Identifier(exec, propertyName), descriptor)) 111 return jsUndefined(); 112 if (exec->hadException()) 113 return jsUndefined(); 114 115 JSObject* description = constructEmptyObject(exec); 116 if (!descriptor.isAccessorDescriptor()) { 117 description->putDirect(exec->propertyNames().value, descriptor.value() ? descriptor.value() : jsUndefined(), 0); 118 description->putDirect(exec->propertyNames().writable, jsBoolean(descriptor.writable()), 0); 119 } else { 120 description->putDirect(exec->propertyNames().get, descriptor.getter() ? descriptor.getter() : jsUndefined(), 0); 121 description->putDirect(exec->propertyNames().set, descriptor.setter() ? descriptor.setter() : jsUndefined(), 0); 122 } 123 124 description->putDirect(exec->propertyNames().enumerable, jsBoolean(descriptor.enumerable()), 0); 125 description->putDirect(exec->propertyNames().configurable, jsBoolean(descriptor.configurable()), 0); 126 127 return description; 128} 129 130// FIXME: Use the enumeration cache. 131JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exec, JSObject*, JSValue, const ArgList& args) 132{ 133 if (!args.at(0).isObject()) 134 return throwError(exec, TypeError, "Requested property names of a value that is not an object."); 135 PropertyNameArray properties(exec); 136 asObject(args.at(0))->getOwnPropertyNames(exec, properties, IncludeDontEnumProperties); 137 JSArray* names = constructEmptyArray(exec); 138 size_t numProperties = properties.size(); 139 for (size_t i = 0; i < numProperties; i++) 140 names->push(exec, jsOwnedString(exec, properties[i].ustring())); 141 return names; 142} 143 144// FIXME: Use the enumeration cache. 145JSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec, JSObject*, JSValue, const ArgList& args) 146{ 147 if (!args.at(0).isObject()) 148 return throwError(exec, TypeError, "Requested keys of a value that is not an object."); 149 PropertyNameArray properties(exec); 150 asObject(args.at(0))->getOwnPropertyNames(exec, properties); 151 JSArray* keys = constructEmptyArray(exec); 152 size_t numProperties = properties.size(); 153 for (size_t i = 0; i < numProperties; i++) 154 keys->push(exec, jsOwnedString(exec, properties[i].ustring())); 155 return keys; 156} 157 158// ES5 8.10.5 ToPropertyDescriptor 159static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc) 160{ 161 if (!in.isObject()) { 162 throwError(exec, TypeError, "Property description must be an object."); 163 return false; 164 } 165 JSObject* description = asObject(in); 166 167 PropertySlot enumerableSlot(description); 168 if (description->getPropertySlot(exec, exec->propertyNames().enumerable, enumerableSlot)) { 169 desc.setEnumerable(enumerableSlot.getValue(exec, exec->propertyNames().enumerable).toBoolean(exec)); 170 if (exec->hadException()) 171 return false; 172 } 173 174 PropertySlot configurableSlot(description); 175 if (description->getPropertySlot(exec, exec->propertyNames().configurable, configurableSlot)) { 176 desc.setConfigurable(configurableSlot.getValue(exec, exec->propertyNames().configurable).toBoolean(exec)); 177 if (exec->hadException()) 178 return false; 179 } 180 181 JSValue value; 182 PropertySlot valueSlot(description); 183 if (description->getPropertySlot(exec, exec->propertyNames().value, valueSlot)) { 184 desc.setValue(valueSlot.getValue(exec, exec->propertyNames().value)); 185 if (exec->hadException()) 186 return false; 187 } 188 189 PropertySlot writableSlot(description); 190 if (description->getPropertySlot(exec, exec->propertyNames().writable, writableSlot)) { 191 desc.setWritable(writableSlot.getValue(exec, exec->propertyNames().writable).toBoolean(exec)); 192 if (exec->hadException()) 193 return false; 194 } 195 196 PropertySlot getSlot(description); 197 if (description->getPropertySlot(exec, exec->propertyNames().get, getSlot)) { 198 JSValue get = getSlot.getValue(exec, exec->propertyNames().get); 199 if (exec->hadException()) 200 return false; 201 if (!get.isUndefined()) { 202 CallData callData; 203 if (get.getCallData(callData) == CallTypeNone) { 204 throwError(exec, TypeError, "Getter must be a function."); 205 return false; 206 } 207 } else 208 get = JSValue(); 209 desc.setGetter(get); 210 } 211 212 PropertySlot setSlot(description); 213 if (description->getPropertySlot(exec, exec->propertyNames().set, setSlot)) { 214 JSValue set = setSlot.getValue(exec, exec->propertyNames().set); 215 if (exec->hadException()) 216 return false; 217 if (!set.isUndefined()) { 218 CallData callData; 219 if (set.getCallData(callData) == CallTypeNone) { 220 throwError(exec, TypeError, "Setter must be a function."); 221 return false; 222 } 223 } else 224 set = JSValue(); 225 226 desc.setSetter(set); 227 } 228 229 if (!desc.isAccessorDescriptor()) 230 return true; 231 232 if (desc.value()) { 233 throwError(exec, TypeError, "Invalid property. 'value' present on property with getter or setter."); 234 return false; 235 } 236 237 if (desc.writablePresent()) { 238 throwError(exec, TypeError, "Invalid property. 'writable' present on property with getter or setter."); 239 return false; 240 } 241 return true; 242} 243 244JSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState* exec, JSObject*, JSValue, const ArgList& args) 245{ 246 if (!args.at(0).isObject()) 247 return throwError(exec, TypeError, "Properties can only be defined on Objects."); 248 JSObject* O = asObject(args.at(0)); 249 UString propertyName = args.at(1).toString(exec); 250 if (exec->hadException()) 251 return jsNull(); 252 PropertyDescriptor descriptor; 253 if (!toPropertyDescriptor(exec, args.at(2), descriptor)) 254 return jsNull(); 255 ASSERT((descriptor.attributes() & (Getter | Setter)) || (!descriptor.isAccessorDescriptor())); 256 ASSERT(!exec->hadException()); 257 O->defineOwnProperty(exec, Identifier(exec, propertyName), descriptor, true); 258 return O; 259} 260 261static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* properties) 262{ 263 PropertyNameArray propertyNames(exec); 264 asObject(properties)->getOwnPropertyNames(exec, propertyNames); 265 size_t numProperties = propertyNames.size(); 266 Vector<PropertyDescriptor> descriptors; 267 MarkedArgumentBuffer markBuffer; 268 for (size_t i = 0; i < numProperties; i++) { 269 PropertySlot slot; 270 JSValue prop = properties->get(exec, propertyNames[i]); 271 if (exec->hadException()) 272 return jsNull(); 273 PropertyDescriptor descriptor; 274 if (!toPropertyDescriptor(exec, prop, descriptor)) 275 return jsNull(); 276 descriptors.append(descriptor); 277 // Ensure we mark all the values that we're accumulating 278 if (descriptor.isDataDescriptor() && descriptor.value()) 279 markBuffer.append(descriptor.value()); 280 if (descriptor.isAccessorDescriptor()) { 281 if (descriptor.getter()) 282 markBuffer.append(descriptor.getter()); 283 if (descriptor.setter()) 284 markBuffer.append(descriptor.setter()); 285 } 286 } 287 for (size_t i = 0; i < numProperties; i++) { 288 object->defineOwnProperty(exec, propertyNames[i], descriptors[i], true); 289 if (exec->hadException()) 290 return jsNull(); 291 } 292 return object; 293} 294 295JSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState* exec, JSObject*, JSValue, const ArgList& args) 296{ 297 if (!args.at(0).isObject()) 298 return throwError(exec, TypeError, "Properties can only be defined on Objects."); 299 if (!args.at(1).isObject()) 300 return throwError(exec, TypeError, "Property descriptor list must be an Object."); 301 return defineProperties(exec, asObject(args.at(0)), asObject(args.at(1))); 302} 303 304JSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec, JSObject*, JSValue, const ArgList& args) 305{ 306 if (!args.at(0).isObject() && !args.at(0).isNull()) 307 return throwError(exec, TypeError, "Object prototype may only be an Object or null."); 308 JSObject* newObject = constructEmptyObject(exec); 309 newObject->setPrototype(args.at(0)); 310 if (args.at(1).isUndefined()) 311 return newObject; 312 if (!args.at(1).isObject()) 313 return throwError(exec, TypeError, "Property descriptor list must be an Object."); 314 return defineProperties(exec, newObject, asObject(args.at(1))); 315} 316 317} // namespace JSC 318