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