JSObject.cpp revision 81bc750723a18f21cd17d1b173cd2a4dda9cea6e
1/*
2 *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 *  Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Apple Inc. All rights reserved.
5 *  Copyright (C) 2007 Eric Seidel (eric@webkit.org)
6 *
7 *  This library is free software; you can redistribute it and/or
8 *  modify it under the terms of the GNU Library General Public
9 *  License as published by the Free Software Foundation; either
10 *  version 2 of the License, or (at your option) any later version.
11 *
12 *  This library is distributed in the hope that it will be useful,
13 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 *  Library General Public License for more details.
16 *
17 *  You should have received a copy of the GNU Library General Public License
18 *  along with this library; see the file COPYING.LIB.  If not, write to
19 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 *  Boston, MA 02110-1301, USA.
21 *
22 */
23
24#include "config.h"
25#include "JSObject.h"
26
27#include "DatePrototype.h"
28#include "ErrorConstructor.h"
29#include "GetterSetter.h"
30#include "JSFunction.h"
31#include "JSGlobalObject.h"
32#include "NativeErrorConstructor.h"
33#include "ObjectPrototype.h"
34#include "PropertyDescriptor.h"
35#include "PropertyNameArray.h"
36#include "Lookup.h"
37#include "Nodes.h"
38#include "Operations.h"
39#include <math.h>
40#include <wtf/Assertions.h>
41
42namespace JSC {
43
44ASSERT_CLASS_FITS_IN_CELL(JSObject);
45ASSERT_CLASS_FITS_IN_CELL(JSNonFinalObject);
46ASSERT_CLASS_FILLS_CELL(JSFinalObject);
47
48const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property.";
49
50const ClassInfo JSObject::s_info = { "Object", 0, 0, 0 };
51
52static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode)
53{
54    // Add properties from the static hashtables of properties
55    for (; classInfo; classInfo = classInfo->parentClass) {
56        const HashTable* table = classInfo->propHashTable(exec);
57        if (!table)
58            continue;
59        table->initializeIfNeeded(exec);
60        ASSERT(table->table);
61
62        int hashSizeMask = table->compactSize - 1;
63        const HashEntry* entry = table->table;
64        for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
65            if (entry->key() && (!(entry->attributes() & DontEnum) || (mode == IncludeDontEnumProperties)))
66                propertyNames.add(entry->key());
67        }
68    }
69}
70
71void JSObject::markChildren(MarkStack& markStack)
72{
73#ifndef NDEBUG
74    bool wasCheckingForDefaultMarkViolation = markStack.m_isCheckingForDefaultMarkViolation;
75    markStack.m_isCheckingForDefaultMarkViolation = false;
76#endif
77
78    markChildrenDirect(markStack);
79
80#ifndef NDEBUG
81    markStack.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
82#endif
83}
84
85UString JSObject::className() const
86{
87    const ClassInfo* info = classInfo();
88    ASSERT(info);
89    return info->className;
90}
91
92bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
93{
94    return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
95}
96
97static void throwSetterError(ExecState* exec)
98{
99    throwError(exec, createTypeError(exec, "setting a property that has only a getter"));
100}
101
102// ECMA 8.6.2.2
103void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
104{
105    ASSERT(value);
106    ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
107
108    if (propertyName == exec->propertyNames().underscoreProto) {
109        // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
110        if (!value.isObject() && !value.isNull())
111            return;
112        if (!setPrototypeWithCycleCheck(value))
113            throwError(exec, createError(exec, "cyclic __proto__ value"));
114        return;
115    }
116
117    // Check if there are any setters or getters in the prototype chain
118    JSValue prototype;
119    for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) {
120        prototype = obj->prototype();
121        if (prototype.isNull()) {
122            if (!putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot) && slot.isStrictMode())
123                throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
124            return;
125        }
126    }
127
128    unsigned attributes;
129    JSCell* specificValue;
130    if ((m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly) {
131        if (slot.isStrictMode())
132            throwError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError));
133        return;
134    }
135
136    for (JSObject* obj = this; ; obj = asObject(prototype)) {
137        if (JSValue gs = obj->getDirect(propertyName)) {
138            if (gs.isGetterSetter()) {
139                JSObject* setterFunc = asGetterSetter(gs)->setter();
140                if (!setterFunc) {
141                    throwSetterError(exec);
142                    return;
143                }
144
145                CallData callData;
146                CallType callType = setterFunc->getCallData(callData);
147                MarkedArgumentBuffer args;
148                args.append(value);
149                call(exec, setterFunc, callType, callData, this, args);
150                return;
151            }
152
153            // If there's an existing property on the object or one of its
154            // prototypes it should be replaced, so break here.
155            break;
156        }
157
158        prototype = obj->prototype();
159        if (prototype.isNull())
160            break;
161    }
162
163    if (!putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot) && slot.isStrictMode())
164        throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
165    return;
166}
167
168void JSObject::put(ExecState* exec, unsigned propertyName, JSValue value)
169{
170    PutPropertySlot slot;
171    put(exec, Identifier::from(exec, propertyName), value, slot);
172}
173
174void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
175{
176    putDirectInternal(*globalData, propertyName, value, attributes, checkReadOnly, slot);
177}
178
179void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
180{
181    putDirectInternal(*globalData, propertyName, value, attributes);
182}
183
184void JSObject::putWithAttributes(JSGlobalData* globalData, unsigned propertyName, JSValue value, unsigned attributes)
185{
186    putWithAttributes(globalData, Identifier::from(globalData, propertyName), value, attributes);
187}
188
189void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
190{
191    putDirectInternal(exec->globalData(), propertyName, value, attributes, checkReadOnly, slot);
192}
193
194void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes)
195{
196    putDirectInternal(exec->globalData(), propertyName, value, attributes);
197}
198
199void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes)
200{
201    putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
202}
203
204bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const
205{
206    PropertySlot slot;
207    return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
208}
209
210bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
211{
212    PropertySlot slot;
213    return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
214}
215
216// ECMA 8.6.2.5
217bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
218{
219    unsigned attributes;
220    JSCell* specificValue;
221    if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) {
222        if ((attributes & DontDelete))
223            return false;
224        removeDirect(propertyName);
225        return true;
226    }
227
228    // Look in the static hashtable of properties
229    const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
230    if (entry && entry->attributes() & DontDelete)
231        return false; // this builtin property can't be deleted
232
233    // FIXME: Should the code here actually do some deletion?
234    return true;
235}
236
237bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
238{
239    PropertySlot slot;
240    return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
241}
242
243bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName)
244{
245    return deleteProperty(exec, Identifier::from(exec, propertyName));
246}
247
248static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
249{
250    JSValue function = object->get(exec, propertyName);
251    CallData callData;
252    CallType callType = getCallData(function, callData);
253    if (callType == CallTypeNone)
254        return exec->exception();
255
256    // Prevent "toString" and "valueOf" from observing execution if an exception
257    // is pending.
258    if (exec->hadException())
259        return exec->exception();
260
261    JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
262    ASSERT(!result.isGetterSetter());
263    if (exec->hadException())
264        return exec->exception();
265    if (result.isObject())
266        return JSValue();
267    return result;
268}
269
270bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result)
271{
272    result = defaultValue(exec, PreferNumber);
273    number = result.toNumber(exec);
274    return !result.isString();
275}
276
277// ECMA 8.6.2.6
278JSValue JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
279{
280    // Must call toString first for Date objects.
281    if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) {
282        JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
283        if (value)
284            return value;
285        value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
286        if (value)
287            return value;
288    } else {
289        JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
290        if (value)
291            return value;
292        value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
293        if (value)
294            return value;
295    }
296
297    ASSERT(!exec->hadException());
298
299    return throwError(exec, createTypeError(exec, "No default value"));
300}
301
302const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
303{
304    for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
305        if (const HashTable* propHashTable = info->propHashTable(exec)) {
306            if (const HashEntry* entry = propHashTable->entry(exec, propertyName))
307                return entry;
308        }
309    }
310    return 0;
311}
312
313void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes)
314{
315    JSValue object = getDirect(propertyName);
316    if (object && object.isGetterSetter()) {
317        ASSERT(m_structure->hasGetterSetterProperties());
318        asGetterSetter(object)->setGetter(exec->globalData(), getterFunction);
319        return;
320    }
321
322    PutPropertySlot slot;
323    GetterSetter* getterSetter = new (exec) GetterSetter(exec);
324    putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Getter, true, slot);
325
326    // putDirect will change our Structure if we add a new property. For
327    // getters and setters, though, we also need to change our Structure
328    // if we override an existing non-getter or non-setter.
329    if (slot.type() != PutPropertySlot::NewProperty) {
330        if (!m_structure->isDictionary()) {
331            RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure);
332            setStructure(structure.release());
333        }
334    }
335
336    m_structure->setHasGetterSetterProperties(true);
337    getterSetter->setGetter(exec->globalData(), getterFunction);
338}
339
340void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes)
341{
342    JSValue object = getDirect(propertyName);
343    if (object && object.isGetterSetter()) {
344        ASSERT(m_structure->hasGetterSetterProperties());
345        asGetterSetter(object)->setSetter(exec->globalData(), setterFunction);
346        return;
347    }
348
349    PutPropertySlot slot;
350    GetterSetter* getterSetter = new (exec) GetterSetter(exec);
351    putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot);
352
353    // putDirect will change our Structure if we add a new property. For
354    // getters and setters, though, we also need to change our Structure
355    // if we override an existing non-getter or non-setter.
356    if (slot.type() != PutPropertySlot::NewProperty) {
357        if (!m_structure->isDictionary()) {
358            RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure);
359            setStructure(structure.release());
360        }
361    }
362
363    m_structure->setHasGetterSetterProperties(true);
364    getterSetter->setSetter(exec->globalData(), setterFunction);
365}
366
367JSValue JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
368{
369    JSObject* object = this;
370    while (true) {
371        if (JSValue value = object->getDirect(propertyName)) {
372            if (!value.isGetterSetter())
373                return jsUndefined();
374            JSObject* functionObject = asGetterSetter(value)->getter();
375            if (!functionObject)
376                return jsUndefined();
377            return functionObject;
378        }
379
380        if (!object->prototype() || !object->prototype().isObject())
381            return jsUndefined();
382        object = asObject(object->prototype());
383    }
384}
385
386JSValue JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
387{
388    JSObject* object = this;
389    while (true) {
390        if (JSValue value = object->getDirect(propertyName)) {
391            if (!value.isGetterSetter())
392                return jsUndefined();
393            JSObject* functionObject = asGetterSetter(value)->setter();
394            if (!functionObject)
395                return jsUndefined();
396            return functionObject;
397        }
398
399        if (!object->prototype() || !object->prototype().isObject())
400            return jsUndefined();
401        object = asObject(object->prototype());
402    }
403}
404
405bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto)
406{
407    if (!value.isObject())
408        return false;
409
410    if (!proto.isObject()) {
411        throwError(exec, createTypeError(exec, "instanceof called on an object with an invalid prototype property."));
412        return false;
413    }
414
415    JSObject* object = asObject(value);
416    while ((object = object->prototype().getObject())) {
417        if (proto == object)
418            return true;
419    }
420    return false;
421}
422
423bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
424{
425    PropertyDescriptor descriptor;
426    if (!const_cast<JSObject*>(this)->getOwnPropertyDescriptor(exec, propertyName, descriptor))
427        return false;
428    return descriptor.enumerable();
429}
430
431bool JSObject::getPropertySpecificValue(ExecState*, const Identifier& propertyName, JSCell*& specificValue) const
432{
433    unsigned attributes;
434    if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound)
435        return true;
436
437    // This could be a function within the static table? - should probably
438    // also look in the hash?  This currently should not be a problem, since
439    // we've currently always call 'get' first, which should have populated
440    // the normal storage.
441    return false;
442}
443
444void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
445{
446    getOwnPropertyNames(exec, propertyNames, mode);
447
448    if (prototype().isNull())
449        return;
450
451    JSObject* prototype = asObject(this->prototype());
452    while(1) {
453        if (prototype->structure()->typeInfo().overridesGetPropertyNames()) {
454            prototype->getPropertyNames(exec, propertyNames, mode);
455            break;
456        }
457        prototype->getOwnPropertyNames(exec, propertyNames, mode);
458        JSValue nextProto = prototype->prototype();
459        if (nextProto.isNull())
460            break;
461        prototype = asObject(nextProto);
462    }
463}
464
465void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
466{
467    m_structure->getPropertyNames(propertyNames, mode);
468    getClassPropertyNames(exec, classInfo(), propertyNames, mode);
469}
470
471bool JSObject::toBoolean(ExecState*) const
472{
473    return true;
474}
475
476double JSObject::toNumber(ExecState* exec) const
477{
478    JSValue primitive = toPrimitive(exec, PreferNumber);
479    if (exec->hadException()) // should be picked up soon in Nodes.cpp
480        return 0.0;
481    return primitive.toNumber(exec);
482}
483
484UString JSObject::toString(ExecState* exec) const
485{
486    JSValue primitive = toPrimitive(exec, PreferString);
487    if (exec->hadException())
488        return "";
489    return primitive.toString(exec);
490}
491
492JSObject* JSObject::toObject(ExecState*) const
493{
494    return const_cast<JSObject*>(this);
495}
496
497JSObject* JSObject::toThisObject(ExecState*) const
498{
499    return const_cast<JSObject*>(this);
500}
501
502JSValue JSObject::toStrictThisObject(ExecState*) const
503{
504    return const_cast<JSObject*>(this);
505}
506
507JSObject* JSObject::unwrappedObject()
508{
509    return this;
510}
511
512void JSObject::seal()
513{
514    setStructure(Structure::sealTransition(m_structure));
515}
516
517void JSObject::freeze()
518{
519    setStructure(Structure::freezeTransition(m_structure));
520}
521
522void JSObject::preventExtensions()
523{
524    if (isExtensible())
525        setStructure(Structure::preventExtensionsTransition(m_structure));
526}
527
528void JSObject::removeDirect(const Identifier& propertyName)
529{
530    size_t offset;
531    if (m_structure->isUncacheableDictionary()) {
532        offset = m_structure->removePropertyWithoutTransition(propertyName);
533        if (offset != WTF::notFound)
534            putUndefinedAtDirectOffset(offset);
535        return;
536    }
537
538    RefPtr<Structure> structure = Structure::removePropertyTransition(m_structure, propertyName, offset);
539    setStructure(structure.release());
540    if (offset != WTF::notFound)
541        putUndefinedAtDirectOffset(offset);
542}
543
544void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
545{
546    putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
547}
548
549void JSObject::putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr)
550{
551    putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
552}
553
554void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
555{
556    putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
557}
558
559void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr)
560{
561    putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
562}
563
564NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarrierBase<Unknown>* location)
565{
566    if (JSObject* getterFunction = asGetterSetter(location->get())->getter()) {
567        if (!structure()->isDictionary())
568            slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location));
569        else
570            slot.setGetterSlot(getterFunction);
571    } else
572        slot.setUndefined();
573}
574
575Structure* JSObject::createInheritorID()
576{
577    m_inheritorID = createEmptyObjectStructure(this);
578    return m_inheritorID.get();
579}
580
581void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
582{
583    ASSERT(newSize > oldSize);
584
585    // It's important that this function not rely on m_structure, since
586    // we might be in the middle of a transition.
587    bool wasInline = (oldSize < JSObject::baseExternalStorageCapacity);
588
589    PropertyStorage oldPropertyStorage = m_propertyStorage;
590    PropertyStorage newPropertyStorage = new WriteBarrierBase<Unknown>[newSize];
591
592    for (unsigned i = 0; i < oldSize; ++i)
593       newPropertyStorage[i] = oldPropertyStorage[i];
594
595    if (!wasInline)
596        delete [] oldPropertyStorage;
597
598    m_propertyStorage = newPropertyStorage;
599}
600
601bool JSObject::getOwnPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor& descriptor)
602{
603    unsigned attributes = 0;
604    JSCell* cell = 0;
605    size_t offset = m_structure->get(propertyName, attributes, cell);
606    if (offset == WTF::notFound)
607        return false;
608    descriptor.setDescriptor(getDirectOffset(offset), attributes);
609    return true;
610}
611
612bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
613{
614    JSObject* object = this;
615    while (true) {
616        if (object->getOwnPropertyDescriptor(exec, propertyName, descriptor))
617            return true;
618        JSValue prototype = object->prototype();
619        if (!prototype.isObject())
620            return false;
621        object = asObject(prototype);
622    }
623}
624
625static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor)
626{
627    if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
628        if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) {
629            GetterSetter* accessor = new (exec) GetterSetter(exec);
630            if (oldDescriptor.getter()) {
631                attributes |= Getter;
632                accessor->setGetter(exec->globalData(), asObject(oldDescriptor.getter()));
633            }
634            if (oldDescriptor.setter()) {
635                attributes |= Setter;
636                accessor->setSetter(exec->globalData(), asObject(oldDescriptor.setter()));
637            }
638            target->putWithAttributes(exec, propertyName, accessor, attributes);
639            return true;
640        }
641        JSValue newValue = jsUndefined();
642        if (descriptor.value())
643            newValue = descriptor.value();
644        else if (oldDescriptor.value())
645            newValue = oldDescriptor.value();
646        target->putWithAttributes(exec, propertyName, newValue, attributes & ~(Getter | Setter));
647        return true;
648    }
649    attributes &= ~ReadOnly;
650    if (descriptor.getter() && descriptor.getter().isObject())
651        target->defineGetter(exec, propertyName, asObject(descriptor.getter()), attributes);
652    if (exec->hadException())
653        return false;
654    if (descriptor.setter() && descriptor.setter().isObject())
655        target->defineSetter(exec, propertyName, asObject(descriptor.setter()), attributes);
656    return !exec->hadException();
657}
658
659bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException)
660{
661    // If we have a new property we can just put it on normally
662    PropertyDescriptor current;
663    if (!getOwnPropertyDescriptor(exec, propertyName, current)) {
664        // unless extensions are prevented!
665        if (!isExtensible()) {
666            if (throwException)
667                throwError(exec, createTypeError(exec, "Attempting to define property on object that is not extensible."));
668            return false;
669        }
670        PropertyDescriptor oldDescriptor;
671        oldDescriptor.setValue(jsUndefined());
672        return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
673    }
674
675    if (descriptor.isEmpty())
676        return true;
677
678    if (current.equalTo(exec, descriptor))
679        return true;
680
681    // Filter out invalid changes
682    if (!current.configurable()) {
683        if (descriptor.configurable()) {
684            if (throwException)
685                throwError(exec, createTypeError(exec, "Attempting to configurable attribute of unconfigurable property."));
686            return false;
687        }
688        if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
689            if (throwException)
690                throwError(exec, createTypeError(exec, "Attempting to change enumerable attribute of unconfigurable property."));
691            return false;
692        }
693    }
694
695    // A generic descriptor is simply changing the attributes of an existing property
696    if (descriptor.isGenericDescriptor()) {
697        if (!current.attributesEqual(descriptor)) {
698            deleteProperty(exec, propertyName);
699            putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
700        }
701        return true;
702    }
703
704    // Changing between a normal property or an accessor property
705    if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
706        if (!current.configurable()) {
707            if (throwException)
708                throwError(exec, createTypeError(exec, "Attempting to change access mechanism for an unconfigurable property."));
709            return false;
710        }
711        deleteProperty(exec, propertyName);
712        return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
713    }
714
715    // Changing the value and attributes of an existing property
716    if (descriptor.isDataDescriptor()) {
717        if (!current.configurable()) {
718            if (!current.writable() && descriptor.writable()) {
719                if (throwException)
720                    throwError(exec, createTypeError(exec, "Attempting to change writable attribute of unconfigurable property."));
721                return false;
722            }
723            if (!current.writable()) {
724                if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) {
725                    if (throwException)
726                        throwError(exec, createTypeError(exec, "Attempting to change value of a readonly property."));
727                    return false;
728                }
729            }
730        } else if (current.attributesEqual(descriptor)) {
731            if (!descriptor.value())
732                return true;
733            PutPropertySlot slot;
734            put(exec, propertyName, descriptor.value(), slot);
735            if (exec->hadException())
736                return false;
737            return true;
738        }
739        deleteProperty(exec, propertyName);
740        return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
741    }
742
743    // Changing the accessor functions of an existing accessor property
744    ASSERT(descriptor.isAccessorDescriptor());
745    if (!current.configurable()) {
746        if (descriptor.setterPresent() && !(current.setter() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) {
747            if (throwException)
748                throwError(exec, createTypeError(exec, "Attempting to change the setter of an unconfigurable property."));
749            return false;
750        }
751        if (descriptor.getterPresent() && !(current.getter() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) {
752            if (throwException)
753                throwError(exec, createTypeError(exec, "Attempting to change the getter of an unconfigurable property."));
754            return false;
755        }
756    }
757    JSValue accessor = getDirect(propertyName);
758    if (!accessor)
759        return false;
760    GetterSetter* getterSetter = asGetterSetter(accessor);
761    if (current.attributesEqual(descriptor)) {
762        if (descriptor.setter())
763            getterSetter->setSetter(exec->globalData(), asObject(descriptor.setter()));
764        if (descriptor.getter())
765            getterSetter->setGetter(exec->globalData(), asObject(descriptor.getter()));
766        return true;
767    }
768    deleteProperty(exec, propertyName);
769    unsigned attrs = current.attributesWithOverride(descriptor);
770    if (descriptor.setter())
771        attrs |= Setter;
772    if (descriptor.getter())
773        attrs |= Getter;
774    putDirect(exec->globalData(), propertyName, getterSetter, attrs);
775    return true;
776}
777
778JSObject* throwTypeError(ExecState* exec, const UString& message)
779{
780    return throwError(exec, createTypeError(exec, message));
781}
782
783} // namespace JSC
784