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