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