]>
Commit | Line | Data |
---|---|---|
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 | ||
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 | |
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 | ||
72 | static JSObject* constructWithObjectConstructor(ExecState* exec, JSObject*, const ArgList& args) | |
73 | { | |
74 | return constructObject(exec, args); | |
75 | } | |
76 | ||
77 | ConstructType ObjectConstructor::getConstructData(ConstructData& constructData) | |
78 | { | |
79 | constructData.native.function = constructWithObjectConstructor; | |
80 | return ConstructTypeHost; | |
81 | } | |
82 | ||
83 | static JSValue JSC_HOST_CALL callObjectConstructor(ExecState* exec, JSObject*, JSValue, const ArgList& args) | |
84 | { | |
85 | return constructObject(exec, args); | |
86 | } | |
87 | ||
88 | CallType ObjectConstructor::getCallData(CallData& callData) | |
89 | { | |
90 | callData.native.function = callObjectConstructor; | |
91 | return CallTypeHost; | |
92 | } | |
93 | ||
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 | ||
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. | |
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. | |
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 | |
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 | ||
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 | ||
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 | ||
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 | ||
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 |