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