2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2008 Apple Inc. All rights reserved.
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.
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.
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
22 #include "ObjectConstructor.h"
25 #include "ExceptionHelpers.h"
26 #include "JSFunction.h"
28 #include "JSGlobalObject.h"
29 #include "ObjectPrototype.h"
30 #include "PropertyDescriptor.h"
31 #include "PropertyNameArray.h"
32 #include "PrototypeFunction.h"
36 ASSERT_CLASS_FITS_IN_CELL(ObjectConstructor);
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*);
46 ObjectConstructor::ObjectConstructor(ExecState* exec, JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure, ObjectPrototype* objectPrototype, Structure* prototypeFunctionStructure)
47 : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, "Object"))
50 putDirectWithoutTransition(exec->globalData(), exec->propertyNames().prototype, objectPrototype, DontEnum | DontDelete | ReadOnly);
52 // no. of arguments for constructor
53 putDirectWithoutTransition(exec->globalData(), exec->propertyNames().length, jsNumber(1), ReadOnly | DontEnum | DontDelete);
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);
65 static ALWAYS_INLINE JSObject* constructObject(ExecState* exec, const ArgList& args)
67 JSValue arg = args.at(0);
68 if (arg.isUndefinedOrNull())
69 return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure());
70 return arg.toObject(exec);
73 static EncodedJSValue JSC_HOST_CALL constructWithObjectConstructor(ExecState* exec)
76 return JSValue::encode(constructObject(exec, args));
79 ConstructType ObjectConstructor::getConstructData(ConstructData& constructData)
81 constructData.native.function = constructWithObjectConstructor;
82 return ConstructTypeHost;
85 static EncodedJSValue JSC_HOST_CALL callObjectConstructor(ExecState* exec)
88 return JSValue::encode(constructObject(exec, args));
91 CallType ObjectConstructor::getCallData(CallData& callData)
93 callData.native.function = callObjectConstructor;
97 EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec)
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());
104 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec)
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());
118 JSObject* description = constructEmptyObject(exec);
119 if (!descriptor.isAccessorDescriptor()) {
120 description->putDirect(exec->globalData(), exec->propertyNames().value, descriptor.value() ? descriptor.value() : jsUndefined(), 0);
121 description->putDirect(exec->globalData(), exec->propertyNames().writable, jsBoolean(descriptor.writable()), 0);
123 description->putDirect(exec->globalData(), exec->propertyNames().get, descriptor.getter() ? descriptor.getter() : jsUndefined(), 0);
124 description->putDirect(exec->globalData(), exec->propertyNames().set, descriptor.setter() ? descriptor.setter() : jsUndefined(), 0);
127 description->putDirect(exec->globalData(), exec->propertyNames().enumerable, jsBoolean(descriptor.enumerable()), 0);
128 description->putDirect(exec->globalData(), exec->propertyNames().configurable, jsBoolean(descriptor.configurable()), 0);
130 return JSValue::encode(description);
133 // FIXME: Use the enumeration cache.
134 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exec)
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);
147 // FIXME: Use the enumeration cache.
148 EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec)
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);
161 // ES5 8.10.5 ToPropertyDescriptor
162 static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc)
164 if (!in.isObject()) {
165 throwError(exec, createTypeError(exec, "Property description must be an object."));
168 JSObject* description = asObject(in);
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())
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())
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())
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())
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())
204 if (!get.isUndefined()) {
206 if (getCallData(get, callData) == CallTypeNone) {
207 throwError(exec, createTypeError(exec, "Getter must be a function."));
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())
220 if (!set.isUndefined()) {
222 if (getCallData(set, callData) == CallTypeNone) {
223 throwError(exec, createTypeError(exec, "Setter must be a function."));
232 if (!desc.isAccessorDescriptor())
236 throwError(exec, createTypeError(exec, "Invalid property. 'value' present on property with getter or setter."));
240 if (desc.writablePresent()) {
241 throwError(exec, createTypeError(exec, "Invalid property. 'writable' present on property with getter or setter."));
247 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState* exec)
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);
264 static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* properties)
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++) {
273 JSValue prop = properties->get(exec, propertyNames[i]);
274 if (exec->hadException())
276 PropertyDescriptor descriptor;
277 if (!toPropertyDescriptor(exec, prop, descriptor))
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());
290 for (size_t i = 0; i < numProperties; i++) {
291 object->defineOwnProperty(exec, propertyNames[i], descriptors[i], true);
292 if (exec->hadException())
298 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState* exec)
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))));
307 EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec)
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))));