2011-01-27 Oliver Hunt <oliver@apple.com>
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSObject.cpp
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
42 namespace JSC {
43
44 ASSERT_CLASS_FITS_IN_CELL(JSObject);
45
46 const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property.";
47
48 static 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
67 void 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
81 UString JSObject::className() const
82 {
83     const ClassInfo* info = classInfo();
84     if (info)
85         return info->className;
86     return "Object";
87 }
88
89 bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
90 {
91     return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
92 }
93
94 static 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
100 void 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
165 void JSObject::put(ExecState* exec, unsigned propertyName, JSValue value)
166 {
167     PutPropertySlot slot;
168     put(exec, Identifier::from(exec, propertyName), value, slot);
169 }
170
171 void 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
176 void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
177 {
178     putDirectInternal(*globalData, propertyName, value, attributes);
179 }
180
181 void JSObject::putWithAttributes(JSGlobalData* globalData, unsigned propertyName, JSValue value, unsigned attributes)
182 {
183     putWithAttributes(globalData, Identifier::from(globalData, propertyName), value, attributes);
184 }
185
186 void 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
191 void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes)
192 {
193     putDirectInternal(exec->globalData(), propertyName, value, attributes);
194 }
195
196 void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes)
197 {
198     putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
199 }
200
201 bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const
202 {
203     PropertySlot slot;
204     return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
205 }
206
207 bool 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
214 bool 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
234 bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
235 {
236     PropertySlot slot;
237     return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
238 }
239
240 bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName)
241 {
242     return deleteProperty(exec, Identifier::from(exec, propertyName));
243 }
244
245 static 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
267 bool 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
275 JSValue 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
299 const 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
310 void 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
337 void 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
364 JSValue 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
383 JSValue 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
402 bool 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
420 bool 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
428 bool 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
441 void 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
462 void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
463 {
464     m_structure->getPropertyNames(propertyNames, mode);
465     getClassPropertyNames(exec, classInfo(), propertyNames, mode);
466 }
467
468 bool JSObject::toBoolean(ExecState*) const
469 {
470     return true;
471 }
472
473 double 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
481 UString 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
489 JSObject* JSObject::toObject(ExecState*) const
490 {
491     return const_cast<JSObject*>(this);
492 }
493
494 JSObject* JSObject::toThisObject(ExecState*) const
495 {
496     return const_cast<JSObject*>(this);
497 }
498
499 JSValue JSObject::toStrictThisObject(ExecState*) const
500 {
501     return const_cast<JSObject*>(this);
502 }
503
504 JSObject* JSObject::unwrappedObject()
505 {
506     return this;
507 }
508
509 void 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
525 void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
526 {
527     putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
528 }
529
530 void JSObject::putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr)
531 {
532     putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
533 }
534
535 void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
536 {
537     putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
538 }
539
540 void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr)
541 {
542     putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
543 }
544
545 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location)
546 {
547     if (JSObject* getterFunction = asGetterSetter(*location)->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
556 Structure* JSObject::createInheritorID()
557 {
558     m_inheritorID = JSObject::createStructure(this);
559     return m_inheritorID.get();
560 }
561
562 void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
563 {
564     allocatePropertyStorageInline(oldSize, newSize);
565 }
566
567 bool 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
578 bool 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
591 static 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
625 bool 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
738 JSObject* throwTypeError(ExecState* exec, const UString& message)
739 {
740     return throwError(exec, createTypeError(exec, message));
741 }
742
743 } // namespace JSC