| 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 |  | 
| 33 | namespace JSC { | 
| 34 |  | 
| 35 | ASSERT_CLASS_FITS_IN_CELL(ObjectConstructor); | 
| 36 |  | 
| 37 | static JSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState*, JSObject*, JSValue, const ArgList&); | 
| 38 | static JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState*, JSObject*, JSValue, const ArgList&); | 
| 39 | static JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState*, JSObject*, JSValue, const ArgList&); | 
| 40 | static JSValue JSC_HOST_CALL objectConstructorKeys(ExecState*, JSObject*, JSValue, const ArgList&); | 
| 41 | static JSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState*, JSObject*, JSValue, const ArgList&); | 
| 42 | static JSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState*, JSObject*, JSValue, const ArgList&); | 
| 43 | static JSValue JSC_HOST_CALL objectConstructorCreate(ExecState*, JSObject*, JSValue, const ArgList&); | 
| 44 |  | 
| 45 | ObjectConstructor::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(propertyName: exec->propertyNames().prototype, value: objectPrototype, attributes: DontEnum | DontDelete | ReadOnly); | 
| 50 |      | 
| 51 |     // no. of arguments for constructor | 
| 52 |     putDirectWithoutTransition(propertyName: exec->propertyNames().length, value: jsNumber(exec, i: 1), attributes: ReadOnly | DontEnum | DontDelete); | 
| 53 |      | 
| 54 |     putDirectFunctionWithoutTransition(exec, function: new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().getPrototypeOf, objectConstructorGetPrototypeOf), attr: DontEnum); | 
| 55 |     putDirectFunctionWithoutTransition(exec, function: new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().getOwnPropertyDescriptor, objectConstructorGetOwnPropertyDescriptor), attr: DontEnum); | 
| 56 |     putDirectFunctionWithoutTransition(exec, function: new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().getOwnPropertyNames, objectConstructorGetOwnPropertyNames), attr: DontEnum); | 
| 57 |     putDirectFunctionWithoutTransition(exec, function: new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().keys, objectConstructorKeys), attr: DontEnum); | 
| 58 |     putDirectFunctionWithoutTransition(exec, function: new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 3, exec->propertyNames().defineProperty, objectConstructorDefineProperty), attr: DontEnum); | 
| 59 |     putDirectFunctionWithoutTransition(exec, function: new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().defineProperties, objectConstructorDefineProperties), attr: DontEnum); | 
| 60 |     putDirectFunctionWithoutTransition(exec, function: new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().create, objectConstructorCreate), attr: DontEnum); | 
| 61 | } | 
| 62 |  | 
| 63 | // ECMA 15.2.2 | 
| 64 | static ALWAYS_INLINE JSObject* constructObject(ExecState* exec, const ArgList& args) | 
| 65 | { | 
| 66 |     JSValue arg = args.at(idx: 0); | 
| 67 |     if (arg.isUndefinedOrNull()) | 
| 68 |         return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure()); | 
| 69 |     return arg.toObject(exec); | 
| 70 | } | 
| 71 |  | 
| 72 | static JSObject* constructWithObjectConstructor(ExecState* exec, JSObject*, const ArgList& args) | 
| 73 | { | 
| 74 |     return constructObject(exec, args); | 
| 75 | } | 
| 76 |  | 
| 77 | ConstructType ObjectConstructor::getConstructData(ConstructData& constructData) | 
| 78 | { | 
| 79 |     constructData.native.function = constructWithObjectConstructor; | 
| 80 |     return ConstructTypeHost; | 
| 81 | } | 
| 82 |  | 
| 83 | static JSValue JSC_HOST_CALL callObjectConstructor(ExecState* exec, JSObject*, JSValue, const ArgList& args) | 
| 84 | { | 
| 85 |     return constructObject(exec, args); | 
| 86 | } | 
| 87 |  | 
| 88 | CallType ObjectConstructor::getCallData(CallData& callData) | 
| 89 | { | 
| 90 |     callData.native.function = callObjectConstructor; | 
| 91 |     return CallTypeHost; | 
| 92 | } | 
| 93 |  | 
| 94 | JSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec, JSObject*, JSValue, const ArgList& args) | 
| 95 | { | 
| 96 |     if (!args.at(idx: 0).isObject()) | 
| 97 |         return throwError(exec, TypeError, message: "Requested prototype of a value that is not an object." ); | 
| 98 |     return asObject(value: args.at(idx: 0))->prototype(); | 
| 99 | } | 
| 100 |  | 
| 101 | JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec, JSObject*, JSValue, const ArgList& args) | 
| 102 | { | 
| 103 |     if (!args.at(idx: 0).isObject()) | 
| 104 |         return throwError(exec, TypeError, message: "Requested property descriptor of a value that is not an object." ); | 
| 105 |     UString propertyName = args.at(idx: 1).toString(exec); | 
| 106 |     if (exec->hadException()) | 
| 107 |         return jsNull(); | 
| 108 |     JSObject* object = asObject(value: args.at(idx: 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(propertyName: exec->propertyNames().value, value: descriptor.value() ? descriptor.value() : jsUndefined(), attributes: 0); | 
| 118 |         description->putDirect(propertyName: exec->propertyNames().writable, value: jsBoolean(b: descriptor.writable()), attributes: 0); | 
| 119 |     } else { | 
| 120 |         description->putDirect(propertyName: exec->propertyNames().get, value: descriptor.getter() ? descriptor.getter() : jsUndefined(), attributes: 0); | 
| 121 |         description->putDirect(propertyName: exec->propertyNames().set, value: descriptor.setter() ? descriptor.setter() : jsUndefined(), attributes: 0); | 
| 122 |     } | 
| 123 |      | 
| 124 |     description->putDirect(propertyName: exec->propertyNames().enumerable, value: jsBoolean(b: descriptor.enumerable()), attributes: 0); | 
| 125 |     description->putDirect(propertyName: exec->propertyNames().configurable, value: jsBoolean(b: descriptor.configurable()), attributes: 0); | 
| 126 |  | 
| 127 |     return description; | 
| 128 | } | 
| 129 |  | 
| 130 | // FIXME: Use the enumeration cache. | 
| 131 | JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exec, JSObject*, JSValue, const ArgList& args) | 
| 132 | { | 
| 133 |     if (!args.at(idx: 0).isObject()) | 
| 134 |         return throwError(exec, TypeError, message: "Requested property names of a value that is not an object." ); | 
| 135 |     PropertyNameArray properties(exec); | 
| 136 |     asObject(value: args.at(idx: 0))->getOwnPropertyNames(exec, properties, mode: 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, s: properties[i].ustring())); | 
| 141 |     return names; | 
| 142 | } | 
| 143 |  | 
| 144 | // FIXME: Use the enumeration cache. | 
| 145 | JSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec, JSObject*, JSValue, const ArgList& args) | 
| 146 | { | 
| 147 |     if (!args.at(idx: 0).isObject()) | 
| 148 |         return throwError(exec, TypeError, message: "Requested keys of a value that is not an object." ); | 
| 149 |     PropertyNameArray properties(exec); | 
| 150 |     asObject(value: args.at(idx: 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, s: properties[i].ustring())); | 
| 155 |     return keys; | 
| 156 | } | 
| 157 |  | 
| 158 | // ES5 8.10.5 ToPropertyDescriptor | 
| 159 | static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc) | 
| 160 | { | 
| 161 |     if (!in.isObject()) { | 
| 162 |         throwError(exec, TypeError, message: "Property description must be an object." ); | 
| 163 |         return false; | 
| 164 |     } | 
| 165 |     JSObject* description = asObject(value: in); | 
| 166 |  | 
| 167 |     PropertySlot enumerableSlot(description); | 
| 168 |     if (description->getPropertySlot(exec, propertyName: exec->propertyNames().enumerable, slot&: enumerableSlot)) { | 
| 169 |         desc.setEnumerable(enumerableSlot.getValue(exec, propertyName: exec->propertyNames().enumerable).toBoolean(exec)); | 
| 170 |         if (exec->hadException()) | 
| 171 |             return false; | 
| 172 |     } | 
| 173 |  | 
| 174 |     PropertySlot configurableSlot(description); | 
| 175 |     if (description->getPropertySlot(exec, propertyName: exec->propertyNames().configurable, slot&: configurableSlot)) { | 
| 176 |         desc.setConfigurable(configurableSlot.getValue(exec, propertyName: 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, propertyName: exec->propertyNames().value, slot&: valueSlot)) { | 
| 184 |         desc.setValue(valueSlot.getValue(exec, propertyName: exec->propertyNames().value)); | 
| 185 |         if (exec->hadException()) | 
| 186 |             return false; | 
| 187 |     } | 
| 188 |  | 
| 189 |     PropertySlot writableSlot(description); | 
| 190 |     if (description->getPropertySlot(exec, propertyName: exec->propertyNames().writable, slot&: writableSlot)) { | 
| 191 |         desc.setWritable(writableSlot.getValue(exec, propertyName: exec->propertyNames().writable).toBoolean(exec)); | 
| 192 |         if (exec->hadException()) | 
| 193 |             return false; | 
| 194 |     } | 
| 195 |  | 
| 196 |     PropertySlot getSlot(description); | 
| 197 |     if (description->getPropertySlot(exec, propertyName: exec->propertyNames().get, slot&: getSlot)) { | 
| 198 |         JSValue get = getSlot.getValue(exec, propertyName: 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, message: "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, propertyName: exec->propertyNames().set, slot&: setSlot)) { | 
| 214 |         JSValue set = setSlot.getValue(exec, propertyName: 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, message: "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, message: "Invalid property.  'value' present on property with getter or setter." ); | 
| 234 |         return false; | 
| 235 |     } | 
| 236 |  | 
| 237 |     if (desc.writablePresent()) { | 
| 238 |         throwError(exec, TypeError, message: "Invalid property.  'writable' present on property with getter or setter." ); | 
| 239 |         return false; | 
| 240 |     } | 
| 241 |     return true; | 
| 242 | } | 
| 243 |  | 
| 244 | JSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState* exec, JSObject*, JSValue, const ArgList& args) | 
| 245 | { | 
| 246 |     if (!args.at(idx: 0).isObject()) | 
| 247 |         return throwError(exec, TypeError, message: "Properties can only be defined on Objects." ); | 
| 248 |     JSObject* O = asObject(value: args.at(idx: 0)); | 
| 249 |     UString propertyName = args.at(idx: 1).toString(exec); | 
| 250 |     if (exec->hadException()) | 
| 251 |         return jsNull(); | 
| 252 |     PropertyDescriptor descriptor; | 
| 253 |     if (!toPropertyDescriptor(exec, in: args.at(idx: 2), desc&: descriptor)) | 
| 254 |         return jsNull(); | 
| 255 |     ASSERT((descriptor.attributes() & (Getter | Setter)) || (!descriptor.isAccessorDescriptor())); | 
| 256 |     ASSERT(!exec->hadException()); | 
| 257 |     O->defineOwnProperty(exec, propertyName: Identifier(exec, propertyName), descriptor, shouldThrow: true); | 
| 258 |     return O; | 
| 259 | } | 
| 260 |  | 
| 261 | static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* properties) | 
| 262 | { | 
| 263 |     PropertyNameArray propertyNames(exec); | 
| 264 |     asObject(cell: 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, propertyName: propertyNames[i]); | 
| 271 |         if (exec->hadException()) | 
| 272 |             return jsNull(); | 
| 273 |         PropertyDescriptor descriptor; | 
| 274 |         if (!toPropertyDescriptor(exec, in: prop, desc&: descriptor)) | 
| 275 |             return jsNull(); | 
| 276 |         descriptors.append(val: descriptor); | 
| 277 |         // Ensure we mark all the values that we're accumulating | 
| 278 |         if (descriptor.isDataDescriptor() && descriptor.value()) | 
| 279 |             markBuffer.append(v: descriptor.value()); | 
| 280 |         if (descriptor.isAccessorDescriptor()) { | 
| 281 |             if (descriptor.getter()) | 
| 282 |                 markBuffer.append(v: descriptor.getter()); | 
| 283 |             if (descriptor.setter()) | 
| 284 |                 markBuffer.append(v: descriptor.setter()); | 
| 285 |         } | 
| 286 |     } | 
| 287 |     for (size_t i = 0; i < numProperties; i++) { | 
| 288 |         object->defineOwnProperty(exec, propertyName: propertyNames[i], descriptors[i], shouldThrow: true); | 
| 289 |         if (exec->hadException()) | 
| 290 |             return jsNull(); | 
| 291 |     } | 
| 292 |     return object; | 
| 293 | } | 
| 294 |  | 
| 295 | JSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState* exec, JSObject*, JSValue, const ArgList& args) | 
| 296 | { | 
| 297 |     if (!args.at(idx: 0).isObject()) | 
| 298 |         return throwError(exec, TypeError, message: "Properties can only be defined on Objects." ); | 
| 299 |     if (!args.at(idx: 1).isObject()) | 
| 300 |         return throwError(exec, TypeError, message: "Property descriptor list must be an Object." ); | 
| 301 |     return defineProperties(exec, object: asObject(value: args.at(idx: 0)), properties: asObject(value: args.at(idx: 1))); | 
| 302 | } | 
| 303 |  | 
| 304 | JSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec, JSObject*, JSValue, const ArgList& args) | 
| 305 | { | 
| 306 |     if (!args.at(idx: 0).isObject() && !args.at(idx: 0).isNull()) | 
| 307 |         return throwError(exec, TypeError, message: "Object prototype may only be an Object or null." ); | 
| 308 |     JSObject* newObject = constructEmptyObject(exec); | 
| 309 |     newObject->setPrototype(args.at(idx: 0)); | 
| 310 |     if (args.at(idx: 1).isUndefined()) | 
| 311 |         return newObject; | 
| 312 |     if (!args.at(idx: 1).isObject()) | 
| 313 |         return throwError(exec, TypeError, message: "Property descriptor list must be an Object." ); | 
| 314 |     return defineProperties(exec, object: newObject, properties: asObject(value: args.at(idx: 1))); | 
| 315 | } | 
| 316 |  | 
| 317 | } // namespace JSC | 
| 318 |  |