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 "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
ObjectConstructor(ExecState * exec,JSGlobalObject * globalObject,Structure * structure,ObjectPrototype * objectPrototype)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
getOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)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
getOwnPropertyDescriptor(ExecState * exec,const Identifier & propertyName,PropertyDescriptor & descriptor)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
constructObject(ExecState * exec,JSGlobalObject * globalObject,const ArgList & args)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
constructWithObjectConstructor(ExecState * exec)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
getConstructData(ConstructData & constructData)112 ConstructType ObjectConstructor::getConstructData(ConstructData& constructData)
113 {
114 constructData.native.function = constructWithObjectConstructor;
115 return ConstructTypeHost;
116 }
117
callObjectConstructor(ExecState * exec)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
getCallData(CallData & callData)124 CallType ObjectConstructor::getCallData(CallData& callData)
125 {
126 callData.native.function = callObjectConstructor;
127 return CallTypeHost;
128 }
129
objectConstructorGetPrototypeOf(ExecState * exec)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
objectConstructorGetOwnPropertyDescriptor(ExecState * exec)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.
objectConstructorGetOwnPropertyNames(ExecState * exec)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.
objectConstructorKeys(ExecState * exec)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
toPropertyDescriptor(ExecState * exec,JSValue in,PropertyDescriptor & desc)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
objectConstructorDefineProperty(ExecState * exec)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
defineProperties(ExecState * exec,JSObject * object,JSObject * properties)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
objectConstructorDefineProperties(ExecState * exec)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
objectConstructorCreate(ExecState * exec)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
objectConstructorSeal(ExecState * exec)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
objectConstructorFreeze(ExecState * exec)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
objectConstructorPreventExtensions(ExecState * exec)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
objectConstructorIsSealed(ExecState * exec)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
objectConstructorIsFrozen(ExecState * exec)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
objectConstructorIsExtensible(ExecState * exec)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