|
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->propertyNames().prototype, objectPrototype, DontEnum | DontDelete | ReadOnly); |
|
51 |
|
52 // no. of arguments for constructor |
|
53 putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 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->propertyNames().value, descriptor.value() ? descriptor.value() : jsUndefined(), 0); |
|
121 description->putDirect(exec->propertyNames().writable, jsBoolean(descriptor.writable()), 0); |
|
122 } else { |
|
123 description->putDirect(exec->propertyNames().get, descriptor.getter() ? descriptor.getter() : jsUndefined(), 0); |
|
124 description->putDirect(exec->propertyNames().set, descriptor.setter() ? descriptor.setter() : jsUndefined(), 0); |
|
125 } |
|
126 |
|
127 description->putDirect(exec->propertyNames().enumerable, jsBoolean(descriptor.enumerable()), 0); |
|
128 description->putDirect(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 |