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, 2007, 2008, 2009 Apple Inc. All rights reserved.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
27 #include "ClassInfo.h"
28 #include "CommonIdentifiers.h"
29 #include "CallFrame.h"
31 #include "PropertySlot.h"
32 #include "PutPropertySlot.h"
33 #include "ScopeChain.h"
34 #include "StorageBarrier.h"
35 #include "Structure.h"
36 #include "JSGlobalData.h"
38 #include <wtf/StdLibExtras.h>
42 inline JSCell* getJSFunction(JSValue value)
44 if (value.isCell() && (value.asCell()->structure()->typeInfo().type() == JSFunctionType))
45 return value.asCell();
51 class InternalFunction;
53 class PropertyDescriptor;
54 class PropertyNameArray;
58 JSObject* throwTypeError(ExecState*, const UString&);
59 extern const char* StrictModeReadonlyPropertyWriteError;
62 // Property attributes
65 ReadOnly = 1 << 1, // property can be only read, not written
66 DontEnum = 1 << 2, // property doesn't appear in (for .. in ..)
67 DontDelete = 1 << 3, // property can't be deleted
68 Function = 1 << 4, // property is a function - only used by static hashtables
69 Getter = 1 << 5, // property is a getter
70 Setter = 1 << 6 // property is a setter
73 class JSObject : public JSCell {
74 friend class BatchedTransitionOptimizer;
77 friend class MarkedBlock;
78 friend bool setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot);
83 virtual void vtableAnchor();
85 static void visitChildren(JSCell*, SlotVisitor&);
87 static UString className(const JSObject*);
89 static void finalize(JSCell*);
91 JSValue prototype() const;
92 void setPrototype(JSGlobalData&, JSValue prototype);
93 bool setPrototypeWithCycleCheck(JSGlobalData&, JSValue prototype);
95 Structure* inheritorID(JSGlobalData&);
97 JSValue get(ExecState*, const Identifier& propertyName) const;
98 JSValue get(ExecState*, unsigned propertyName) const;
100 bool getPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
101 bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
102 bool getPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&);
104 static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&);
105 static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
106 static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&);
108 static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&);
109 static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue);
111 static void putWithAttributes(JSObject*, ExecState*, const Identifier& propertyName, JSValue, unsigned attributes);
112 void putWithAttributes(JSGlobalData*, const Identifier& propertyName, JSValue, unsigned attributes);
114 bool propertyIsEnumerable(ExecState*, const Identifier& propertyName) const;
116 bool hasProperty(ExecState*, const Identifier& propertyName) const;
117 bool hasProperty(ExecState*, unsigned propertyName) const;
118 bool hasOwnProperty(ExecState*, const Identifier& propertyName) const;
120 static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName);
121 static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName);
123 static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType);
125 static bool hasInstance(JSObject*, ExecState*, JSValue, JSValue prototypeProperty);
127 static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
128 static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
130 JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const;
131 bool toBoolean(ExecState*) const;
132 bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
133 double toNumber(ExecState*) const;
134 UString toString(ExecState*) const;
136 static JSObject* toThisObject(JSCell*, ExecState*);
137 JSObject* unwrappedObject();
139 bool getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificFunction) const;
141 // This get function only looks at the property map.
142 JSValue getDirect(JSGlobalData& globalData, const Identifier& propertyName) const
144 size_t offset = structure()->get(globalData, propertyName);
145 return offset != WTF::notFound ? getDirectOffset(offset) : JSValue();
148 WriteBarrierBase<Unknown>* getDirectLocation(JSGlobalData& globalData, const Identifier& propertyName)
150 size_t offset = structure()->get(globalData, propertyName);
151 return offset != WTF::notFound ? locationForOffset(offset) : 0;
154 WriteBarrierBase<Unknown>* getDirectLocation(JSGlobalData& globalData, const Identifier& propertyName, unsigned& attributes)
156 JSCell* specificFunction;
157 size_t offset = structure()->get(globalData, propertyName, attributes, specificFunction);
158 return offset != WTF::notFound ? locationForOffset(offset) : 0;
161 size_t offsetForLocation(WriteBarrierBase<Unknown>* location) const
163 return location - propertyStorage();
166 void transitionTo(JSGlobalData&, Structure*);
168 void removeDirect(JSGlobalData&, const Identifier& propertyName);
169 bool hasCustomProperties() { return structure()->didTransition(); }
170 bool hasGetterSetterProperties() { return structure()->hasGetterSetterProperties(); }
172 bool putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&);
173 void putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr = 0);
174 bool putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, PutPropertySlot&);
176 void putDirectWithoutTransition(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr = 0);
178 // Fast access to known property offsets.
179 JSValue getDirectOffset(size_t offset) const { return propertyStorage()[offset].get(); }
180 void putDirectOffset(JSGlobalData& globalData, size_t offset, JSValue value) { propertyStorage()[offset].set(globalData, this, value); }
181 void putUndefinedAtDirectOffset(size_t offset) { propertyStorage()[offset].setUndefined(); }
183 void fillGetterPropertySlot(PropertySlot&, WriteBarrierBase<Unknown>* location);
184 void initializeGetterSetterProperty(ExecState*, const Identifier&, GetterSetter*, unsigned attributes);
186 static void defineGetter(JSObject*, ExecState*, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes = 0);
187 static void defineSetter(JSObject*, ExecState*, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes = 0);
188 JSValue lookupGetter(ExecState*, const Identifier& propertyName);
189 JSValue lookupSetter(ExecState*, const Identifier& propertyName);
190 static bool defineOwnProperty(JSObject*, ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow);
192 bool isGlobalObject() const;
193 bool isVariableObject() const;
194 bool isActivationObject() const;
195 bool isErrorInstance() const;
196 bool isGlobalThis() const;
198 void seal(JSGlobalData&);
199 void freeze(JSGlobalData&);
200 void preventExtensions(JSGlobalData&);
201 bool isSealed(JSGlobalData& globalData) { return structure()->isSealed(globalData); }
202 bool isFrozen(JSGlobalData& globalData) { return structure()->isFrozen(globalData); }
203 bool isExtensible() { return structure()->isExtensible(); }
205 bool staticFunctionsReified() { return structure()->staticFunctionsReified(); }
206 void reifyStaticFunctionsForDelete(ExecState* exec);
208 void allocatePropertyStorage(JSGlobalData&, size_t oldSize, size_t newSize);
209 bool isUsingInlineStorage() const { return static_cast<const void*>(m_propertyStorage.get()) == static_cast<const void*>(this + 1); }
211 void* addressOfPropertyStorage()
213 return &m_propertyStorage;
216 static const unsigned baseExternalStorageCapacity = 16;
218 void flattenDictionaryObject(JSGlobalData& globalData)
220 structure()->flattenDictionaryStructure(globalData, this);
223 JSGlobalObject* globalObject() const
225 ASSERT(structure()->globalObject());
226 ASSERT(!isGlobalObject() || ((JSObject*)structure()->globalObject()) == this);
227 return structure()->globalObject();
230 static size_t offsetOfInlineStorage();
231 static size_t offsetOfPropertyStorage();
232 static size_t offsetOfInheritorID();
234 static JS_EXPORTDATA const ClassInfo s_info;
237 void finishCreation(JSGlobalData& globalData, PropertyStorage inlineStorage)
239 Base::finishCreation(globalData);
240 ASSERT(inherits(&s_info));
241 ASSERT(structure()->propertyStorageCapacity() < baseExternalStorageCapacity);
242 ASSERT(structure()->isEmpty());
243 ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype()));
244 ASSERT_UNUSED(inlineStorage, static_cast<void*>(inlineStorage) == static_cast<void*>(this + 1));
245 ASSERT(structure()->isObject());
248 static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
250 return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
253 static const unsigned StructureFlags = 0;
255 // To instantiate objects you likely want JSFinalObject, below.
256 // To create derived types you likely want JSNonFinalObject, below.
257 JSObject(JSGlobalData&, Structure*, PropertyStorage inlineStorage);
258 JSObject(VPtrStealingHackType, PropertyStorage inlineStorage)
259 : JSCell(VPtrStealingHack)
260 , m_propertyStorage(inlineStorage, StorageBarrier::Unchecked)
265 // Nobody should ever ask any of these questions on something already known to be a JSObject.
266 using JSCell::isAPIValueWrapper;
267 using JSCell::isGetterSetter;
269 void getString(ExecState* exec);
273 ConstPropertyStorage propertyStorage() const { return m_propertyStorage.get(); }
274 PropertyStorage propertyStorage() { return m_propertyStorage.get(); }
276 const WriteBarrierBase<Unknown>* locationForOffset(size_t offset) const
278 return &propertyStorage()[offset];
281 WriteBarrierBase<Unknown>* locationForOffset(size_t offset)
283 return &propertyStorage()[offset];
286 bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&, JSCell*);
288 bool inlineGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
290 const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const;
291 Structure* createInheritorID(JSGlobalData&);
293 StorageBarrier m_propertyStorage;
294 WriteBarrier<Structure> m_inheritorID;
298 #if USE(JSVALUE32_64)
299 #define JSNonFinalObject_inlineStorageCapacity 4
300 #define JSFinalObject_inlineStorageCapacity 6
302 #define JSNonFinalObject_inlineStorageCapacity 2
303 #define JSFinalObject_inlineStorageCapacity 4
306 COMPILE_ASSERT((JSFinalObject_inlineStorageCapacity >= JSNonFinalObject_inlineStorageCapacity), final_storage_is_at_least_as_large_as_non_final);
308 // JSNonFinalObject is a type of JSObject that has some internal storage,
309 // but also preserves some space in the collector cell for additional
310 // data members in derived types.
311 class JSNonFinalObject : public JSObject {
312 friend class JSObject;
315 typedef JSObject Base;
317 static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
319 return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
323 explicit JSNonFinalObject(VPtrStealingHackType)
324 : JSObject(VPtrStealingHack, m_inlineStorage)
328 explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure)
329 : JSObject(globalData, structure, m_inlineStorage)
333 void finishCreation(JSGlobalData& globalData)
335 Base::finishCreation(globalData, m_inlineStorage);
336 ASSERT(!(OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage) % sizeof(double)));
337 ASSERT(this->structure()->propertyStorageCapacity() == JSNonFinalObject_inlineStorageCapacity);
341 WriteBarrier<Unknown> m_inlineStorage[JSNonFinalObject_inlineStorageCapacity];
344 // JSFinalObject is a type of JSObject that contains sufficent internal
345 // storage to fully make use of the colloctor cell containing it.
346 class JSFinalObject : public JSObject {
347 friend class JSObject;
350 typedef JSObject Base;
352 explicit JSFinalObject(VPtrStealingHackType)
353 : JSObject(VPtrStealingHack, m_inlineStorage)
357 static JSFinalObject* create(ExecState* exec, Structure* structure)
359 JSFinalObject* finalObject = new (allocateCell<JSFinalObject>(*exec->heap())) JSFinalObject(exec->globalData(), structure);
360 finalObject->finishCreation(exec->globalData());
364 static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
366 return Structure::create(globalData, globalObject, prototype, TypeInfo(FinalObjectType, StructureFlags), &s_info);
369 static JS_EXPORTDATA const ClassInfo s_info;
372 void finishCreation(JSGlobalData& globalData)
374 Base::finishCreation(globalData, m_inlineStorage);
375 ASSERT(!(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) % sizeof(double)));
376 ASSERT(this->structure()->propertyStorageCapacity() == JSFinalObject_inlineStorageCapacity);
380 explicit JSFinalObject(JSGlobalData& globalData, Structure* structure)
381 : JSObject(globalData, structure, m_inlineStorage)
385 static const unsigned StructureFlags = JSObject::StructureFlags;
387 WriteBarrierBase<Unknown> m_inlineStorage[JSFinalObject_inlineStorageCapacity];
390 inline size_t JSObject::offsetOfInlineStorage()
392 ASSERT(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) == OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage));
393 return OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage);
396 inline size_t JSObject::offsetOfPropertyStorage()
398 return OBJECT_OFFSETOF(JSObject, m_propertyStorage);
401 inline size_t JSObject::offsetOfInheritorID()
403 return OBJECT_OFFSETOF(JSObject, m_inheritorID);
406 inline bool JSObject::isGlobalObject() const
408 return structure()->typeInfo().type() == GlobalObjectType;
411 inline bool JSObject::isVariableObject() const
413 return structure()->typeInfo().type() >= VariableObjectType;
416 inline bool JSObject::isActivationObject() const
418 return structure()->typeInfo().type() == ActivationObjectType;
421 inline bool JSObject::isErrorInstance() const
423 return structure()->typeInfo().type() == ErrorInstanceType;
426 inline bool JSObject::isGlobalThis() const
428 return structure()->typeInfo().type() == GlobalThisType;
431 inline JSObject* constructEmptyObject(ExecState* exec, Structure* structure)
433 return JSFinalObject::create(exec, structure);
436 inline CallType getCallData(JSValue value, CallData& callData)
438 CallType result = value.isCell() ? value.asCell()->methodTable()->getCallData(value.asCell(), callData) : CallTypeNone;
439 ASSERT(result == CallTypeNone || value.isValidCallee());
443 inline ConstructType getConstructData(JSValue value, ConstructData& constructData)
445 ConstructType result = value.isCell() ? value.asCell()->methodTable()->getConstructData(value.asCell(), constructData) : ConstructTypeNone;
446 ASSERT(result == ConstructTypeNone || value.isValidCallee());
450 inline Structure* createEmptyObjectStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
452 return JSFinalObject::createStructure(globalData, globalObject, prototype);
455 inline JSObject* asObject(JSCell* cell)
457 ASSERT(cell->isObject());
458 return static_cast<JSObject*>(cell);
461 inline JSObject* asObject(JSValue value)
463 return asObject(value.asCell());
466 inline JSObject::JSObject(JSGlobalData& globalData, Structure* structure, PropertyStorage inlineStorage)
467 : JSCell(globalData, structure)
468 , m_propertyStorage(globalData, this, inlineStorage)
472 inline JSValue JSObject::prototype() const
474 return structure()->storedPrototype();
477 inline bool JSObject::setPrototypeWithCycleCheck(JSGlobalData& globalData, JSValue prototype)
479 JSValue nextPrototypeValue = prototype;
480 while (nextPrototypeValue && nextPrototypeValue.isObject()) {
481 JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject();
482 if (nextPrototype == this)
484 nextPrototypeValue = nextPrototype->prototype();
486 setPrototype(globalData, prototype);
490 inline void JSObject::setPrototype(JSGlobalData& globalData, JSValue prototype)
493 setStructure(globalData, Structure::changePrototypeTransition(globalData, structure(), prototype));
496 inline Structure* JSObject::inheritorID(JSGlobalData& globalData)
499 ASSERT(m_inheritorID->isEmpty());
500 return m_inheritorID.get();
502 return createInheritorID(globalData);
505 inline bool Structure::isUsingInlineStorage() const
507 return propertyStorageCapacity() < JSObject::baseExternalStorageCapacity;
510 inline bool JSCell::inherits(const ClassInfo* info) const
512 return classInfo()->isSubClassOf(info);
515 inline const MethodTable* JSCell::methodTable() const
517 return &classInfo()->methodTable;
520 // this method is here to be after the inline declaration of JSCell::inherits
521 inline bool JSValue::inherits(const ClassInfo* classInfo) const
523 return isCell() && asCell()->inherits(classInfo);
526 inline JSObject* JSValue::toThisObject(ExecState* exec) const
528 return isCell() ? asCell()->methodTable()->toThisObject(asCell(), exec) : toThisObjectSlowCase(exec);
531 ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
533 if (WriteBarrierBase<Unknown>* location = getDirectLocation(exec->globalData(), propertyName)) {
534 if (structure()->hasGetterSetterProperties() && location->isGetterSetter())
535 fillGetterPropertySlot(slot, location);
537 slot.setValue(this, location->get(), offsetForLocation(location));
541 // non-standard Netscape extension
542 if (propertyName == exec->propertyNames().underscoreProto) {
543 slot.setValue(prototype());
550 // It may seem crazy to inline a function this large, especially a virtual function,
551 // but it makes a big difference to property lookup that derived classes can inline their
552 // base class call to this.
553 ALWAYS_INLINE bool JSObject::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
555 return jsCast<JSObject*>(cell)->inlineGetOwnPropertySlot(exec, propertyName, slot);
558 ALWAYS_INLINE bool JSCell::fastGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
560 if (!structure()->typeInfo().overridesGetOwnPropertySlot())
561 return asObject(this)->inlineGetOwnPropertySlot(exec, propertyName, slot);
562 return methodTable()->getOwnPropertySlot(this, exec, propertyName, slot);
565 // Fast call to get a property where we may not yet have converted the string to an
566 // identifier. The first time we perform a property access with a given string, try
567 // performing the property map lookup without forming an identifier. We detect this
568 // case by checking whether the hash has yet been set for this string.
569 ALWAYS_INLINE JSValue JSCell::fastGetOwnProperty(ExecState* exec, const UString& name)
571 if (!structure()->typeInfo().overridesGetOwnPropertySlot() && !structure()->hasGetterSetterProperties()) {
572 size_t offset = name.impl()->hasHash()
573 ? structure()->get(exec->globalData(), Identifier(exec, name))
574 : structure()->get(exec->globalData(), name);
575 if (offset != WTF::notFound)
576 return asObject(this)->locationForOffset(offset)->get();
581 // It may seem crazy to inline a function this large but it makes a big difference
582 // since this is function very hot in variable lookup
583 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
585 JSObject* object = this;
587 if (object->fastGetOwnPropertySlot(exec, propertyName, slot))
589 JSValue prototype = object->prototype();
590 if (!prototype.isObject())
592 object = asObject(prototype);
596 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
598 JSObject* object = this;
600 if (object->methodTable()->getOwnPropertySlotByIndex(object, exec, propertyName, slot))
602 JSValue prototype = object->prototype();
603 if (!prototype.isObject())
605 object = asObject(prototype);
609 inline JSValue JSObject::get(ExecState* exec, const Identifier& propertyName) const
611 PropertySlot slot(this);
612 if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
613 return slot.getValue(exec, propertyName);
615 return jsUndefined();
618 inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const
620 PropertySlot slot(this);
621 if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
622 return slot.getValue(exec, propertyName);
624 return jsUndefined();
627 inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot, JSCell* specificFunction)
630 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
632 if (structure()->isDictionary()) {
633 unsigned currentAttributes;
634 JSCell* currentSpecificFunction;
635 size_t offset = structure()->get(globalData, propertyName, currentAttributes, currentSpecificFunction);
636 if (offset != WTF::notFound) {
637 // If there is currently a specific function, and there now either isn't,
638 // or the new value is different, then despecify.
639 if (currentSpecificFunction && (specificFunction != currentSpecificFunction))
640 structure()->despecifyDictionaryFunction(globalData, propertyName);
641 if (checkReadOnly && currentAttributes & ReadOnly)
644 putDirectOffset(globalData, offset, value);
645 // At this point, the objects structure only has a specific value set if previously there
646 // had been one set, and if the new value being specified is the same (otherwise we would
647 // have despecified, above). So, if currentSpecificFunction is not set, or if the new
648 // value is different (or there is no new value), then the slot now has no value - and
649 // as such it is cachable.
650 // If there was previously a value, and the new value is the same, then we cannot cache.
651 if (!currentSpecificFunction || (specificFunction != currentSpecificFunction))
652 slot.setExistingProperty(this, offset);
656 if (checkReadOnly && !isExtensible())
659 size_t currentCapacity = structure()->propertyStorageCapacity();
660 offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, specificFunction);
661 if (currentCapacity != structure()->propertyStorageCapacity())
662 allocatePropertyStorage(globalData, currentCapacity, structure()->propertyStorageCapacity());
664 ASSERT(offset < structure()->propertyStorageCapacity());
665 putDirectOffset(globalData, offset, value);
666 // See comment on setNewProperty call below.
667 if (!specificFunction)
668 slot.setNewProperty(this, offset);
673 size_t currentCapacity = structure()->propertyStorageCapacity();
674 if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName, attributes, specificFunction, offset)) {
675 if (currentCapacity != structure->propertyStorageCapacity())
676 allocatePropertyStorage(globalData, currentCapacity, structure->propertyStorageCapacity());
678 ASSERT(offset < structure->propertyStorageCapacity());
679 setStructure(globalData, structure);
680 putDirectOffset(globalData, offset, value);
681 // This is a new property; transitions with specific values are not currently cachable,
682 // so leave the slot in an uncachable state.
683 if (!specificFunction)
684 slot.setNewProperty(this, offset);
688 unsigned currentAttributes;
689 JSCell* currentSpecificFunction;
690 offset = structure()->get(globalData, propertyName, currentAttributes, currentSpecificFunction);
691 if (offset != WTF::notFound) {
692 if (checkReadOnly && currentAttributes & ReadOnly)
695 // There are three possibilities here:
696 // (1) There is an existing specific value set, and we're overwriting with *the same value*.
697 // * Do nothing - no need to despecify, but that means we can't cache (a cached
698 // put could write a different value). Leave the slot in an uncachable state.
699 // (2) There is a specific value currently set, but we're writing a different value.
700 // * First, we have to despecify. Having done so, this is now a regular slot
701 // with no specific value, so go ahead & cache like normal.
702 // (3) Normal case, there is no specific value set.
703 // * Go ahead & cache like normal.
704 if (currentSpecificFunction) {
705 // case (1) Do the put, then return leaving the slot uncachable.
706 if (specificFunction == currentSpecificFunction) {
707 putDirectOffset(globalData, offset, value);
710 // case (2) Despecify, fall through to (3).
711 setStructure(globalData, Structure::despecifyFunctionTransition(globalData, structure(), propertyName));
714 // case (3) set the slot, do the put, return.
715 slot.setExistingProperty(this, offset);
716 putDirectOffset(globalData, offset, value);
720 if (checkReadOnly && !isExtensible())
723 Structure* structure = Structure::addPropertyTransition(globalData, this->structure(), propertyName, attributes, specificFunction, offset);
725 if (currentCapacity != structure->propertyStorageCapacity())
726 allocatePropertyStorage(globalData, currentCapacity, structure->propertyStorageCapacity());
728 ASSERT(offset < structure->propertyStorageCapacity());
729 setStructure(globalData, structure);
730 putDirectOffset(globalData, offset, value);
731 // This is a new property; transitions with specific values are not currently cachable,
732 // so leave the slot in an uncachable state.
733 if (!specificFunction)
734 slot.setNewProperty(this, offset);
738 inline bool JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
741 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
743 return putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, getJSFunction(value));
746 inline void JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
748 PutPropertySlot slot;
749 putDirectInternal(globalData, propertyName, value, attributes, false, slot, getJSFunction(value));
752 inline bool JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
754 return putDirectInternal(globalData, propertyName, value, 0, false, slot, getJSFunction(value));
757 inline void JSObject::putDirectWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
759 size_t currentCapacity = structure()->propertyStorageCapacity();
760 size_t offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, getJSFunction(value));
761 if (currentCapacity != structure()->propertyStorageCapacity())
762 allocatePropertyStorage(globalData, currentCapacity, structure()->propertyStorageCapacity());
763 putDirectOffset(globalData, offset, value);
766 inline void JSObject::transitionTo(JSGlobalData& globalData, Structure* newStructure)
768 if (structure()->propertyStorageCapacity() != newStructure->propertyStorageCapacity())
769 allocatePropertyStorage(globalData, structure()->propertyStorageCapacity(), newStructure->propertyStorageCapacity());
770 setStructure(globalData, newStructure);
773 inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
775 return methodTable()->defaultValue(this, exec, preferredType);
778 inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName) const
780 PropertySlot slot(asValue());
781 return get(exec, propertyName, slot);
784 inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) const
786 if (UNLIKELY(!isCell())) {
787 JSObject* prototype = synthesizePrototype(exec);
788 if (propertyName == exec->propertyNames().underscoreProto)
790 if (!prototype->getPropertySlot(exec, propertyName, slot))
791 return jsUndefined();
792 return slot.getValue(exec, propertyName);
794 JSCell* cell = asCell();
796 if (cell->fastGetOwnPropertySlot(exec, propertyName, slot))
797 return slot.getValue(exec, propertyName);
798 JSValue prototype = asObject(cell)->prototype();
799 if (!prototype.isObject())
800 return jsUndefined();
801 cell = asObject(prototype);
805 inline JSValue JSValue::get(ExecState* exec, unsigned propertyName) const
807 PropertySlot slot(asValue());
808 return get(exec, propertyName, slot);
811 inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const
813 if (UNLIKELY(!isCell())) {
814 JSObject* prototype = synthesizePrototype(exec);
815 if (!prototype->getPropertySlot(exec, propertyName, slot))
816 return jsUndefined();
817 return slot.getValue(exec, propertyName);
819 JSCell* cell = const_cast<JSCell*>(asCell());
821 if (cell->methodTable()->getOwnPropertySlotByIndex(cell, exec, propertyName, slot))
822 return slot.getValue(exec, propertyName);
823 JSValue prototype = asObject(cell)->prototype();
824 if (!prototype.isObject())
825 return jsUndefined();
826 cell = prototype.asCell();
830 inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
832 if (UNLIKELY(!isCell())) {
833 JSObject* thisObject = synthesizeObject(exec);
834 thisObject->methodTable()->put(thisObject, exec, propertyName, value, slot);
837 asCell()->methodTable()->put(asCell(), exec, propertyName, value, slot);
840 inline void JSValue::putDirect(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
842 ASSERT(isCell() && isObject());
843 if (!asObject(asCell())->putDirect(exec->globalData(), propertyName, value, slot) && slot.isStrictMode())
844 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
847 inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value)
849 if (UNLIKELY(!isCell())) {
850 JSObject* thisObject = synthesizeObject(exec);
851 thisObject->methodTable()->putByIndex(thisObject, exec, propertyName, value);
854 asCell()->methodTable()->putByIndex(asCell(), exec, propertyName, value);
857 // --- JSValue inlines ----------------------------
859 ALWAYS_INLINE JSObject* Register::function() const
863 return asObject(jsValue());
866 ALWAYS_INLINE Register Register::withCallee(JSObject* callee)