1 /* 2 * Copyright (C) 1999-2000 Harri Porten (porten (at) 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 "ExceptionHelpers.h" 26 #include "JSFunction.h" 27 #include "JSArray.h" 28 #include "JSGlobalObject.h" 29 #include "Lookup.h" 30 #include "ObjectPrototype.h" 31 #include "PropertyDescriptor.h" 32 #include "PropertyNameArray.h" 33 34 namespace JSC { 35 36 ASSERT_CLASS_FITS_IN_CELL(ObjectConstructor); 37 38 static EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState*); 39 static EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState*); 40 static EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState*); 41 static EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState*); 42 static EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState*); 43 static EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState*); 44 static EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState*); 45 static EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState*); 46 static EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState*); 47 static EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState*); 48 static EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState*); 49 static EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState*); 50 static EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState*); 51 52 } 53 54 #include "ObjectConstructor.lut.h" 55 56 namespace JSC { 57 58 const ClassInfo ObjectConstructor::s_info = { "Function", &InternalFunction::s_info, 0, ExecState::objectConstructorTable }; 59 60 /* Source for ObjectConstructor.lut.h 61 @begin objectConstructorTable 62 getPrototypeOf objectConstructorGetPrototypeOf DontEnum|Function 1 63 getOwnPropertyDescriptor objectConstructorGetOwnPropertyDescriptor DontEnum|Function 2 64 getOwnPropertyNames objectConstructorGetOwnPropertyNames DontEnum|Function 1 65 keys objectConstructorKeys DontEnum|Function 1 66 defineProperty objectConstructorDefineProperty DontEnum|Function 3 67 defineProperties objectConstructorDefineProperties DontEnum|Function 2 68 create objectConstructorCreate DontEnum|Function 2 69 seal objectConstructorSeal DontEnum|Function 1 70 freeze objectConstructorFreeze DontEnum|Function 1 71 preventExtensions objectConstructorPreventExtensions DontEnum|Function 1 72 isSealed objectConstructorIsSealed DontEnum|Function 1 73 isFrozen objectConstructorIsFrozen DontEnum|Function 1 74 isExtensible objectConstructorIsExtensible DontEnum|Function 1 75 @end 76 */ 77 78 ObjectConstructor::ObjectConstructor(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, ObjectPrototype* objectPrototype) 79 : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, "Object")) 80 { 81 // ECMA 15.2.3.1 82 putDirectWithoutTransition(exec->globalData(), exec->propertyNames().prototype, objectPrototype, DontEnum | DontDelete | ReadOnly); 83 // no. of arguments for constructor 84 putDirectWithoutTransition(exec->globalData(), exec->propertyNames().length, jsNumber(1), ReadOnly | DontEnum | DontDelete); 85 } 86 87 bool ObjectConstructor::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot &slot) 88 { 89 return getStaticFunctionSlot<JSObject>(exec, ExecState::objectConstructorTable(exec), this, propertyName, slot); 90 } 91 92 bool ObjectConstructor::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) 93 { 94 return getStaticFunctionDescriptor<JSObject>(exec, ExecState::objectConstructorTable(exec), this, propertyName, descriptor); 95 } 96 97 // ECMA 15.2.2 98 static ALWAYS_INLINE JSObject* constructObject(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args) 99 { 100 JSValue arg = args.at(0); 101 if (arg.isUndefinedOrNull()) 102 return constructEmptyObject(exec, globalObject); 103 return arg.toObject(exec, globalObject); 104 } 105 106 static EncodedJSValue JSC_HOST_CALL constructWithObjectConstructor(ExecState* exec) 107 { 108 ArgList args(exec); 109 return JSValue::encode(constructObject(exec, asInternalFunction(exec->callee())->globalObject(), args)); 110 } 111 112 ConstructType ObjectConstructor::getConstructData(ConstructData& constructData) 113 { 114 constructData.native.function = constructWithObjectConstructor; 115 return ConstructTypeHost; 116 } 117 118 static EncodedJSValue JSC_HOST_CALL callObjectConstructor(ExecState* exec) 119 { 120 ArgList args(exec); 121 return JSValue::encode(constructObject(exec, asInternalFunction(exec->callee())->globalObject(), args)); 122 } 123 124 CallType ObjectConstructor::getCallData(CallData& callData) 125 { 126 callData.native.function = callObjectConstructor; 127 return CallTypeHost; 128 } 129 130 EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec) 131 { 132 if (!exec->argument(0).isObject()) 133 return throwVMError(exec, createTypeError(exec, "Requested prototype of a value that is not an object.")); 134 return JSValue::encode(asObject(exec->argument(0))->prototype()); 135 } 136 137 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec) 138 { 139 if (!exec->argument(0).isObject()) 140 return throwVMError(exec, createTypeError(exec, "Requested property descriptor of a value that is not an object.")); 141 UString propertyName = exec->argument(1).toString(exec); 142 if (exec->hadException()) 143 return JSValue::encode(jsNull()); 144 JSObject* object = asObject(exec->argument(0)); 145 PropertyDescriptor descriptor; 146 if (!object->getOwnPropertyDescriptor(exec, Identifier(exec, propertyName), descriptor)) 147 return JSValue::encode(jsUndefined()); 148 if (exec->hadException()) 149 return JSValue::encode(jsUndefined()); 150 151 JSObject* description = constructEmptyObject(exec); 152 if (!descriptor.isAccessorDescriptor()) { 153 description->putDirect(exec->globalData(), exec->propertyNames().value, descriptor.value() ? descriptor.value() : jsUndefined(), 0); 154 description->putDirect(exec->globalData(), exec->propertyNames().writable, jsBoolean(descriptor.writable()), 0); 155 } else { 156 description->putDirect(exec->globalData(), exec->propertyNames().get, descriptor.getter() ? descriptor.getter() : jsUndefined(), 0); 157 description->putDirect(exec->globalData(), exec->propertyNames().set, descriptor.setter() ? descriptor.setter() : jsUndefined(), 0); 158 } 159 160 description->putDirect(exec->globalData(), exec->propertyNames().enumerable, jsBoolean(descriptor.enumerable()), 0); 161 description->putDirect(exec->globalData(), exec->propertyNames().configurable, jsBoolean(descriptor.configurable()), 0); 162 163 return JSValue::encode(description); 164 } 165 166 // FIXME: Use the enumeration cache. 167 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exec) 168 { 169 if (!exec->argument(0).isObject()) 170 return throwVMError(exec, createTypeError(exec, "Requested property names of a value that is not an object.")); 171 PropertyNameArray properties(exec); 172 asObject(exec->argument(0))->getOwnPropertyNames(exec, properties, IncludeDontEnumProperties); 173 JSArray* names = constructEmptyArray(exec); 174 size_t numProperties = properties.size(); 175 for (size_t i = 0; i < numProperties; i++) 176 names->push(exec, jsOwnedString(exec, properties[i].ustring())); 177 return JSValue::encode(names); 178 } 179 180 // FIXME: Use the enumeration cache. 181 EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec) 182 { 183 if (!exec->argument(0).isObject()) 184 return throwVMError(exec, createTypeError(exec, "Requested keys of a value that is not an object.")); 185 PropertyNameArray properties(exec); 186 asObject(exec->argument(0))->getOwnPropertyNames(exec, properties); 187 JSArray* keys = constructEmptyArray(exec); 188 size_t numProperties = properties.size(); 189 for (size_t i = 0; i < numProperties; i++) 190 keys->push(exec, jsOwnedString(exec, properties[i].ustring())); 191 return JSValue::encode(keys); 192 } 193 194 // ES5 8.10.5 ToPropertyDescriptor 195 static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc) 196 { 197 if (!in.isObject()) { 198 throwError(exec, createTypeError(exec, "Property description must be an object.")); 199 return false; 200 } 201 JSObject* description = asObject(in); 202 203 PropertySlot enumerableSlot(description); 204 if (description->getPropertySlot(exec, exec->propertyNames().enumerable, enumerableSlot)) { 205 desc.setEnumerable(enumerableSlot.getValue(exec, exec->propertyNames().enumerable).toBoolean(exec)); 206 if (exec->hadException()) 207 return false; 208 } 209 210 PropertySlot configurableSlot(description); 211 if (description->getPropertySlot(exec, exec->propertyNames().configurable, configurableSlot)) { 212 desc.setConfigurable(configurableSlot.getValue(exec, exec->propertyNames().configurable).toBoolean(exec)); 213 if (exec->hadException()) 214 return false; 215 } 216 217 JSValue value; 218 PropertySlot valueSlot(description); 219 if (description->getPropertySlot(exec, exec->propertyNames().value, valueSlot)) { 220 desc.setValue(valueSlot.getValue(exec, exec->propertyNames().value)); 221 if (exec->hadException()) 222 return false; 223 } 224 225 PropertySlot writableSlot(description); 226 if (description->getPropertySlot(exec, exec->propertyNames().writable, writableSlot)) { 227 desc.setWritable(writableSlot.getValue(exec, exec->propertyNames().writable).toBoolean(exec)); 228 if (exec->hadException()) 229 return false; 230 } 231 232 PropertySlot getSlot(description); 233 if (description->getPropertySlot(exec, exec->propertyNames().get, getSlot)) { 234 JSValue get = getSlot.getValue(exec, exec->propertyNames().get); 235 if (exec->hadException()) 236 return false; 237 if (!get.isUndefined()) { 238 CallData callData; 239 if (getCallData(get, callData) == CallTypeNone) { 240 throwError(exec, createTypeError(exec, "Getter must be a function.")); 241 return false; 242 } 243 } else 244 get = JSValue(); 245 desc.setGetter(get); 246 } 247 248 PropertySlot setSlot(description); 249 if (description->getPropertySlot(exec, exec->propertyNames().set, setSlot)) { 250 JSValue set = setSlot.getValue(exec, exec->propertyNames().set); 251 if (exec->hadException()) 252 return false; 253 if (!set.isUndefined()) { 254 CallData callData; 255 if (getCallData(set, callData) == CallTypeNone) { 256 throwError(exec, createTypeError(exec, "Setter must be a function.")); 257 return false; 258 } 259 } else 260 set = JSValue(); 261 262 desc.setSetter(set); 263 } 264 265 if (!desc.isAccessorDescriptor()) 266 return true; 267 268 if (desc.value()) { 269 throwError(exec, createTypeError(exec, "Invalid property. 'value' present on property with getter or setter.")); 270 return false; 271 } 272 273 if (desc.writablePresent()) { 274 throwError(exec, createTypeError(exec, "Invalid property. 'writable' present on property with getter or setter.")); 275 return false; 276 } 277 return true; 278 } 279 280 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState* exec) 281 { 282 if (!exec->argument(0).isObject()) 283 return throwVMError(exec, createTypeError(exec, "Properties can only be defined on Objects.")); 284 JSObject* O = asObject(exec->argument(0)); 285 UString propertyName = exec->argument(1).toString(exec); 286 if (exec->hadException()) 287 return JSValue::encode(jsNull()); 288 PropertyDescriptor descriptor; 289 if (!toPropertyDescriptor(exec, exec->argument(2), descriptor)) 290 return JSValue::encode(jsNull()); 291 ASSERT((descriptor.attributes() & (Getter | Setter)) || (!descriptor.isAccessorDescriptor())); 292 ASSERT(!exec->hadException()); 293 O->defineOwnProperty(exec, Identifier(exec, propertyName), descriptor, true); 294 return JSValue::encode(O); 295 } 296 297 static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* properties) 298 { 299 PropertyNameArray propertyNames(exec); 300 asObject(properties)->getOwnPropertyNames(exec, propertyNames); 301 size_t numProperties = propertyNames.size(); 302 Vector<PropertyDescriptor> descriptors; 303 MarkedArgumentBuffer markBuffer; 304 for (size_t i = 0; i < numProperties; i++) { 305 PropertySlot slot; 306 JSValue prop = properties->get(exec, propertyNames[i]); 307 if (exec->hadException()) 308 return jsNull(); 309 PropertyDescriptor descriptor; 310 if (!toPropertyDescriptor(exec, prop, descriptor)) 311 return jsNull(); 312 descriptors.append(descriptor); 313 // Ensure we mark all the values that we're accumulating 314 if (descriptor.isDataDescriptor() && descriptor.value()) 315 markBuffer.append(descriptor.value()); 316 if (descriptor.isAccessorDescriptor()) { 317 if (descriptor.getter()) 318 markBuffer.append(descriptor.getter()); 319 if (descriptor.setter()) 320 markBuffer.append(descriptor.setter()); 321 } 322 } 323 for (size_t i = 0; i < numProperties; i++) { 324 object->defineOwnProperty(exec, propertyNames[i], descriptors[i], true); 325 if (exec->hadException()) 326 return jsNull(); 327 } 328 return object; 329 } 330 331 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState* exec) 332 { 333 if (!exec->argument(0).isObject()) 334 return throwVMError(exec, createTypeError(exec, "Properties can only be defined on Objects.")); 335 if (!exec->argument(1).isObject()) 336 return throwVMError(exec, createTypeError(exec, "Property descriptor list must be an Object.")); 337 return JSValue::encode(defineProperties(exec, asObject(exec->argument(0)), asObject(exec->argument(1)))); 338 } 339 340 EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec) 341 { 342 if (!exec->argument(0).isObject() && !exec->argument(0).isNull()) 343 return throwVMError(exec, createTypeError(exec, "Object prototype may only be an Object or null.")); 344 JSObject* newObject = constructEmptyObject(exec); 345 newObject->setPrototype(exec->globalData(), exec->argument(0)); 346 if (exec->argument(1).isUndefined()) 347 return JSValue::encode(newObject); 348 if (!exec->argument(1).isObject()) 349 return throwVMError(exec, createTypeError(exec, "Property descriptor list must be an Object.")); 350 return JSValue::encode(defineProperties(exec, newObject, asObject(exec->argument(1)))); 351 } 352 353 EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState* exec) 354 { 355 JSValue obj = exec->argument(0); 356 if (!obj.isObject()) 357 return throwVMError(exec, createTypeError(exec, "Object.seal can only be called on Objects.")); 358 asObject(obj)->seal(exec->globalData()); 359 return JSValue::encode(obj); 360 } 361 362 EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState* exec) 363 { 364 JSValue obj = exec->argument(0); 365 if (!obj.isObject()) 366 return throwVMError(exec, createTypeError(exec, "Object.freeze can only be called on Objects.")); 367 asObject(obj)->freeze(exec->globalData()); 368 return JSValue::encode(obj); 369 } 370 371 EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState* exec) 372 { 373 JSValue obj = exec->argument(0); 374 if (!obj.isObject()) 375 return throwVMError(exec, createTypeError(exec, "Object.preventExtensions can only be called on Objects.")); 376 asObject(obj)->preventExtensions(exec->globalData()); 377 return JSValue::encode(obj); 378 } 379 380 EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState* exec) 381 { 382 JSValue obj = exec->argument(0); 383 if (!obj.isObject()) 384 return throwVMError(exec, createTypeError(exec, "Object.isSealed can only be called on Objects.")); 385 return JSValue::encode(jsBoolean(asObject(obj)->isSealed(exec->globalData()))); 386 } 387 388 EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState* exec) 389 { 390 JSValue obj = exec->argument(0); 391 if (!obj.isObject()) 392 return throwVMError(exec, createTypeError(exec, "Object.isFrozen can only be called on Objects.")); 393 return JSValue::encode(jsBoolean(asObject(obj)->isFrozen(exec->globalData()))); 394 } 395 396 EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState* exec) 397 { 398 JSValue obj = exec->argument(0); 399 if (!obj.isObject()) 400 return throwVMError(exec, createTypeError(exec, "Object.isExtensible can only be called on Objects.")); 401 return JSValue::encode(jsBoolean(asObject(obj)->isExtensible())); 402 } 403 404 } // namespace JSC 405