/* | |
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org) | |
* Copyright (C) 2008 Apple Inc. All rights reserved. | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
* License as published by the Free Software Foundation; either | |
* version 2 of the License, or (at your option) any later version. | |
* | |
* This library is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
* Lesser General Public License for more details. | |
* | |
* You should have received a copy of the GNU Lesser General Public | |
* License along with this library; if not, write to the Free Software | |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
* | |
*/ | |
#include "config.h" | |
#include "ObjectConstructor.h" | |
#include "Error.h" | |
#include "JSFunction.h" | |
#include "JSArray.h" | |
#include "JSGlobalObject.h" | |
#include "ObjectPrototype.h" | |
#include "PropertyDescriptor.h" | |
#include "PropertyNameArray.h" | |
#include "PrototypeFunction.h" | |
namespace JSC { | |
ASSERT_CLASS_FITS_IN_CELL(ObjectConstructor); | |
static JSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState*, JSObject*, JSValue, const ArgList&); | |
static JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState*, JSObject*, JSValue, const ArgList&); | |
static JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState*, JSObject*, JSValue, const ArgList&); | |
static JSValue JSC_HOST_CALL objectConstructorKeys(ExecState*, JSObject*, JSValue, const ArgList&); | |
static JSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState*, JSObject*, JSValue, const ArgList&); | |
static JSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState*, JSObject*, JSValue, const ArgList&); | |
static JSValue JSC_HOST_CALL objectConstructorCreate(ExecState*, JSObject*, JSValue, const ArgList&); | |
ObjectConstructor::ObjectConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, ObjectPrototype* objectPrototype, Structure* prototypeFunctionStructure) | |
: InternalFunction(&exec->globalData(), structure, Identifier(exec, "Object")) | |
{ | |
// ECMA 15.2.3.1 | |
putDirectWithoutTransition(exec->propertyNames().prototype, objectPrototype, DontEnum | DontDelete | ReadOnly); | |
// no. of arguments for constructor | |
putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 1), ReadOnly | DontEnum | DontDelete); | |
putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().getPrototypeOf, objectConstructorGetPrototypeOf), DontEnum); | |
putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().getOwnPropertyDescriptor, objectConstructorGetOwnPropertyDescriptor), DontEnum); | |
putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().getOwnPropertyNames, objectConstructorGetOwnPropertyNames), DontEnum); | |
putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().keys, objectConstructorKeys), DontEnum); | |
putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 3, exec->propertyNames().defineProperty, objectConstructorDefineProperty), DontEnum); | |
putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().defineProperties, objectConstructorDefineProperties), DontEnum); | |
putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().create, objectConstructorCreate), DontEnum); | |
} | |
// ECMA 15.2.2 | |
static ALWAYS_INLINE JSObject* constructObject(ExecState* exec, const ArgList& args) | |
{ | |
JSValue arg = args.at(0); | |
if (arg.isUndefinedOrNull()) | |
return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure()); | |
return arg.toObject(exec); | |
} | |
static JSObject* constructWithObjectConstructor(ExecState* exec, JSObject*, const ArgList& args) | |
{ | |
return constructObject(exec, args); | |
} | |
ConstructType ObjectConstructor::getConstructData(ConstructData& constructData) | |
{ | |
constructData.native.function = constructWithObjectConstructor; | |
return ConstructTypeHost; | |
} | |
static JSValue JSC_HOST_CALL callObjectConstructor(ExecState* exec, JSObject*, JSValue, const ArgList& args) | |
{ | |
return constructObject(exec, args); | |
} | |
CallType ObjectConstructor::getCallData(CallData& callData) | |
{ | |
callData.native.function = callObjectConstructor; | |
return CallTypeHost; | |
} | |
JSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec, JSObject*, JSValue, const ArgList& args) | |
{ | |
if (!args.at(0).isObject()) | |
return throwError(exec, TypeError, "Requested prototype of a value that is not an object."); | |
return asObject(args.at(0))->prototype(); | |
} | |
JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec, JSObject*, JSValue, const ArgList& args) | |
{ | |
if (!args.at(0).isObject()) | |
return throwError(exec, TypeError, "Requested property descriptor of a value that is not an object."); | |
UString propertyName = args.at(1).toString(exec); | |
if (exec->hadException()) | |
return jsNull(); | |
JSObject* object = asObject(args.at(0)); | |
PropertyDescriptor descriptor; | |
if (!object->getOwnPropertyDescriptor(exec, Identifier(exec, propertyName), descriptor)) | |
return jsUndefined(); | |
if (exec->hadException()) | |
return jsUndefined(); | |
JSObject* description = constructEmptyObject(exec); | |
if (!descriptor.isAccessorDescriptor()) { | |
description->putDirect(exec->propertyNames().value, descriptor.value() ? descriptor.value() : jsUndefined(), 0); | |
description->putDirect(exec->propertyNames().writable, jsBoolean(descriptor.writable()), 0); | |
} else { | |
description->putDirect(exec->propertyNames().get, descriptor.getter() ? descriptor.getter() : jsUndefined(), 0); | |
description->putDirect(exec->propertyNames().set, descriptor.setter() ? descriptor.setter() : jsUndefined(), 0); | |
} | |
description->putDirect(exec->propertyNames().enumerable, jsBoolean(descriptor.enumerable()), 0); | |
description->putDirect(exec->propertyNames().configurable, jsBoolean(descriptor.configurable()), 0); | |
return description; | |
} | |
// FIXME: Use the enumeration cache. | |
JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exec, JSObject*, JSValue, const ArgList& args) | |
{ | |
if (!args.at(0).isObject()) | |
return throwError(exec, TypeError, "Requested property names of a value that is not an object."); | |
PropertyNameArray properties(exec); | |
asObject(args.at(0))->getOwnPropertyNames(exec, properties, IncludeDontEnumProperties); | |
JSArray* names = constructEmptyArray(exec); | |
size_t numProperties = properties.size(); | |
for (size_t i = 0; i < numProperties; i++) | |
names->push(exec, jsOwnedString(exec, properties[i].ustring())); | |
return names; | |
} | |
// FIXME: Use the enumeration cache. | |
JSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec, JSObject*, JSValue, const ArgList& args) | |
{ | |
if (!args.at(0).isObject()) | |
return throwError(exec, TypeError, "Requested keys of a value that is not an object."); | |
PropertyNameArray properties(exec); | |
asObject(args.at(0))->getOwnPropertyNames(exec, properties); | |
JSArray* keys = constructEmptyArray(exec); | |
size_t numProperties = properties.size(); | |
for (size_t i = 0; i < numProperties; i++) | |
keys->push(exec, jsOwnedString(exec, properties[i].ustring())); | |
return keys; | |
} | |
// ES5 8.10.5 ToPropertyDescriptor | |
static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc) | |
{ | |
if (!in.isObject()) { | |
throwError(exec, TypeError, "Property description must be an object."); | |
return false; | |
} | |
JSObject* description = asObject(in); | |
PropertySlot enumerableSlot(description); | |
if (description->getPropertySlot(exec, exec->propertyNames().enumerable, enumerableSlot)) { | |
desc.setEnumerable(enumerableSlot.getValue(exec, exec->propertyNames().enumerable).toBoolean(exec)); | |
if (exec->hadException()) | |
return false; | |
} | |
PropertySlot configurableSlot(description); | |
if (description->getPropertySlot(exec, exec->propertyNames().configurable, configurableSlot)) { | |
desc.setConfigurable(configurableSlot.getValue(exec, exec->propertyNames().configurable).toBoolean(exec)); | |
if (exec->hadException()) | |
return false; | |
} | |
JSValue value; | |
PropertySlot valueSlot(description); | |
if (description->getPropertySlot(exec, exec->propertyNames().value, valueSlot)) { | |
desc.setValue(valueSlot.getValue(exec, exec->propertyNames().value)); | |
if (exec->hadException()) | |
return false; | |
} | |
PropertySlot writableSlot(description); | |
if (description->getPropertySlot(exec, exec->propertyNames().writable, writableSlot)) { | |
desc.setWritable(writableSlot.getValue(exec, exec->propertyNames().writable).toBoolean(exec)); | |
if (exec->hadException()) | |
return false; | |
} | |
PropertySlot getSlot(description); | |
if (description->getPropertySlot(exec, exec->propertyNames().get, getSlot)) { | |
JSValue get = getSlot.getValue(exec, exec->propertyNames().get); | |
if (exec->hadException()) | |
return false; | |
if (!get.isUndefined()) { | |
CallData callData; | |
if (get.getCallData(callData) == CallTypeNone) { | |
throwError(exec, TypeError, "Getter must be a function."); | |
return false; | |
} | |
} else | |
get = JSValue(); | |
desc.setGetter(get); | |
} | |
PropertySlot setSlot(description); | |
if (description->getPropertySlot(exec, exec->propertyNames().set, setSlot)) { | |
JSValue set = setSlot.getValue(exec, exec->propertyNames().set); | |
if (exec->hadException()) | |
return false; | |
if (!set.isUndefined()) { | |
CallData callData; | |
if (set.getCallData(callData) == CallTypeNone) { | |
throwError(exec, TypeError, "Setter must be a function."); | |
return false; | |
} | |
} else | |
set = JSValue(); | |
desc.setSetter(set); | |
} | |
if (!desc.isAccessorDescriptor()) | |
return true; | |
if (desc.value()) { | |
throwError(exec, TypeError, "Invalid property. 'value' present on property with getter or setter."); | |
return false; | |
} | |
if (desc.writablePresent()) { | |
throwError(exec, TypeError, "Invalid property. 'writable' present on property with getter or setter."); | |
return false; | |
} | |
return true; | |
} | |
JSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState* exec, JSObject*, JSValue, const ArgList& args) | |
{ | |
if (!args.at(0).isObject()) | |
return throwError(exec, TypeError, "Properties can only be defined on Objects."); | |
JSObject* O = asObject(args.at(0)); | |
UString propertyName = args.at(1).toString(exec); | |
if (exec->hadException()) | |
return jsNull(); | |
PropertyDescriptor descriptor; | |
if (!toPropertyDescriptor(exec, args.at(2), descriptor)) | |
return jsNull(); | |
ASSERT((descriptor.attributes() & (Getter | Setter)) || (!descriptor.isAccessorDescriptor())); | |
ASSERT(!exec->hadException()); | |
O->defineOwnProperty(exec, Identifier(exec, propertyName), descriptor, true); | |
return O; | |
} | |
static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* properties) | |
{ | |
PropertyNameArray propertyNames(exec); | |
asObject(properties)->getOwnPropertyNames(exec, propertyNames); | |
size_t numProperties = propertyNames.size(); | |
Vector<PropertyDescriptor> descriptors; | |
MarkedArgumentBuffer markBuffer; | |
for (size_t i = 0; i < numProperties; i++) { | |
PropertySlot slot; | |
JSValue prop = properties->get(exec, propertyNames[i]); | |
if (exec->hadException()) | |
return jsNull(); | |
PropertyDescriptor descriptor; | |
if (!toPropertyDescriptor(exec, prop, descriptor)) | |
return jsNull(); | |
descriptors.append(descriptor); | |
// Ensure we mark all the values that we're accumulating | |
if (descriptor.isDataDescriptor() && descriptor.value()) | |
markBuffer.append(descriptor.value()); | |
if (descriptor.isAccessorDescriptor()) { | |
if (descriptor.getter()) | |
markBuffer.append(descriptor.getter()); | |
if (descriptor.setter()) | |
markBuffer.append(descriptor.setter()); | |
} | |
} | |
for (size_t i = 0; i < numProperties; i++) { | |
object->defineOwnProperty(exec, propertyNames[i], descriptors[i], true); | |
if (exec->hadException()) | |
return jsNull(); | |
} | |
return object; | |
} | |
JSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState* exec, JSObject*, JSValue, const ArgList& args) | |
{ | |
if (!args.at(0).isObject()) | |
return throwError(exec, TypeError, "Properties can only be defined on Objects."); | |
if (!args.at(1).isObject()) | |
return throwError(exec, TypeError, "Property descriptor list must be an Object."); | |
return defineProperties(exec, asObject(args.at(0)), asObject(args.at(1))); | |
} | |
JSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec, JSObject*, JSValue, const ArgList& args) | |
{ | |
if (!args.at(0).isObject() && !args.at(0).isNull()) | |
return throwError(exec, TypeError, "Object prototype may only be an Object or null."); | |
JSObject* newObject = constructEmptyObject(exec); | |
newObject->setPrototype(args.at(0)); | |
if (args.at(1).isUndefined()) | |
return newObject; | |
if (!args.at(1).isObject()) | |
return throwError(exec, TypeError, "Property descriptor list must be an Object."); | |
return defineProperties(exec, newObject, asObject(args.at(1))); | |
} | |
} // namespace JSC |