Home | History | Annotate | Download | only in runtime
      1 /*
      2  *  Copyright (C) 1999-2000 Harri Porten (porten (at) 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 "Lookup.h"
     30 #include "ObjectPrototype.h"
     31 #include "PropertyDescriptor.h"
     32 #include "PropertyNameArray.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 static EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState*);
     46 static EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState*);
     47 static EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState*);
     48 static EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState*);
     49 static EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState*);
     50 static EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState*);
     51 
     52 }
     53 
     54 #include "ObjectConstructor.lut.h"
     55 
     56 namespace JSC {
     57 
     58 const ClassInfo ObjectConstructor::s_info = { "Function", &InternalFunction::s_info, 0, ExecState::objectConstructorTable };
     59 
     60 /* Source for ObjectConstructor.lut.h
     61 @begin objectConstructorTable
     62   getPrototypeOf            objectConstructorGetPrototypeOf             DontEnum|Function 1
     63   getOwnPropertyDescriptor  objectConstructorGetOwnPropertyDescriptor   DontEnum|Function 2
     64   getOwnPropertyNames       objectConstructorGetOwnPropertyNames        DontEnum|Function 1
     65   keys                      objectConstructorKeys                       DontEnum|Function 1
     66   defineProperty            objectConstructorDefineProperty             DontEnum|Function 3
     67   defineProperties          objectConstructorDefineProperties           DontEnum|Function 2
     68   create                    objectConstructorCreate                     DontEnum|Function 2
     69   seal                      objectConstructorSeal                       DontEnum|Function 1
     70   freeze                    objectConstructorFreeze                     DontEnum|Function 1
     71   preventExtensions         objectConstructorPreventExtensions          DontEnum|Function 1
     72   isSealed                  objectConstructorIsSealed                   DontEnum|Function 1
     73   isFrozen                  objectConstructorIsFrozen                   DontEnum|Function 1
     74   isExtensible              objectConstructorIsExtensible               DontEnum|Function 1
     75 @end
     76 */
     77 
     78 ObjectConstructor::ObjectConstructor(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, ObjectPrototype* objectPrototype)
     79     : InternalFunction(&exec->globalData(), globalObject, structure, Identifier(exec, "Object"))
     80 {
     81     // ECMA 15.2.3.1
     82     putDirectWithoutTransition(exec->globalData(), exec->propertyNames().prototype, objectPrototype, DontEnum | DontDelete | ReadOnly);
     83     // no. of arguments for constructor
     84     putDirectWithoutTransition(exec->globalData(), exec->propertyNames().length, jsNumber(1), ReadOnly | DontEnum | DontDelete);
     85 }
     86 
     87 bool ObjectConstructor::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot &slot)
     88 {
     89     return getStaticFunctionSlot<JSObject>(exec, ExecState::objectConstructorTable(exec), this, propertyName, slot);
     90 }
     91 
     92 bool ObjectConstructor::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
     93 {
     94     return getStaticFunctionDescriptor<JSObject>(exec, ExecState::objectConstructorTable(exec), this, propertyName, descriptor);
     95 }
     96 
     97 // ECMA 15.2.2
     98 static ALWAYS_INLINE JSObject* constructObject(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args)
     99 {
    100     JSValue arg = args.at(0);
    101     if (arg.isUndefinedOrNull())
    102         return constructEmptyObject(exec, globalObject);
    103     return arg.toObject(exec, globalObject);
    104 }
    105 
    106 static EncodedJSValue JSC_HOST_CALL constructWithObjectConstructor(ExecState* exec)
    107 {
    108     ArgList args(exec);
    109     return JSValue::encode(constructObject(exec, asInternalFunction(exec->callee())->globalObject(), args));
    110 }
    111 
    112 ConstructType ObjectConstructor::getConstructData(ConstructData& constructData)
    113 {
    114     constructData.native.function = constructWithObjectConstructor;
    115     return ConstructTypeHost;
    116 }
    117 
    118 static EncodedJSValue JSC_HOST_CALL callObjectConstructor(ExecState* exec)
    119 {
    120     ArgList args(exec);
    121     return JSValue::encode(constructObject(exec, asInternalFunction(exec->callee())->globalObject(), args));
    122 }
    123 
    124 CallType ObjectConstructor::getCallData(CallData& callData)
    125 {
    126     callData.native.function = callObjectConstructor;
    127     return CallTypeHost;
    128 }
    129 
    130 EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec)
    131 {
    132     if (!exec->argument(0).isObject())
    133         return throwVMError(exec, createTypeError(exec, "Requested prototype of a value that is not an object."));
    134     return JSValue::encode(asObject(exec->argument(0))->prototype());
    135 }
    136 
    137 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec)
    138 {
    139     if (!exec->argument(0).isObject())
    140         return throwVMError(exec, createTypeError(exec, "Requested property descriptor of a value that is not an object."));
    141     UString propertyName = exec->argument(1).toString(exec);
    142     if (exec->hadException())
    143         return JSValue::encode(jsNull());
    144     JSObject* object = asObject(exec->argument(0));
    145     PropertyDescriptor descriptor;
    146     if (!object->getOwnPropertyDescriptor(exec, Identifier(exec, propertyName), descriptor))
    147         return JSValue::encode(jsUndefined());
    148     if (exec->hadException())
    149         return JSValue::encode(jsUndefined());
    150 
    151     JSObject* description = constructEmptyObject(exec);
    152     if (!descriptor.isAccessorDescriptor()) {
    153         description->putDirect(exec->globalData(), exec->propertyNames().value, descriptor.value() ? descriptor.value() : jsUndefined(), 0);
    154         description->putDirect(exec->globalData(), exec->propertyNames().writable, jsBoolean(descriptor.writable()), 0);
    155     } else {
    156         description->putDirect(exec->globalData(), exec->propertyNames().get, descriptor.getter() ? descriptor.getter() : jsUndefined(), 0);
    157         description->putDirect(exec->globalData(), exec->propertyNames().set, descriptor.setter() ? descriptor.setter() : jsUndefined(), 0);
    158     }
    159 
    160     description->putDirect(exec->globalData(), exec->propertyNames().enumerable, jsBoolean(descriptor.enumerable()), 0);
    161     description->putDirect(exec->globalData(), exec->propertyNames().configurable, jsBoolean(descriptor.configurable()), 0);
    162 
    163     return JSValue::encode(description);
    164 }
    165 
    166 // FIXME: Use the enumeration cache.
    167 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exec)
    168 {
    169     if (!exec->argument(0).isObject())
    170         return throwVMError(exec, createTypeError(exec, "Requested property names of a value that is not an object."));
    171     PropertyNameArray properties(exec);
    172     asObject(exec->argument(0))->getOwnPropertyNames(exec, properties, IncludeDontEnumProperties);
    173     JSArray* names = constructEmptyArray(exec);
    174     size_t numProperties = properties.size();
    175     for (size_t i = 0; i < numProperties; i++)
    176         names->push(exec, jsOwnedString(exec, properties[i].ustring()));
    177     return JSValue::encode(names);
    178 }
    179 
    180 // FIXME: Use the enumeration cache.
    181 EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec)
    182 {
    183     if (!exec->argument(0).isObject())
    184         return throwVMError(exec, createTypeError(exec, "Requested keys of a value that is not an object."));
    185     PropertyNameArray properties(exec);
    186     asObject(exec->argument(0))->getOwnPropertyNames(exec, properties);
    187     JSArray* keys = constructEmptyArray(exec);
    188     size_t numProperties = properties.size();
    189     for (size_t i = 0; i < numProperties; i++)
    190         keys->push(exec, jsOwnedString(exec, properties[i].ustring()));
    191     return JSValue::encode(keys);
    192 }
    193 
    194 // ES5 8.10.5 ToPropertyDescriptor
    195 static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc)
    196 {
    197     if (!in.isObject()) {
    198         throwError(exec, createTypeError(exec, "Property description must be an object."));
    199         return false;
    200     }
    201     JSObject* description = asObject(in);
    202 
    203     PropertySlot enumerableSlot(description);
    204     if (description->getPropertySlot(exec, exec->propertyNames().enumerable, enumerableSlot)) {
    205         desc.setEnumerable(enumerableSlot.getValue(exec, exec->propertyNames().enumerable).toBoolean(exec));
    206         if (exec->hadException())
    207             return false;
    208     }
    209 
    210     PropertySlot configurableSlot(description);
    211     if (description->getPropertySlot(exec, exec->propertyNames().configurable, configurableSlot)) {
    212         desc.setConfigurable(configurableSlot.getValue(exec, exec->propertyNames().configurable).toBoolean(exec));
    213         if (exec->hadException())
    214             return false;
    215     }
    216 
    217     JSValue value;
    218     PropertySlot valueSlot(description);
    219     if (description->getPropertySlot(exec, exec->propertyNames().value, valueSlot)) {
    220         desc.setValue(valueSlot.getValue(exec, exec->propertyNames().value));
    221         if (exec->hadException())
    222             return false;
    223     }
    224 
    225     PropertySlot writableSlot(description);
    226     if (description->getPropertySlot(exec, exec->propertyNames().writable, writableSlot)) {
    227         desc.setWritable(writableSlot.getValue(exec, exec->propertyNames().writable).toBoolean(exec));
    228         if (exec->hadException())
    229             return false;
    230     }
    231 
    232     PropertySlot getSlot(description);
    233     if (description->getPropertySlot(exec, exec->propertyNames().get, getSlot)) {
    234         JSValue get = getSlot.getValue(exec, exec->propertyNames().get);
    235         if (exec->hadException())
    236             return false;
    237         if (!get.isUndefined()) {
    238             CallData callData;
    239             if (getCallData(get, callData) == CallTypeNone) {
    240                 throwError(exec, createTypeError(exec, "Getter must be a function."));
    241                 return false;
    242             }
    243         } else
    244             get = JSValue();
    245         desc.setGetter(get);
    246     }
    247 
    248     PropertySlot setSlot(description);
    249     if (description->getPropertySlot(exec, exec->propertyNames().set, setSlot)) {
    250         JSValue set = setSlot.getValue(exec, exec->propertyNames().set);
    251         if (exec->hadException())
    252             return false;
    253         if (!set.isUndefined()) {
    254             CallData callData;
    255             if (getCallData(set, callData) == CallTypeNone) {
    256                 throwError(exec, createTypeError(exec, "Setter must be a function."));
    257                 return false;
    258             }
    259         } else
    260             set = JSValue();
    261 
    262         desc.setSetter(set);
    263     }
    264 
    265     if (!desc.isAccessorDescriptor())
    266         return true;
    267 
    268     if (desc.value()) {
    269         throwError(exec, createTypeError(exec, "Invalid property.  'value' present on property with getter or setter."));
    270         return false;
    271     }
    272 
    273     if (desc.writablePresent()) {
    274         throwError(exec, createTypeError(exec, "Invalid property.  'writable' present on property with getter or setter."));
    275         return false;
    276     }
    277     return true;
    278 }
    279 
    280 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState* exec)
    281 {
    282     if (!exec->argument(0).isObject())
    283         return throwVMError(exec, createTypeError(exec, "Properties can only be defined on Objects."));
    284     JSObject* O = asObject(exec->argument(0));
    285     UString propertyName = exec->argument(1).toString(exec);
    286     if (exec->hadException())
    287         return JSValue::encode(jsNull());
    288     PropertyDescriptor descriptor;
    289     if (!toPropertyDescriptor(exec, exec->argument(2), descriptor))
    290         return JSValue::encode(jsNull());
    291     ASSERT((descriptor.attributes() & (Getter | Setter)) || (!descriptor.isAccessorDescriptor()));
    292     ASSERT(!exec->hadException());
    293     O->defineOwnProperty(exec, Identifier(exec, propertyName), descriptor, true);
    294     return JSValue::encode(O);
    295 }
    296 
    297 static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* properties)
    298 {
    299     PropertyNameArray propertyNames(exec);
    300     asObject(properties)->getOwnPropertyNames(exec, propertyNames);
    301     size_t numProperties = propertyNames.size();
    302     Vector<PropertyDescriptor> descriptors;
    303     MarkedArgumentBuffer markBuffer;
    304     for (size_t i = 0; i < numProperties; i++) {
    305         PropertySlot slot;
    306         JSValue prop = properties->get(exec, propertyNames[i]);
    307         if (exec->hadException())
    308             return jsNull();
    309         PropertyDescriptor descriptor;
    310         if (!toPropertyDescriptor(exec, prop, descriptor))
    311             return jsNull();
    312         descriptors.append(descriptor);
    313         // Ensure we mark all the values that we're accumulating
    314         if (descriptor.isDataDescriptor() && descriptor.value())
    315             markBuffer.append(descriptor.value());
    316         if (descriptor.isAccessorDescriptor()) {
    317             if (descriptor.getter())
    318                 markBuffer.append(descriptor.getter());
    319             if (descriptor.setter())
    320                 markBuffer.append(descriptor.setter());
    321         }
    322     }
    323     for (size_t i = 0; i < numProperties; i++) {
    324         object->defineOwnProperty(exec, propertyNames[i], descriptors[i], true);
    325         if (exec->hadException())
    326             return jsNull();
    327     }
    328     return object;
    329 }
    330 
    331 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState* exec)
    332 {
    333     if (!exec->argument(0).isObject())
    334         return throwVMError(exec, createTypeError(exec, "Properties can only be defined on Objects."));
    335     if (!exec->argument(1).isObject())
    336         return throwVMError(exec, createTypeError(exec, "Property descriptor list must be an Object."));
    337     return JSValue::encode(defineProperties(exec, asObject(exec->argument(0)), asObject(exec->argument(1))));
    338 }
    339 
    340 EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec)
    341 {
    342     if (!exec->argument(0).isObject() && !exec->argument(0).isNull())
    343         return throwVMError(exec, createTypeError(exec, "Object prototype may only be an Object or null."));
    344     JSObject* newObject = constructEmptyObject(exec);
    345     newObject->setPrototype(exec->globalData(), exec->argument(0));
    346     if (exec->argument(1).isUndefined())
    347         return JSValue::encode(newObject);
    348     if (!exec->argument(1).isObject())
    349         return throwVMError(exec, createTypeError(exec, "Property descriptor list must be an Object."));
    350     return JSValue::encode(defineProperties(exec, newObject, asObject(exec->argument(1))));
    351 }
    352 
    353 EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState* exec)
    354 {
    355     JSValue obj = exec->argument(0);
    356     if (!obj.isObject())
    357         return throwVMError(exec, createTypeError(exec, "Object.seal can only be called on Objects."));
    358     asObject(obj)->seal(exec->globalData());
    359     return JSValue::encode(obj);
    360 }
    361 
    362 EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState* exec)
    363 {
    364     JSValue obj = exec->argument(0);
    365     if (!obj.isObject())
    366         return throwVMError(exec, createTypeError(exec, "Object.freeze can only be called on Objects."));
    367     asObject(obj)->freeze(exec->globalData());
    368     return JSValue::encode(obj);
    369 }
    370 
    371 EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState* exec)
    372 {
    373     JSValue obj = exec->argument(0);
    374     if (!obj.isObject())
    375         return throwVMError(exec, createTypeError(exec, "Object.preventExtensions can only be called on Objects."));
    376     asObject(obj)->preventExtensions(exec->globalData());
    377     return JSValue::encode(obj);
    378 }
    379 
    380 EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState* exec)
    381 {
    382     JSValue obj = exec->argument(0);
    383     if (!obj.isObject())
    384         return throwVMError(exec, createTypeError(exec, "Object.isSealed can only be called on Objects."));
    385     return JSValue::encode(jsBoolean(asObject(obj)->isSealed(exec->globalData())));
    386 }
    387 
    388 EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState* exec)
    389 {
    390     JSValue obj = exec->argument(0);
    391     if (!obj.isObject())
    392         return throwVMError(exec, createTypeError(exec, "Object.isFrozen can only be called on Objects."));
    393     return JSValue::encode(jsBoolean(asObject(obj)->isFrozen(exec->globalData())));
    394 }
    395 
    396 EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState* exec)
    397 {
    398     JSValue obj = exec->argument(0);
    399     if (!obj.isObject())
    400         return throwVMError(exec, createTypeError(exec, "Object.isExtensible can only be called on Objects."));
    401     return JSValue::encode(jsBoolean(asObject(obj)->isExtensible()));
    402 }
    403 
    404 } // namespace JSC
    405