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_FITS_IN_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(exec->globalData(), 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(exec->globalData(), 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(exec->globalData(), 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(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound) {
222        if ((attributes & DontDelete))
223            return false;
224        removeDirect(exec->globalData(), 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(exec->globalData(), propertyName);
316    if (object && object.isGetterSetter()) {
317        ASSERT(m_structure->hasGetterSetterProperties());
318        asGetterSetter(object)->setGetter(exec->globalData(), getterFunction);
319        return;
320    }
321
322    JSGlobalData& globalData = exec->globalData();
323    PutPropertySlot slot;
324    GetterSetter* getterSetter = new (exec) GetterSetter(exec);
325    putDirectInternal(globalData, propertyName, getterSetter, attributes | Getter, true, slot);
326
327    // putDirect will change our Structure if we add a new property. For
328    // getters and setters, though, we also need to change our Structure
329    // if we override an existing non-getter or non-setter.
330    if (slot.type() != PutPropertySlot::NewProperty) {
331        if (!m_structure->isDictionary())
332            setStructure(exec->globalData(), Structure::getterSetterTransition(globalData, m_structure.get()));
333    }
334
335    m_structure->setHasGetterSetterProperties(true);
336    getterSetter->setGetter(globalData, getterFunction);
337}
338
339void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes)
340{
341    JSValue object = getDirect(exec->globalData(), propertyName);
342    if (object && object.isGetterSetter()) {
343        ASSERT(m_structure->hasGetterSetterProperties());
344        asGetterSetter(object)->setSetter(exec->globalData(), setterFunction);
345        return;
346    }
347
348    PutPropertySlot slot;
349    GetterSetter* getterSetter = new (exec) GetterSetter(exec);
350    putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot);
351
352    // putDirect will change our Structure if we add a new property. For
353    // getters and setters, though, we also need to change our Structure
354    // if we override an existing non-getter or non-setter.
355    if (slot.type() != PutPropertySlot::NewProperty) {
356        if (!m_structure->isDictionary())
357            setStructure(exec->globalData(), Structure::getterSetterTransition(exec->globalData(), m_structure.get()));
358    }
359
360    m_structure->setHasGetterSetterProperties(true);
361    getterSetter->setSetter(exec->globalData(), setterFunction);
362}
363
364JSValue JSObject::lookupGetter(ExecState* exec, const Identifier& propertyName)
365{
366    JSObject* object = this;
367    while (true) {
368        if (JSValue value = object->getDirect(exec->globalData(), 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* exec, const Identifier& propertyName)
384{
385    JSObject* object = this;
386    while (true) {
387        if (JSValue value = object->getDirect(exec->globalData(), 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* exec, const Identifier& propertyName, JSCell*& specificValue) const
429{
430    unsigned attributes;
431    if (m_structure->get(exec->globalData(), 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(exec->globalData(), 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*, JSGlobalObject*) 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::seal(JSGlobalData& globalData)
510{
511    setStructure(globalData, Structure::sealTransition(globalData, m_structure.get()));
512}
513
514void JSObject::freeze(JSGlobalData& globalData)
515{
516    setStructure(globalData, Structure::freezeTransition(globalData, m_structure.get()));
517}
518
519void JSObject::preventExtensions(JSGlobalData& globalData)
520{
521    if (isExtensible())
522        setStructure(globalData, Structure::preventExtensionsTransition(globalData, m_structure.get()));
523}
524
525void JSObject::removeDirect(JSGlobalData& globalData, const Identifier& propertyName)
526{
527    size_t offset;
528    if (m_structure->isUncacheableDictionary()) {
529        offset = m_structure->removePropertyWithoutTransition(globalData, propertyName);
530        if (offset != WTF::notFound)
531            putUndefinedAtDirectOffset(offset);
532        return;
533    }
534
535    setStructure(globalData, Structure::removePropertyTransition(globalData, m_structure.get(), propertyName, offset));
536    if (offset != WTF::notFound)
537        putUndefinedAtDirectOffset(offset);
538}
539
540void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
541{
542    putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
543}
544
545void JSObject::putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr)
546{
547    putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
548}
549
550void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
551{
552    putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
553}
554
555void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr)
556{
557    putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
558}
559
560NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarrierBase<Unknown>* location)
561{
562    if (JSObject* getterFunction = asGetterSetter(location->get())->getter()) {
563        if (!structure()->isDictionary())
564            slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location));
565        else
566            slot.setGetterSlot(getterFunction);
567    } else
568        slot.setUndefined();
569}
570
571Structure* JSObject::createInheritorID(JSGlobalData& globalData)
572{
573    m_inheritorID.set(globalData, this, createEmptyObjectStructure(globalData, this));
574    ASSERT(m_inheritorID->isEmpty());
575    return m_inheritorID.get();
576}
577
578void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
579{
580    ASSERT(newSize > oldSize);
581
582    // It's important that this function not rely on m_structure, since
583    // we might be in the middle of a transition.
584    bool wasInline = (oldSize < JSObject::baseExternalStorageCapacity);
585
586    PropertyStorage oldPropertyStorage = m_propertyStorage;
587    PropertyStorage newPropertyStorage = new WriteBarrierBase<Unknown>[newSize];
588
589    for (unsigned i = 0; i < oldSize; ++i)
590       newPropertyStorage[i] = oldPropertyStorage[i];
591
592    if (!wasInline)
593        delete [] oldPropertyStorage;
594
595    m_propertyStorage = newPropertyStorage;
596}
597
598bool JSObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
599{
600    unsigned attributes = 0;
601    JSCell* cell = 0;
602    size_t offset = m_structure->get(exec->globalData(), propertyName, attributes, cell);
603    if (offset == WTF::notFound)
604        return false;
605    descriptor.setDescriptor(getDirectOffset(offset), attributes);
606    return true;
607}
608
609bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
610{
611    JSObject* object = this;
612    while (true) {
613        if (object->getOwnPropertyDescriptor(exec, propertyName, descriptor))
614            return true;
615        JSValue prototype = object->prototype();
616        if (!prototype.isObject())
617            return false;
618        object = asObject(prototype);
619    }
620}
621
622static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor)
623{
624    if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
625        if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) {
626            GetterSetter* accessor = new (exec) GetterSetter(exec);
627            if (oldDescriptor.getter()) {
628                attributes |= Getter;
629                accessor->setGetter(exec->globalData(), asObject(oldDescriptor.getter()));
630            }
631            if (oldDescriptor.setter()) {
632                attributes |= Setter;
633                accessor->setSetter(exec->globalData(), asObject(oldDescriptor.setter()));
634            }
635            target->putWithAttributes(exec, propertyName, accessor, attributes);
636            return true;
637        }
638        JSValue newValue = jsUndefined();
639        if (descriptor.value())
640            newValue = descriptor.value();
641        else if (oldDescriptor.value())
642            newValue = oldDescriptor.value();
643        target->putWithAttributes(exec, propertyName, newValue, attributes & ~(Getter | Setter));
644        return true;
645    }
646    attributes &= ~ReadOnly;
647    if (descriptor.getter() && descriptor.getter().isObject())
648        target->defineGetter(exec, propertyName, asObject(descriptor.getter()), attributes);
649    if (exec->hadException())
650        return false;
651    if (descriptor.setter() && descriptor.setter().isObject())
652        target->defineSetter(exec, propertyName, asObject(descriptor.setter()), attributes);
653    return !exec->hadException();
654}
655
656bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException)
657{
658    // If we have a new property we can just put it on normally
659    PropertyDescriptor current;
660    if (!getOwnPropertyDescriptor(exec, propertyName, current)) {
661        // unless extensions are prevented!
662        if (!isExtensible()) {
663            if (throwException)
664                throwError(exec, createTypeError(exec, "Attempting to define property on object that is not extensible."));
665            return false;
666        }
667        PropertyDescriptor oldDescriptor;
668        oldDescriptor.setValue(jsUndefined());
669        return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
670    }
671
672    if (descriptor.isEmpty())
673        return true;
674
675    if (current.equalTo(exec, descriptor))
676        return true;
677
678    // Filter out invalid changes
679    if (!current.configurable()) {
680        if (descriptor.configurable()) {
681            if (throwException)
682                throwError(exec, createTypeError(exec, "Attempting to configurable attribute of unconfigurable property."));
683            return false;
684        }
685        if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
686            if (throwException)
687                throwError(exec, createTypeError(exec, "Attempting to change enumerable attribute of unconfigurable property."));
688            return false;
689        }
690    }
691
692    // A generic descriptor is simply changing the attributes of an existing property
693    if (descriptor.isGenericDescriptor()) {
694        if (!current.attributesEqual(descriptor)) {
695            deleteProperty(exec, propertyName);
696            putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
697        }
698        return true;
699    }
700
701    // Changing between a normal property or an accessor property
702    if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
703        if (!current.configurable()) {
704            if (throwException)
705                throwError(exec, createTypeError(exec, "Attempting to change access mechanism for an unconfigurable property."));
706            return false;
707        }
708        deleteProperty(exec, propertyName);
709        return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
710    }
711
712    // Changing the value and attributes of an existing property
713    if (descriptor.isDataDescriptor()) {
714        if (!current.configurable()) {
715            if (!current.writable() && descriptor.writable()) {
716                if (throwException)
717                    throwError(exec, createTypeError(exec, "Attempting to change writable attribute of unconfigurable property."));
718                return false;
719            }
720            if (!current.writable()) {
721                if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) {
722                    if (throwException)
723                        throwError(exec, createTypeError(exec, "Attempting to change value of a readonly property."));
724                    return false;
725                }
726            }
727        } else if (current.attributesEqual(descriptor)) {
728            if (!descriptor.value())
729                return true;
730            PutPropertySlot slot;
731            put(exec, propertyName, descriptor.value(), slot);
732            if (exec->hadException())
733                return false;
734            return true;
735        }
736        deleteProperty(exec, propertyName);
737        return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
738    }
739
740    // Changing the accessor functions of an existing accessor property
741    ASSERT(descriptor.isAccessorDescriptor());
742    if (!current.configurable()) {
743        if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) {
744            if (throwException)
745                throwError(exec, createTypeError(exec, "Attempting to change the setter of an unconfigurable property."));
746            return false;
747        }
748        if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) {
749            if (throwException)
750                throwError(exec, createTypeError(exec, "Attempting to change the getter of an unconfigurable property."));
751            return false;
752        }
753    }
754    JSValue accessor = getDirect(exec->globalData(), propertyName);
755    if (!accessor)
756        return false;
757    GetterSetter* getterSetter = asGetterSetter(accessor);
758    if (current.attributesEqual(descriptor)) {
759        if (descriptor.setter())
760            getterSetter->setSetter(exec->globalData(), asObject(descriptor.setter()));
761        if (descriptor.getter())
762            getterSetter->setGetter(exec->globalData(), asObject(descriptor.getter()));
763        return true;
764    }
765    deleteProperty(exec, propertyName);
766    unsigned attrs = current.attributesWithOverride(descriptor);
767    if (descriptor.setter())
768        attrs |= Setter;
769    if (descriptor.getter())
770        attrs |= Getter;
771    putDirect(exec->globalData(), propertyName, getterSetter, attrs);
772    return true;
773}
774
775JSObject* throwTypeError(ExecState* exec, const UString& message)
776{
777    return throwError(exec, createTypeError(exec, message));
778}
779
780} // namespace JSC
781