Bug 55736 - Implement seal/freeze/preventExtensions for normal object types.
[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 "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, NonNullPassRefPtr<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, const ArgList& args)
99 {
100     JSValue arg = args.at(0);
101     if (arg.isUndefinedOrNull())
102         return constructEmptyObject(exec);
103     return arg.toObject(exec);
104 }
105
106 static EncodedJSValue JSC_HOST_CALL constructWithObjectConstructor(ExecState* exec)
107 {
108     ArgList args(exec);
109     return JSValue::encode(constructObject(exec, 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, 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     return JSValue::encode(asObject(exec->argument(0))->prototype());
135 }
136
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.
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.
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
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
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
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
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
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->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
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();
359     return JSValue::encode(obj);
360 }
361
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();
368     return JSValue::encode(obj);
369 }
370
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();
377     return JSValue::encode(obj);
378 }
379
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()));
386 }
387
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()));
394 }
395
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