2011-01-27 Oliver Hunt <oliver@apple.com>
[WebKit-https.git] / 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 "ObjectPrototype.h"
30 #include "PropertyDescriptor.h"
31 #include "PropertyNameArray.h"
32 #include "PrototypeFunction.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
46 ObjectConstructor::ObjectConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, ObjectPrototype* objectPrototype, Structure* prototypeFunctionStructure)
47 : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, "Object"))
48 {
49     // ECMA 15.2.3.1
50     putDirectWithoutTransition(exec->globalData(), exec->propertyNames().prototype, objectPrototype, DontEnum | DontDelete | ReadOnly);
51     
52     // no. of arguments for constructor
53     putDirectWithoutTransition(exec->globalData(), exec->propertyNames().length, jsNumber(1), ReadOnly | DontEnum | DontDelete);
54     
55     putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().getPrototypeOf, objectConstructorGetPrototypeOf), DontEnum);
56     putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 2, exec->propertyNames().getOwnPropertyDescriptor, objectConstructorGetOwnPropertyDescriptor), DontEnum);
57     putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().getOwnPropertyNames, objectConstructorGetOwnPropertyNames), DontEnum);
58     putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 1, exec->propertyNames().keys, objectConstructorKeys), DontEnum);
59     putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 3, exec->propertyNames().defineProperty, objectConstructorDefineProperty), DontEnum);
60     putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 2, exec->propertyNames().defineProperties, objectConstructorDefineProperties), DontEnum);
61     putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, globalObject, prototypeFunctionStructure, 2, exec->propertyNames().create, objectConstructorCreate), DontEnum);
62 }
63
64 // ECMA 15.2.2
65 static ALWAYS_INLINE JSObject* constructObject(ExecState* exec, const ArgList& args)
66 {
67     JSValue arg = args.at(0);
68     if (arg.isUndefinedOrNull())
69         return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure());
70     return arg.toObject(exec);
71 }
72
73 static EncodedJSValue JSC_HOST_CALL constructWithObjectConstructor(ExecState* exec)
74 {
75     ArgList args(exec);
76     return JSValue::encode(constructObject(exec, args));
77 }
78
79 ConstructType ObjectConstructor::getConstructData(ConstructData& constructData)
80 {
81     constructData.native.function = constructWithObjectConstructor;
82     return ConstructTypeHost;
83 }
84
85 static EncodedJSValue JSC_HOST_CALL callObjectConstructor(ExecState* exec)
86 {
87     ArgList args(exec);
88     return JSValue::encode(constructObject(exec, args));
89 }
90
91 CallType ObjectConstructor::getCallData(CallData& callData)
92 {
93     callData.native.function = callObjectConstructor;
94     return CallTypeHost;
95 }
96
97 EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec)
98 {
99     if (!exec->argument(0).isObject())
100         return throwVMError(exec, createTypeError(exec, "Requested prototype of a value that is not an object."));
101     return JSValue::encode(asObject(exec->argument(0))->prototype());
102 }
103
104 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec)
105 {
106     if (!exec->argument(0).isObject())
107         return throwVMError(exec, createTypeError(exec, "Requested property descriptor of a value that is not an object."));
108     UString propertyName = exec->argument(1).toString(exec);
109     if (exec->hadException())
110         return JSValue::encode(jsNull());
111     JSObject* object = asObject(exec->argument(0));
112     PropertyDescriptor descriptor;
113     if (!object->getOwnPropertyDescriptor(exec, Identifier(exec, propertyName), descriptor))
114         return JSValue::encode(jsUndefined());
115     if (exec->hadException())
116         return JSValue::encode(jsUndefined());
117
118     JSObject* description = constructEmptyObject(exec);
119     if (!descriptor.isAccessorDescriptor()) {
120         description->putDirect(exec->globalData(), exec->propertyNames().value, descriptor.value() ? descriptor.value() : jsUndefined(), 0);
121         description->putDirect(exec->globalData(), exec->propertyNames().writable, jsBoolean(descriptor.writable()), 0);
122     } else {
123         description->putDirect(exec->globalData(), exec->propertyNames().get, descriptor.getter() ? descriptor.getter() : jsUndefined(), 0);
124         description->putDirect(exec->globalData(), exec->propertyNames().set, descriptor.setter() ? descriptor.setter() : jsUndefined(), 0);
125     }
126     
127     description->putDirect(exec->globalData(), exec->propertyNames().enumerable, jsBoolean(descriptor.enumerable()), 0);
128     description->putDirect(exec->globalData(), exec->propertyNames().configurable, jsBoolean(descriptor.configurable()), 0);
129
130     return JSValue::encode(description);
131 }
132
133 // FIXME: Use the enumeration cache.
134 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exec)
135 {
136     if (!exec->argument(0).isObject())
137         return throwVMError(exec, createTypeError(exec, "Requested property names of a value that is not an object."));
138     PropertyNameArray properties(exec);
139     asObject(exec->argument(0))->getOwnPropertyNames(exec, properties, IncludeDontEnumProperties);
140     JSArray* names = constructEmptyArray(exec);
141     size_t numProperties = properties.size();
142     for (size_t i = 0; i < numProperties; i++)
143         names->push(exec, jsOwnedString(exec, properties[i].ustring()));
144     return JSValue::encode(names);
145 }
146
147 // FIXME: Use the enumeration cache.
148 EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec)
149 {
150     if (!exec->argument(0).isObject())
151         return throwVMError(exec, createTypeError(exec, "Requested keys of a value that is not an object."));
152     PropertyNameArray properties(exec);
153     asObject(exec->argument(0))->getOwnPropertyNames(exec, properties);
154     JSArray* keys = constructEmptyArray(exec);
155     size_t numProperties = properties.size();
156     for (size_t i = 0; i < numProperties; i++)
157         keys->push(exec, jsOwnedString(exec, properties[i].ustring()));
158     return JSValue::encode(keys);
159 }
160
161 // ES5 8.10.5 ToPropertyDescriptor
162 static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc)
163 {
164     if (!in.isObject()) {
165         throwError(exec, createTypeError(exec, "Property description must be an object."));
166         return false;
167     }
168     JSObject* description = asObject(in);
169
170     PropertySlot enumerableSlot(description);
171     if (description->getPropertySlot(exec, exec->propertyNames().enumerable, enumerableSlot)) {
172         desc.setEnumerable(enumerableSlot.getValue(exec, exec->propertyNames().enumerable).toBoolean(exec));
173         if (exec->hadException())
174             return false;
175     }
176
177     PropertySlot configurableSlot(description);
178     if (description->getPropertySlot(exec, exec->propertyNames().configurable, configurableSlot)) {
179         desc.setConfigurable(configurableSlot.getValue(exec, exec->propertyNames().configurable).toBoolean(exec));
180         if (exec->hadException())
181             return false;
182     }
183
184     JSValue value;
185     PropertySlot valueSlot(description);
186     if (description->getPropertySlot(exec, exec->propertyNames().value, valueSlot)) {
187         desc.setValue(valueSlot.getValue(exec, exec->propertyNames().value));
188         if (exec->hadException())
189             return false;
190     }
191
192     PropertySlot writableSlot(description);
193     if (description->getPropertySlot(exec, exec->propertyNames().writable, writableSlot)) {
194         desc.setWritable(writableSlot.getValue(exec, exec->propertyNames().writable).toBoolean(exec));
195         if (exec->hadException())
196             return false;
197     }
198
199     PropertySlot getSlot(description);
200     if (description->getPropertySlot(exec, exec->propertyNames().get, getSlot)) {
201         JSValue get = getSlot.getValue(exec, exec->propertyNames().get);
202         if (exec->hadException())
203             return false;
204         if (!get.isUndefined()) {
205             CallData callData;
206             if (getCallData(get, callData) == CallTypeNone) {
207                 throwError(exec, createTypeError(exec, "Getter must be a function."));
208                 return false;
209             }
210         } else
211             get = JSValue();
212         desc.setGetter(get);
213     }
214
215     PropertySlot setSlot(description);
216     if (description->getPropertySlot(exec, exec->propertyNames().set, setSlot)) {
217         JSValue set = setSlot.getValue(exec, exec->propertyNames().set);
218         if (exec->hadException())
219             return false;
220         if (!set.isUndefined()) {
221             CallData callData;
222             if (getCallData(set, callData) == CallTypeNone) {
223                 throwError(exec, createTypeError(exec, "Setter must be a function."));
224                 return false;
225             }
226         } else
227             set = JSValue();
228
229         desc.setSetter(set);
230     }
231
232     if (!desc.isAccessorDescriptor())
233         return true;
234
235     if (desc.value()) {
236         throwError(exec, createTypeError(exec, "Invalid property.  'value' present on property with getter or setter."));
237         return false;
238     }
239
240     if (desc.writablePresent()) {
241         throwError(exec, createTypeError(exec, "Invalid property.  'writable' present on property with getter or setter."));
242         return false;
243     }
244     return true;
245 }
246
247 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState* exec)
248 {
249     if (!exec->argument(0).isObject())
250         return throwVMError(exec, createTypeError(exec, "Properties can only be defined on Objects."));
251     JSObject* O = asObject(exec->argument(0));
252     UString propertyName = exec->argument(1).toString(exec);
253     if (exec->hadException())
254         return JSValue::encode(jsNull());
255     PropertyDescriptor descriptor;
256     if (!toPropertyDescriptor(exec, exec->argument(2), descriptor))
257         return JSValue::encode(jsNull());
258     ASSERT((descriptor.attributes() & (Getter | Setter)) || (!descriptor.isAccessorDescriptor()));
259     ASSERT(!exec->hadException());
260     O->defineOwnProperty(exec, Identifier(exec, propertyName), descriptor, true);
261     return JSValue::encode(O);
262 }
263
264 static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* properties)
265 {
266     PropertyNameArray propertyNames(exec);
267     asObject(properties)->getOwnPropertyNames(exec, propertyNames);
268     size_t numProperties = propertyNames.size();
269     Vector<PropertyDescriptor> descriptors;
270     MarkedArgumentBuffer markBuffer;
271     for (size_t i = 0; i < numProperties; i++) {
272         PropertySlot slot;
273         JSValue prop = properties->get(exec, propertyNames[i]);
274         if (exec->hadException())
275             return jsNull();
276         PropertyDescriptor descriptor;
277         if (!toPropertyDescriptor(exec, prop, descriptor))
278             return jsNull();
279         descriptors.append(descriptor);
280         // Ensure we mark all the values that we're accumulating
281         if (descriptor.isDataDescriptor() && descriptor.value())
282             markBuffer.append(descriptor.value());
283         if (descriptor.isAccessorDescriptor()) {
284             if (descriptor.getter())
285                 markBuffer.append(descriptor.getter());
286             if (descriptor.setter())
287                 markBuffer.append(descriptor.setter());
288         }
289     }
290     for (size_t i = 0; i < numProperties; i++) {
291         object->defineOwnProperty(exec, propertyNames[i], descriptors[i], true);
292         if (exec->hadException())
293             return jsNull();
294     }
295     return object;
296 }
297
298 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState* exec)
299 {
300     if (!exec->argument(0).isObject())
301         return throwVMError(exec, createTypeError(exec, "Properties can only be defined on Objects."));
302     if (!exec->argument(1).isObject())
303         return throwVMError(exec, createTypeError(exec, "Property descriptor list must be an Object."));
304     return JSValue::encode(defineProperties(exec, asObject(exec->argument(0)), asObject(exec->argument(1))));
305 }
306
307 EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec)
308 {
309     if (!exec->argument(0).isObject() && !exec->argument(0).isNull())
310         return throwVMError(exec, createTypeError(exec, "Object prototype may only be an Object or null."));
311     JSObject* newObject = constructEmptyObject(exec);
312     newObject->setPrototype(exec->argument(0));
313     if (exec->argument(1).isUndefined())
314         return JSValue::encode(newObject);
315     if (!exec->argument(1).isObject())
316         return throwVMError(exec, createTypeError(exec, "Property descriptor list must be an Object."));
317     return JSValue::encode(defineProperties(exec, newObject, asObject(exec->argument(1))));
318 }
319
320 } // namespace JSC