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