]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/ObjectConstructor.cpp
6efcea61fb23500bd450621d206d83f9eac2b49c
[apple/javascriptcore.git] / runtime / ObjectConstructor.cpp
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
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
135 // This uses JSValue::get() instead of directly accessing the prototype from the object
136 // (using JSObject::prototype()) in order to allow objects to override the behavior, such
137 // as returning jsUndefined() for cross-origin access.
138 return JSValue::encode(exec->argument(0).get(exec, exec->propertyNames().underscoreProto));
139 }
140
141 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec)
142 {
143 if (!exec->argument(0).isObject())
144 return throwVMError(exec, createTypeError(exec, "Requested property descriptor of a value that is not an object."));
145 UString propertyName = exec->argument(1).toString(exec);
146 if (exec->hadException())
147 return JSValue::encode(jsNull());
148 JSObject* object = asObject(exec->argument(0));
149 PropertyDescriptor descriptor;
150 if (!object->getOwnPropertyDescriptor(exec, Identifier(exec, propertyName), descriptor))
151 return JSValue::encode(jsUndefined());
152 if (exec->hadException())
153 return JSValue::encode(jsUndefined());
154
155 JSObject* description = constructEmptyObject(exec);
156 if (!descriptor.isAccessorDescriptor()) {
157 description->putDirect(exec->globalData(), exec->propertyNames().value, descriptor.value() ? descriptor.value() : jsUndefined(), 0);
158 description->putDirect(exec->globalData(), exec->propertyNames().writable, jsBoolean(descriptor.writable()), 0);
159 } else {
160 description->putDirect(exec->globalData(), exec->propertyNames().get, descriptor.getter() ? descriptor.getter() : jsUndefined(), 0);
161 description->putDirect(exec->globalData(), exec->propertyNames().set, descriptor.setter() ? descriptor.setter() : jsUndefined(), 0);
162 }
163
164 description->putDirect(exec->globalData(), exec->propertyNames().enumerable, jsBoolean(descriptor.enumerable()), 0);
165 description->putDirect(exec->globalData(), exec->propertyNames().configurable, jsBoolean(descriptor.configurable()), 0);
166
167 return JSValue::encode(description);
168 }
169
170 // FIXME: Use the enumeration cache.
171 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exec)
172 {
173 if (!exec->argument(0).isObject())
174 return throwVMError(exec, createTypeError(exec, "Requested property names of a value that is not an object."));
175 PropertyNameArray properties(exec);
176 asObject(exec->argument(0))->getOwnPropertyNames(exec, properties, IncludeDontEnumProperties);
177 JSArray* names = constructEmptyArray(exec);
178 size_t numProperties = properties.size();
179 for (size_t i = 0; i < numProperties; i++)
180 names->push(exec, jsOwnedString(exec, properties[i].ustring()));
181 return JSValue::encode(names);
182 }
183
184 // FIXME: Use the enumeration cache.
185 EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec)
186 {
187 if (!exec->argument(0).isObject())
188 return throwVMError(exec, createTypeError(exec, "Requested keys of a value that is not an object."));
189 PropertyNameArray properties(exec);
190 asObject(exec->argument(0))->getOwnPropertyNames(exec, properties);
191 JSArray* keys = constructEmptyArray(exec);
192 size_t numProperties = properties.size();
193 for (size_t i = 0; i < numProperties; i++)
194 keys->push(exec, jsOwnedString(exec, properties[i].ustring()));
195 return JSValue::encode(keys);
196 }
197
198 // ES5 8.10.5 ToPropertyDescriptor
199 static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc)
200 {
201 if (!in.isObject()) {
202 throwError(exec, createTypeError(exec, "Property description must be an object."));
203 return false;
204 }
205 JSObject* description = asObject(in);
206
207 PropertySlot enumerableSlot(description);
208 if (description->getPropertySlot(exec, exec->propertyNames().enumerable, enumerableSlot)) {
209 desc.setEnumerable(enumerableSlot.getValue(exec, exec->propertyNames().enumerable).toBoolean(exec));
210 if (exec->hadException())
211 return false;
212 }
213
214 PropertySlot configurableSlot(description);
215 if (description->getPropertySlot(exec, exec->propertyNames().configurable, configurableSlot)) {
216 desc.setConfigurable(configurableSlot.getValue(exec, exec->propertyNames().configurable).toBoolean(exec));
217 if (exec->hadException())
218 return false;
219 }
220
221 JSValue value;
222 PropertySlot valueSlot(description);
223 if (description->getPropertySlot(exec, exec->propertyNames().value, valueSlot)) {
224 desc.setValue(valueSlot.getValue(exec, exec->propertyNames().value));
225 if (exec->hadException())
226 return false;
227 }
228
229 PropertySlot writableSlot(description);
230 if (description->getPropertySlot(exec, exec->propertyNames().writable, writableSlot)) {
231 desc.setWritable(writableSlot.getValue(exec, exec->propertyNames().writable).toBoolean(exec));
232 if (exec->hadException())
233 return false;
234 }
235
236 PropertySlot getSlot(description);
237 if (description->getPropertySlot(exec, exec->propertyNames().get, getSlot)) {
238 JSValue get = getSlot.getValue(exec, exec->propertyNames().get);
239 if (exec->hadException())
240 return false;
241 if (!get.isUndefined()) {
242 CallData callData;
243 if (getCallData(get, callData) == CallTypeNone) {
244 throwError(exec, createTypeError(exec, "Getter must be a function."));
245 return false;
246 }
247 } else
248 get = JSValue();
249 desc.setGetter(get);
250 }
251
252 PropertySlot setSlot(description);
253 if (description->getPropertySlot(exec, exec->propertyNames().set, setSlot)) {
254 JSValue set = setSlot.getValue(exec, exec->propertyNames().set);
255 if (exec->hadException())
256 return false;
257 if (!set.isUndefined()) {
258 CallData callData;
259 if (getCallData(set, callData) == CallTypeNone) {
260 throwError(exec, createTypeError(exec, "Setter must be a function."));
261 return false;
262 }
263 } else
264 set = JSValue();
265
266 desc.setSetter(set);
267 }
268
269 if (!desc.isAccessorDescriptor())
270 return true;
271
272 if (desc.value()) {
273 throwError(exec, createTypeError(exec, "Invalid property. 'value' present on property with getter or setter."));
274 return false;
275 }
276
277 if (desc.writablePresent()) {
278 throwError(exec, createTypeError(exec, "Invalid property. 'writable' present on property with getter or setter."));
279 return false;
280 }
281 return true;
282 }
283
284 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState* exec)
285 {
286 if (!exec->argument(0).isObject())
287 return throwVMError(exec, createTypeError(exec, "Properties can only be defined on Objects."));
288 JSObject* O = asObject(exec->argument(0));
289 UString propertyName = exec->argument(1).toString(exec);
290 if (exec->hadException())
291 return JSValue::encode(jsNull());
292 PropertyDescriptor descriptor;
293 if (!toPropertyDescriptor(exec, exec->argument(2), descriptor))
294 return JSValue::encode(jsNull());
295 ASSERT((descriptor.attributes() & (Getter | Setter)) || (!descriptor.isAccessorDescriptor()));
296 ASSERT(!exec->hadException());
297 O->defineOwnProperty(exec, Identifier(exec, propertyName), descriptor, true);
298 return JSValue::encode(O);
299 }
300
301 static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* properties)
302 {
303 PropertyNameArray propertyNames(exec);
304 asObject(properties)->getOwnPropertyNames(exec, propertyNames);
305 size_t numProperties = propertyNames.size();
306 Vector<PropertyDescriptor> descriptors;
307 MarkedArgumentBuffer markBuffer;
308 for (size_t i = 0; i < numProperties; i++) {
309 PropertySlot slot;
310 JSValue prop = properties->get(exec, propertyNames[i]);
311 if (exec->hadException())
312 return jsNull();
313 PropertyDescriptor descriptor;
314 if (!toPropertyDescriptor(exec, prop, descriptor))
315 return jsNull();
316 descriptors.append(descriptor);
317 // Ensure we mark all the values that we're accumulating
318 if (descriptor.isDataDescriptor() && descriptor.value())
319 markBuffer.append(descriptor.value());
320 if (descriptor.isAccessorDescriptor()) {
321 if (descriptor.getter())
322 markBuffer.append(descriptor.getter());
323 if (descriptor.setter())
324 markBuffer.append(descriptor.setter());
325 }
326 }
327 for (size_t i = 0; i < numProperties; i++) {
328 object->defineOwnProperty(exec, propertyNames[i], descriptors[i], true);
329 if (exec->hadException())
330 return jsNull();
331 }
332 return object;
333 }
334
335 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState* exec)
336 {
337 if (!exec->argument(0).isObject())
338 return throwVMError(exec, createTypeError(exec, "Properties can only be defined on Objects."));
339 if (!exec->argument(1).isObject())
340 return throwVMError(exec, createTypeError(exec, "Property descriptor list must be an Object."));
341 return JSValue::encode(defineProperties(exec, asObject(exec->argument(0)), asObject(exec->argument(1))));
342 }
343
344 EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec)
345 {
346 if (!exec->argument(0).isObject() && !exec->argument(0).isNull())
347 return throwVMError(exec, createTypeError(exec, "Object prototype may only be an Object or null."));
348 JSValue proto = exec->argument(0);
349 JSObject* newObject = proto.isObject() ? constructEmptyObject(exec, asObject(proto)->inheritorID(exec->globalData())) : constructEmptyObject(exec, exec->lexicalGlobalObject()->nullPrototypeObjectStructure());
350 if (exec->argument(1).isUndefined())
351 return JSValue::encode(newObject);
352 if (!exec->argument(1).isObject())
353 return throwVMError(exec, createTypeError(exec, "Property descriptor list must be an Object."));
354 return JSValue::encode(defineProperties(exec, newObject, asObject(exec->argument(1))));
355 }
356
357 EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState* exec)
358 {
359 JSValue obj = exec->argument(0);
360 if (!obj.isObject())
361 return throwVMError(exec, createTypeError(exec, "Object.seal can only be called on Objects."));
362 asObject(obj)->seal(exec->globalData());
363 return JSValue::encode(obj);
364 }
365
366 EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState* exec)
367 {
368 JSValue obj = exec->argument(0);
369 if (!obj.isObject())
370 return throwVMError(exec, createTypeError(exec, "Object.freeze can only be called on Objects."));
371 asObject(obj)->freeze(exec->globalData());
372 return JSValue::encode(obj);
373 }
374
375 EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState* exec)
376 {
377 JSValue obj = exec->argument(0);
378 if (!obj.isObject())
379 return throwVMError(exec, createTypeError(exec, "Object.preventExtensions can only be called on Objects."));
380 asObject(obj)->preventExtensions(exec->globalData());
381 return JSValue::encode(obj);
382 }
383
384 EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState* exec)
385 {
386 JSValue obj = exec->argument(0);
387 if (!obj.isObject())
388 return throwVMError(exec, createTypeError(exec, "Object.isSealed can only be called on Objects."));
389 return JSValue::encode(jsBoolean(asObject(obj)->isSealed(exec->globalData())));
390 }
391
392 EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState* exec)
393 {
394 JSValue obj = exec->argument(0);
395 if (!obj.isObject())
396 return throwVMError(exec, createTypeError(exec, "Object.isFrozen can only be called on Objects."));
397 return JSValue::encode(jsBoolean(asObject(obj)->isFrozen(exec->globalData())));
398 }
399
400 EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState* exec)
401 {
402 JSValue obj = exec->argument(0);
403 if (!obj.isObject())
404 return throwVMError(exec, createTypeError(exec, "Object.isExtensible can only be called on Objects."));
405 return JSValue::encode(jsBoolean(asObject(obj)->isExtensible()));
406 }
407
408 } // namespace JSC