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