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
;
52 class LLIntOffsetsExtractor
;
54 class PropertyDescriptor
;
55 class PropertyNameArray
;
59 JS_EXPORT_PRIVATE JSObject
* throwTypeError(ExecState
*, const UString
&);
60 extern JS_EXPORTDATA
const char* StrictModeReadonlyPropertyWriteError
;
63 // Property attributes
66 ReadOnly
= 1 << 1, // property can be only read, not written
67 DontEnum
= 1 << 2, // property doesn't appear in (for .. in ..)
68 DontDelete
= 1 << 3, // property can't be deleted
69 Function
= 1 << 4, // property is a function - only used by static hashtables
70 Accessor
= 1 << 5, // property is a getter/setter
73 class JSObject
: public JSCell
{
74 friend class BatchedTransitionOptimizer
;
77 friend class MarkedBlock
;
78 JS_EXPORT_PRIVATE
friend bool setUpStaticFunctionSlot(ExecState
* exec
, const HashEntry
* entry
, JSObject
* thisObj
, const Identifier
& propertyName
, PropertySlot
& slot
);
82 PutModeDefineOwnProperty
,
88 JS_EXPORT_PRIVATE
static void visitChildren(JSCell
*, SlotVisitor
&);
90 JS_EXPORT_PRIVATE
static UString
className(const JSObject
*);
92 JSValue
prototype() const;
93 void setPrototype(JSGlobalData
&, JSValue prototype
);
94 bool setPrototypeWithCycleCheck(JSGlobalData
&, JSValue prototype
);
96 Structure
* inheritorID(JSGlobalData
&);
98 JSValue
get(ExecState
*, const Identifier
& propertyName
) const;
99 JSValue
get(ExecState
*, unsigned propertyName
) const;
101 bool getPropertySlot(ExecState
*, const Identifier
& propertyName
, PropertySlot
&);
102 bool getPropertySlot(ExecState
*, unsigned propertyName
, PropertySlot
&);
103 JS_EXPORT_PRIVATE
bool getPropertyDescriptor(ExecState
*, const Identifier
& propertyName
, PropertyDescriptor
&);
105 static bool getOwnPropertySlot(JSCell
*, ExecState
*, const Identifier
& propertyName
, PropertySlot
&);
106 JS_EXPORT_PRIVATE
static bool getOwnPropertySlotByIndex(JSCell
*, ExecState
*, unsigned propertyName
, PropertySlot
&);
107 JS_EXPORT_PRIVATE
static bool getOwnPropertyDescriptor(JSObject
*, ExecState
*, const Identifier
&, PropertyDescriptor
&);
109 bool allowsAccessFrom(ExecState
*);
111 JS_EXPORT_PRIVATE
static void put(JSCell
*, ExecState
*, const Identifier
& propertyName
, JSValue
, PutPropertySlot
&);
112 JS_EXPORT_PRIVATE
static void putByIndex(JSCell
*, ExecState
*, unsigned propertyName
, JSValue
, bool shouldThrow
);
114 // putDirect is effectively an unchecked vesion of 'defineOwnProperty':
115 // - the prototype chain is not consulted
116 // - accessors are not called.
117 // - attributes will be respected (after the call the property will exist with the given attributes)
118 JS_EXPORT_PRIVATE
static void putDirectVirtual(JSObject
*, ExecState
*, const Identifier
& propertyName
, JSValue
, unsigned attributes
);
119 void putDirect(JSGlobalData
&, const Identifier
& propertyName
, JSValue
, unsigned attributes
= 0);
120 void putDirect(JSGlobalData
&, const Identifier
& propertyName
, JSValue
, PutPropertySlot
&);
121 void putDirectWithoutTransition(JSGlobalData
&, const Identifier
& propertyName
, JSValue
, unsigned attributes
= 0);
122 void putDirectAccessor(JSGlobalData
&, const Identifier
& propertyName
, JSValue
, unsigned attributes
);
124 bool propertyIsEnumerable(ExecState
*, const Identifier
& propertyName
) const;
126 JS_EXPORT_PRIVATE
bool hasProperty(ExecState
*, const Identifier
& propertyName
) const;
127 JS_EXPORT_PRIVATE
bool hasProperty(ExecState
*, unsigned propertyName
) const;
128 bool hasOwnProperty(ExecState
*, const Identifier
& propertyName
) const;
130 JS_EXPORT_PRIVATE
static bool deleteProperty(JSCell
*, ExecState
*, const Identifier
& propertyName
);
131 JS_EXPORT_PRIVATE
static bool deletePropertyByIndex(JSCell
*, ExecState
*, unsigned propertyName
);
133 JS_EXPORT_PRIVATE
static JSValue
defaultValue(const JSObject
*, ExecState
*, PreferredPrimitiveType
);
135 JS_EXPORT_PRIVATE
static bool hasInstance(JSObject
*, ExecState
*, JSValue
, JSValue prototypeProperty
);
137 JS_EXPORT_PRIVATE
static void getOwnPropertyNames(JSObject
*, ExecState
*, PropertyNameArray
&, EnumerationMode
);
138 JS_EXPORT_PRIVATE
static void getPropertyNames(JSObject
*, ExecState
*, PropertyNameArray
&, EnumerationMode
);
140 JSValue
toPrimitive(ExecState
*, PreferredPrimitiveType
= NoPreference
) const;
141 JS_EXPORT_PRIVATE
bool toBoolean(ExecState
*) const;
142 bool getPrimitiveNumber(ExecState
*, double& number
, JSValue
&) const;
143 JS_EXPORT_PRIVATE
double toNumber(ExecState
*) const;
144 JS_EXPORT_PRIVATE JSString
* toString(ExecState
*) const;
146 // NOTE: JSObject and its subclasses must be able to gracefully handle ExecState* = 0,
147 // because this call may come from inside the compiler.
148 JS_EXPORT_PRIVATE
static JSObject
* toThisObject(JSCell
*, ExecState
*);
149 JSObject
* unwrappedObject();
151 bool getPropertySpecificValue(ExecState
* exec
, const Identifier
& propertyName
, JSCell
*& specificFunction
) const;
153 // This get function only looks at the property map.
154 JSValue
getDirect(JSGlobalData
& globalData
, const Identifier
& propertyName
) const
156 size_t offset
= structure()->get(globalData
, propertyName
);
157 return offset
!= WTF::notFound
? getDirectOffset(offset
) : JSValue();
160 WriteBarrierBase
<Unknown
>* getDirectLocation(JSGlobalData
& globalData
, const Identifier
& propertyName
)
162 size_t offset
= structure()->get(globalData
, propertyName
);
163 return offset
!= WTF::notFound
? locationForOffset(offset
) : 0;
166 WriteBarrierBase
<Unknown
>* getDirectLocation(JSGlobalData
& globalData
, const Identifier
& propertyName
, unsigned& attributes
)
168 JSCell
* specificFunction
;
169 size_t offset
= structure()->get(globalData
, propertyName
, attributes
, specificFunction
);
170 return offset
!= WTF::notFound
? locationForOffset(offset
) : 0;
173 size_t offsetForLocation(WriteBarrierBase
<Unknown
>* location
) const
175 return location
- propertyStorage();
178 void transitionTo(JSGlobalData
&, Structure
*);
180 void removeDirect(JSGlobalData
&, const Identifier
& propertyName
);
181 bool hasCustomProperties() { return structure()->didTransition(); }
182 bool hasGetterSetterProperties() { return structure()->hasGetterSetterProperties(); }
184 // putOwnDataProperty has 'put' like semantics, however this method:
185 // - assumes the object contains no own getter/setter properties.
186 // - provides no special handling for __proto__
187 // - does not walk the prototype chain (to check for accessors or non-writable properties).
188 // This is used by JSActivation.
189 bool putOwnDataProperty(JSGlobalData
&, const Identifier
& propertyName
, JSValue
, PutPropertySlot
&);
191 // Fast access to known property offsets.
192 JSValue
getDirectOffset(size_t offset
) const { return propertyStorage()[offset
].get(); }
193 void putDirectOffset(JSGlobalData
& globalData
, size_t offset
, JSValue value
) { propertyStorage()[offset
].set(globalData
, this, value
); }
194 void putUndefinedAtDirectOffset(size_t offset
) { propertyStorage()[offset
].setUndefined(); }
196 JS_EXPORT_PRIVATE
static bool defineOwnProperty(JSObject
*, ExecState
*, const Identifier
& propertyName
, PropertyDescriptor
&, bool shouldThrow
);
198 bool isGlobalObject() const;
199 bool isVariableObject() const;
200 bool isStaticScopeObject() const;
201 bool isActivationObject() const;
202 bool isErrorInstance() const;
203 bool isGlobalThis() const;
205 void seal(JSGlobalData
&);
206 void freeze(JSGlobalData
&);
207 JS_EXPORT_PRIVATE
void preventExtensions(JSGlobalData
&);
208 bool isSealed(JSGlobalData
& globalData
) { return structure()->isSealed(globalData
); }
209 bool isFrozen(JSGlobalData
& globalData
) { return structure()->isFrozen(globalData
); }
210 bool isExtensible() { return structure()->isExtensible(); }
212 bool staticFunctionsReified() { return structure()->staticFunctionsReified(); }
213 void reifyStaticFunctionsForDelete(ExecState
* exec
);
215 JS_EXPORT_PRIVATE PropertyStorage
growPropertyStorage(JSGlobalData
&, size_t oldSize
, size_t newSize
);
216 bool isUsingInlineStorage() const { return static_cast<const void*>(m_propertyStorage
.get()) == static_cast<const void*>(this + 1); }
217 void setPropertyStorage(JSGlobalData
&, PropertyStorage
, Structure
*);
219 void* addressOfPropertyStorage()
221 return &m_propertyStorage
;
224 static const unsigned baseExternalStorageCapacity
= 16;
226 void flattenDictionaryObject(JSGlobalData
& globalData
)
228 structure()->flattenDictionaryStructure(globalData
, this);
231 JSGlobalObject
* globalObject() const
233 ASSERT(structure()->globalObject());
234 ASSERT(!isGlobalObject() || ((JSObject
*)structure()->globalObject()) == this);
235 return structure()->globalObject();
238 static size_t offsetOfInlineStorage();
239 static size_t offsetOfPropertyStorage();
240 static size_t offsetOfInheritorID();
242 static JS_EXPORTDATA
const ClassInfo s_info
;
245 void finishCreation(JSGlobalData
& globalData
, PropertyStorage inlineStorage
)
247 Base::finishCreation(globalData
);
248 ASSERT(inherits(&s_info
));
249 ASSERT(structure()->propertyStorageCapacity() < baseExternalStorageCapacity
);
250 ASSERT(structure()->isEmpty());
251 ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype()));
252 ASSERT_UNUSED(inlineStorage
, static_cast<void*>(inlineStorage
) == static_cast<void*>(this + 1));
253 ASSERT(structure()->isObject());
257 static Structure
* createStructure(JSGlobalData
& globalData
, JSGlobalObject
* globalObject
, JSValue prototype
)
259 return Structure::create(globalData
, globalObject
, prototype
, TypeInfo(ObjectType
, StructureFlags
), &s_info
);
262 static const unsigned StructureFlags
= 0;
264 // To instantiate objects you likely want JSFinalObject, below.
265 // To create derived types you likely want JSNonFinalObject, below.
266 JSObject(JSGlobalData
&, Structure
*, PropertyStorage inlineStorage
);
268 void resetInheritorID()
270 m_inheritorID
.clear();
274 friend class LLIntOffsetsExtractor
;
276 // Nobody should ever ask any of these questions on something already known to be a JSObject.
277 using JSCell::isAPIValueWrapper
;
278 using JSCell::isGetterSetter
;
280 void getString(ExecState
* exec
);
284 ConstPropertyStorage
propertyStorage() const { return m_propertyStorage
.get(); }
285 PropertyStorage
propertyStorage() { return m_propertyStorage
.get(); }
287 const WriteBarrierBase
<Unknown
>* locationForOffset(size_t offset
) const
289 return &propertyStorage()[offset
];
292 WriteBarrierBase
<Unknown
>* locationForOffset(size_t offset
)
294 return &propertyStorage()[offset
];
298 bool putDirectInternal(JSGlobalData
&, const Identifier
& propertyName
, JSValue
, unsigned attr
, PutPropertySlot
&, JSCell
*);
300 bool inlineGetOwnPropertySlot(ExecState
*, const Identifier
& propertyName
, PropertySlot
&);
301 JS_EXPORT_PRIVATE
void fillGetterPropertySlot(PropertySlot
&, WriteBarrierBase
<Unknown
>* location
);
303 const HashEntry
* findPropertyHashEntry(ExecState
*, const Identifier
& propertyName
) const;
304 Structure
* createInheritorID(JSGlobalData
&);
306 StorageBarrier m_propertyStorage
;
307 WriteBarrier
<Structure
> m_inheritorID
;
311 #if USE(JSVALUE32_64)
312 #define JSNonFinalObject_inlineStorageCapacity 4
313 #define JSFinalObject_inlineStorageCapacity 6
315 #define JSNonFinalObject_inlineStorageCapacity 2
316 #define JSFinalObject_inlineStorageCapacity 4
319 COMPILE_ASSERT((JSFinalObject_inlineStorageCapacity
>= JSNonFinalObject_inlineStorageCapacity
), final_storage_is_at_least_as_large_as_non_final
);
321 // JSNonFinalObject is a type of JSObject that has some internal storage,
322 // but also preserves some space in the collector cell for additional
323 // data members in derived types.
324 class JSNonFinalObject
: public JSObject
{
325 friend class JSObject
;
328 typedef JSObject Base
;
330 static Structure
* createStructure(JSGlobalData
& globalData
, JSGlobalObject
* globalObject
, JSValue prototype
)
332 return Structure::create(globalData
, globalObject
, prototype
, TypeInfo(ObjectType
, StructureFlags
), &s_info
);
336 explicit JSNonFinalObject(JSGlobalData
& globalData
, Structure
* structure
)
337 : JSObject(globalData
, structure
, m_inlineStorage
)
341 void finishCreation(JSGlobalData
& globalData
)
343 Base::finishCreation(globalData
, m_inlineStorage
);
344 ASSERT(!(OBJECT_OFFSETOF(JSNonFinalObject
, m_inlineStorage
) % sizeof(double)));
345 ASSERT(this->structure()->propertyStorageCapacity() == JSNonFinalObject_inlineStorageCapacity
);
350 WriteBarrier
<Unknown
> m_inlineStorage
[JSNonFinalObject_inlineStorageCapacity
];
355 // JSFinalObject is a type of JSObject that contains sufficent internal
356 // storage to fully make use of the colloctor cell containing it.
357 class JSFinalObject
: public JSObject
{
358 friend class JSObject
;
361 typedef JSObject Base
;
363 static JSFinalObject
* create(ExecState
*, Structure
*);
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
);
381 friend class LLIntOffsetsExtractor
;
383 explicit JSFinalObject(JSGlobalData
& globalData
, Structure
* structure
)
384 : JSObject(globalData
, structure
, m_inlineStorage
)
388 static const unsigned StructureFlags
= JSObject::StructureFlags
;
390 WriteBarrierBase
<Unknown
> m_inlineStorage
[JSFinalObject_inlineStorageCapacity
];
393 inline JSFinalObject
* JSFinalObject::create(ExecState
* exec
, Structure
* structure
)
395 JSFinalObject
* finalObject
= new (NotNull
, allocateCell
<JSFinalObject
>(*exec
->heap())) JSFinalObject(exec
->globalData(), structure
);
396 finalObject
->finishCreation(exec
->globalData());
400 inline bool isJSFinalObject(JSCell
* cell
)
402 return cell
->classInfo() == &JSFinalObject::s_info
;
405 inline bool isJSFinalObject(JSValue value
)
407 return value
.isCell() && isJSFinalObject(value
.asCell());
410 inline size_t JSObject::offsetOfInlineStorage()
412 ASSERT(OBJECT_OFFSETOF(JSFinalObject
, m_inlineStorage
) == OBJECT_OFFSETOF(JSNonFinalObject
, m_inlineStorage
));
413 return OBJECT_OFFSETOF(JSFinalObject
, m_inlineStorage
);
416 inline size_t JSObject::offsetOfPropertyStorage()
418 return OBJECT_OFFSETOF(JSObject
, m_propertyStorage
);
421 inline size_t JSObject::offsetOfInheritorID()
423 return OBJECT_OFFSETOF(JSObject
, m_inheritorID
);
426 inline bool JSObject::isGlobalObject() const
428 return structure()->typeInfo().type() == GlobalObjectType
;
431 inline bool JSObject::isVariableObject() const
433 return structure()->typeInfo().type() >= VariableObjectType
;
436 inline bool JSObject::isStaticScopeObject() const
438 return structure()->typeInfo().type() == StaticScopeObjectType
;
441 inline bool JSObject::isActivationObject() const
443 return structure()->typeInfo().type() == ActivationObjectType
;
446 inline bool JSObject::isErrorInstance() const
448 return structure()->typeInfo().type() == ErrorInstanceType
;
451 inline bool JSObject::isGlobalThis() const
453 return structure()->typeInfo().type() == GlobalThisType
;
456 inline void JSObject::setPropertyStorage(JSGlobalData
& globalData
, PropertyStorage storage
, Structure
* structure
)
460 setStructure(globalData
, structure
);
461 m_propertyStorage
.set(globalData
, this, storage
);
464 inline JSObject
* constructEmptyObject(ExecState
* exec
, Structure
* structure
)
466 return JSFinalObject::create(exec
, structure
);
469 inline CallType
getCallData(JSValue value
, CallData
& callData
)
471 CallType result
= value
.isCell() ? value
.asCell()->methodTable()->getCallData(value
.asCell(), callData
) : CallTypeNone
;
472 ASSERT(result
== CallTypeNone
|| value
.isValidCallee());
476 inline ConstructType
getConstructData(JSValue value
, ConstructData
& constructData
)
478 ConstructType result
= value
.isCell() ? value
.asCell()->methodTable()->getConstructData(value
.asCell(), constructData
) : ConstructTypeNone
;
479 ASSERT(result
== ConstructTypeNone
|| value
.isValidCallee());
483 inline Structure
* createEmptyObjectStructure(JSGlobalData
& globalData
, JSGlobalObject
* globalObject
, JSValue prototype
)
485 return JSFinalObject::createStructure(globalData
, globalObject
, prototype
);
488 inline JSObject
* asObject(JSCell
* cell
)
490 ASSERT(cell
->isObject());
491 return jsCast
<JSObject
*>(cell
);
494 inline JSObject
* asObject(JSValue value
)
496 return asObject(value
.asCell());
499 inline JSObject::JSObject(JSGlobalData
& globalData
, Structure
* structure
, PropertyStorage inlineStorage
)
500 : JSCell(globalData
, structure
)
501 , m_propertyStorage(globalData
, this, inlineStorage
)
505 inline JSValue
JSObject::prototype() const
507 return structure()->storedPrototype();
510 inline void JSObject::setPrototype(JSGlobalData
& globalData
, JSValue prototype
)
513 setStructure(globalData
, Structure::changePrototypeTransition(globalData
, structure(), prototype
));
516 inline Structure
* JSObject::inheritorID(JSGlobalData
& globalData
)
519 ASSERT(m_inheritorID
->isEmpty());
520 return m_inheritorID
.get();
522 return createInheritorID(globalData
);
525 inline bool Structure::isUsingInlineStorage() const
527 return propertyStorageCapacity() < JSObject::baseExternalStorageCapacity
;
530 inline bool JSCell::inherits(const ClassInfo
* info
) const
532 return classInfo()->isSubClassOf(info
);
535 inline const MethodTable
* JSCell::methodTable() const
537 return &classInfo()->methodTable
;
540 // this method is here to be after the inline declaration of JSCell::inherits
541 inline bool JSValue::inherits(const ClassInfo
* classInfo
) const
543 return isCell() && asCell()->inherits(classInfo
);
546 inline JSObject
* JSValue::toThisObject(ExecState
* exec
) const
548 return isCell() ? asCell()->methodTable()->toThisObject(asCell(), exec
) : toThisObjectSlowCase(exec
);
551 ALWAYS_INLINE
bool JSObject::inlineGetOwnPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
553 if (WriteBarrierBase
<Unknown
>* location
= getDirectLocation(exec
->globalData(), propertyName
)) {
554 if (structure()->hasGetterSetterProperties() && location
->isGetterSetter())
555 fillGetterPropertySlot(slot
, location
);
557 slot
.setValue(this, location
->get(), offsetForLocation(location
));
564 // It may seem crazy to inline a function this large, especially a virtual function,
565 // but it makes a big difference to property lookup that derived classes can inline their
566 // base class call to this.
567 ALWAYS_INLINE
bool JSObject::getOwnPropertySlot(JSCell
* cell
, ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
569 return jsCast
<JSObject
*>(cell
)->inlineGetOwnPropertySlot(exec
, propertyName
, slot
);
572 ALWAYS_INLINE
bool JSCell::fastGetOwnPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
574 if (!structure()->typeInfo().overridesGetOwnPropertySlot())
575 return asObject(this)->inlineGetOwnPropertySlot(exec
, propertyName
, slot
);
576 return methodTable()->getOwnPropertySlot(this, exec
, propertyName
, slot
);
579 // Fast call to get a property where we may not yet have converted the string to an
580 // identifier. The first time we perform a property access with a given string, try
581 // performing the property map lookup without forming an identifier. We detect this
582 // case by checking whether the hash has yet been set for this string.
583 ALWAYS_INLINE JSValue
JSCell::fastGetOwnProperty(ExecState
* exec
, const UString
& name
)
585 if (!structure()->typeInfo().overridesGetOwnPropertySlot() && !structure()->hasGetterSetterProperties()) {
586 size_t offset
= name
.impl()->hasHash()
587 ? structure()->get(exec
->globalData(), Identifier(exec
, name
))
588 : structure()->get(exec
->globalData(), name
);
589 if (offset
!= WTF::notFound
)
590 return asObject(this)->locationForOffset(offset
)->get();
595 // It may seem crazy to inline a function this large but it makes a big difference
596 // since this is function very hot in variable lookup
597 ALWAYS_INLINE
bool JSObject::getPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
599 JSObject
* object
= this;
601 if (object
->fastGetOwnPropertySlot(exec
, propertyName
, slot
))
603 JSValue prototype
= object
->prototype();
604 if (!prototype
.isObject())
606 object
= asObject(prototype
);
610 ALWAYS_INLINE
bool JSObject::getPropertySlot(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
612 JSObject
* object
= this;
614 if (object
->methodTable()->getOwnPropertySlotByIndex(object
, exec
, propertyName
, slot
))
616 JSValue prototype
= object
->prototype();
617 if (!prototype
.isObject())
619 object
= asObject(prototype
);
623 inline JSValue
JSObject::get(ExecState
* exec
, const Identifier
& propertyName
) const
625 PropertySlot
slot(this);
626 if (const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
))
627 return slot
.getValue(exec
, propertyName
);
629 return jsUndefined();
632 inline JSValue
JSObject::get(ExecState
* exec
, unsigned propertyName
) const
634 PropertySlot
slot(this);
635 if (const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
))
636 return slot
.getValue(exec
, propertyName
);
638 return jsUndefined();
641 template<JSObject::PutMode mode
>
642 inline bool JSObject::putDirectInternal(JSGlobalData
& globalData
, const Identifier
& propertyName
, JSValue value
, unsigned attributes
, PutPropertySlot
& slot
, JSCell
* specificFunction
)
645 ASSERT(value
.isGetterSetter() == !!(attributes
& Accessor
));
646 ASSERT(!Heap::heap(value
) || Heap::heap(value
) == Heap::heap(this));
648 if (structure()->isDictionary()) {
649 unsigned currentAttributes
;
650 JSCell
* currentSpecificFunction
;
651 size_t offset
= structure()->get(globalData
, propertyName
, currentAttributes
, currentSpecificFunction
);
652 if (offset
!= WTF::notFound
) {
653 // If there is currently a specific function, and there now either isn't,
654 // or the new value is different, then despecify.
655 if (currentSpecificFunction
&& (specificFunction
!= currentSpecificFunction
))
656 structure()->despecifyDictionaryFunction(globalData
, propertyName
);
657 if ((mode
== PutModePut
) && currentAttributes
& ReadOnly
)
660 putDirectOffset(globalData
, offset
, value
);
661 // At this point, the objects structure only has a specific value set if previously there
662 // had been one set, and if the new value being specified is the same (otherwise we would
663 // have despecified, above). So, if currentSpecificFunction is not set, or if the new
664 // value is different (or there is no new value), then the slot now has no value - and
665 // as such it is cachable.
666 // If there was previously a value, and the new value is the same, then we cannot cache.
667 if (!currentSpecificFunction
|| (specificFunction
!= currentSpecificFunction
))
668 slot
.setExistingProperty(this, offset
);
672 if ((mode
== PutModePut
) && !isExtensible())
675 PropertyStorage newStorage
= propertyStorage();
676 if (structure()->shouldGrowPropertyStorage())
677 newStorage
= growPropertyStorage(globalData
, structure()->propertyStorageCapacity(), structure()->suggestedNewPropertyStorageSize());
678 offset
= structure()->addPropertyWithoutTransition(globalData
, propertyName
, attributes
, specificFunction
);
679 setPropertyStorage(globalData
, newStorage
, structure());
681 ASSERT(offset
< structure()->propertyStorageCapacity());
682 putDirectOffset(globalData
, offset
, value
);
683 // See comment on setNewProperty call below.
684 if (!specificFunction
)
685 slot
.setNewProperty(this, offset
);
690 size_t currentCapacity
= structure()->propertyStorageCapacity();
691 if (Structure
* structure
= Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName
, attributes
, specificFunction
, offset
)) {
692 PropertyStorage newStorage
= propertyStorage();
693 if (currentCapacity
!= structure
->propertyStorageCapacity())
694 newStorage
= growPropertyStorage(globalData
, currentCapacity
, structure
->propertyStorageCapacity());
696 ASSERT(offset
< structure
->propertyStorageCapacity());
697 setPropertyStorage(globalData
, newStorage
, structure
);
698 putDirectOffset(globalData
, offset
, value
);
699 // This is a new property; transitions with specific values are not currently cachable,
700 // so leave the slot in an uncachable state.
701 if (!specificFunction
)
702 slot
.setNewProperty(this, offset
);
706 unsigned currentAttributes
;
707 JSCell
* currentSpecificFunction
;
708 offset
= structure()->get(globalData
, propertyName
, currentAttributes
, currentSpecificFunction
);
709 if (offset
!= WTF::notFound
) {
710 if ((mode
== PutModePut
) && currentAttributes
& ReadOnly
)
713 // There are three possibilities here:
714 // (1) There is an existing specific value set, and we're overwriting with *the same value*.
715 // * Do nothing - no need to despecify, but that means we can't cache (a cached
716 // put could write a different value). Leave the slot in an uncachable state.
717 // (2) There is a specific value currently set, but we're writing a different value.
718 // * First, we have to despecify. Having done so, this is now a regular slot
719 // with no specific value, so go ahead & cache like normal.
720 // (3) Normal case, there is no specific value set.
721 // * Go ahead & cache like normal.
722 if (currentSpecificFunction
) {
723 // case (1) Do the put, then return leaving the slot uncachable.
724 if (specificFunction
== currentSpecificFunction
) {
725 putDirectOffset(globalData
, offset
, value
);
728 // case (2) Despecify, fall through to (3).
729 setStructure(globalData
, Structure::despecifyFunctionTransition(globalData
, structure(), propertyName
));
732 // case (3) set the slot, do the put, return.
733 slot
.setExistingProperty(this, offset
);
734 putDirectOffset(globalData
, offset
, value
);
738 if ((mode
== PutModePut
) && !isExtensible())
741 PropertyStorage newStorage
= propertyStorage();
742 if (structure()->shouldGrowPropertyStorage())
743 newStorage
= growPropertyStorage(globalData
, structure()->propertyStorageCapacity(), structure()->suggestedNewPropertyStorageSize());
745 Structure
* structure
= Structure::addPropertyTransition(globalData
, this->structure(), propertyName
, attributes
, specificFunction
, offset
);
747 ASSERT(offset
< structure
->propertyStorageCapacity());
748 setPropertyStorage(globalData
, newStorage
, structure
);
749 putDirectOffset(globalData
, offset
, value
);
750 // This is a new property; transitions with specific values are not currently cachable,
751 // so leave the slot in an uncachable state.
752 if (!specificFunction
)
753 slot
.setNewProperty(this, offset
);
757 inline bool JSObject::putOwnDataProperty(JSGlobalData
& globalData
, const Identifier
& propertyName
, JSValue value
, PutPropertySlot
& slot
)
760 ASSERT(!Heap::heap(value
) || Heap::heap(value
) == Heap::heap(this));
761 ASSERT(!structure()->hasGetterSetterProperties());
763 return putDirectInternal
<PutModePut
>(globalData
, propertyName
, value
, 0, slot
, getJSFunction(value
));
766 inline void JSObject::putDirect(JSGlobalData
& globalData
, const Identifier
& propertyName
, JSValue value
, unsigned attributes
)
768 ASSERT(!value
.isGetterSetter() && !(attributes
& Accessor
));
769 PutPropertySlot slot
;
770 putDirectInternal
<PutModeDefineOwnProperty
>(globalData
, propertyName
, value
, attributes
, slot
, getJSFunction(value
));
773 inline void JSObject::putDirect(JSGlobalData
& globalData
, const Identifier
& propertyName
, JSValue value
, PutPropertySlot
& slot
)
775 ASSERT(!value
.isGetterSetter());
776 putDirectInternal
<PutModeDefineOwnProperty
>(globalData
, propertyName
, value
, 0, slot
, getJSFunction(value
));
779 inline void JSObject::putDirectWithoutTransition(JSGlobalData
& globalData
, const Identifier
& propertyName
, JSValue value
, unsigned attributes
)
781 ASSERT(!value
.isGetterSetter() && !(attributes
& Accessor
));
782 PropertyStorage newStorage
= propertyStorage();
783 if (structure()->shouldGrowPropertyStorage())
784 newStorage
= growPropertyStorage(globalData
, structure()->propertyStorageCapacity(), structure()->suggestedNewPropertyStorageSize());
785 size_t offset
= structure()->addPropertyWithoutTransition(globalData
, propertyName
, attributes
, getJSFunction(value
));
786 setPropertyStorage(globalData
, newStorage
, structure());
787 putDirectOffset(globalData
, offset
, value
);
790 inline void JSObject::transitionTo(JSGlobalData
& globalData
, Structure
* newStructure
)
792 PropertyStorage newStorage
= propertyStorage();
793 if (structure()->propertyStorageCapacity() != newStructure
->propertyStorageCapacity())
794 newStorage
= growPropertyStorage(globalData
, structure()->propertyStorageCapacity(), newStructure
->propertyStorageCapacity());
795 setPropertyStorage(globalData
, newStorage
, newStructure
);
798 inline JSValue
JSObject::toPrimitive(ExecState
* exec
, PreferredPrimitiveType preferredType
) const
800 return methodTable()->defaultValue(this, exec
, preferredType
);
803 inline JSValue
JSValue::get(ExecState
* exec
, const Identifier
& propertyName
) const
805 PropertySlot
slot(asValue());
806 return get(exec
, propertyName
, slot
);
809 inline JSValue
JSValue::get(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
) const
811 if (UNLIKELY(!isCell())) {
812 JSObject
* prototype
= synthesizePrototype(exec
);
813 if (!prototype
->getPropertySlot(exec
, propertyName
, slot
))
814 return jsUndefined();
815 return slot
.getValue(exec
, propertyName
);
817 JSCell
* cell
= asCell();
819 if (cell
->fastGetOwnPropertySlot(exec
, propertyName
, slot
))
820 return slot
.getValue(exec
, propertyName
);
821 JSValue prototype
= asObject(cell
)->prototype();
822 if (!prototype
.isObject())
823 return jsUndefined();
824 cell
= asObject(prototype
);
828 inline JSValue
JSValue::get(ExecState
* exec
, unsigned propertyName
) const
830 PropertySlot
slot(asValue());
831 return get(exec
, propertyName
, slot
);
834 inline JSValue
JSValue::get(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
) const
836 if (UNLIKELY(!isCell())) {
837 JSObject
* prototype
= synthesizePrototype(exec
);
838 if (!prototype
->getPropertySlot(exec
, propertyName
, slot
))
839 return jsUndefined();
840 return slot
.getValue(exec
, propertyName
);
842 JSCell
* cell
= const_cast<JSCell
*>(asCell());
844 if (cell
->methodTable()->getOwnPropertySlotByIndex(cell
, exec
, propertyName
, slot
))
845 return slot
.getValue(exec
, propertyName
);
846 JSValue prototype
= asObject(cell
)->prototype();
847 if (!prototype
.isObject())
848 return jsUndefined();
849 cell
= prototype
.asCell();
853 inline void JSValue::put(ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, PutPropertySlot
& slot
)
855 if (UNLIKELY(!isCell())) {
856 putToPrimitive(exec
, propertyName
, value
, slot
);
859 asCell()->methodTable()->put(asCell(), exec
, propertyName
, value
, slot
);
862 inline void JSValue::putByIndex(ExecState
* exec
, unsigned propertyName
, JSValue value
, bool shouldThrow
)
864 if (UNLIKELY(!isCell())) {
865 PutPropertySlot
slot(shouldThrow
);
866 putToPrimitive(exec
, Identifier::from(exec
, propertyName
), value
, slot
);
869 asCell()->methodTable()->putByIndex(asCell(), exec
, propertyName
, value
, shouldThrow
);
872 // --- JSValue inlines ----------------------------
874 ALWAYS_INLINE JSObject
* Register::function() const
878 return asObject(jsValue());
881 ALWAYS_INLINE Register
Register::withCallee(JSObject
* callee
)