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