JavaScriptCore/runtime/ObjectConstructor.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     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