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 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"
30 #include "JSNumberCell.h"
31 #include "PropertySlot.h"
32 #include "PutPropertySlot.h"
33 #include "ScopeChain.h"
34 #include "Structure.h"
38 class InternalFunction
;
39 class PropertyNameArray
;
45 // Property attributes
48 ReadOnly
= 1 << 1, // property can be only read, not written
49 DontEnum
= 1 << 2, // property doesn't appear in (for .. in ..)
50 DontDelete
= 1 << 3, // property can't be deleted
51 Function
= 1 << 4, // property is a function - only used by static hashtables
54 typedef JSValuePtr
* PropertyStorage
;
56 class JSObject
: public JSCell
{
57 friend class BatchedTransitionOptimizer
;
62 explicit JSObject(PassRefPtr
<Structure
>);
66 // The inline virtual destructor cannot be the first virtual function declared
67 // in the class as it results in the vtable being generated as a weak symbol
70 bool inherits(const ClassInfo
* classInfo
) const { return JSCell::isObject(classInfo
); }
72 JSValuePtr
prototype() const;
73 void setPrototype(JSValuePtr prototype
);
75 void setStructure(PassRefPtr
<Structure
>);
76 Structure
* inheritorID();
78 PropertyStorage
& propertyStorage() { return m_propertyStorage
; }
80 virtual UString
className() const;
82 JSValuePtr
get(ExecState
*, const Identifier
& propertyName
) const;
83 JSValuePtr
get(ExecState
*, unsigned propertyName
) const;
85 bool getPropertySlot(ExecState
*, const Identifier
& propertyName
, PropertySlot
&);
86 bool getPropertySlot(ExecState
*, unsigned propertyName
, PropertySlot
&);
88 virtual bool getOwnPropertySlot(ExecState
*, const Identifier
& propertyName
, PropertySlot
&);
89 virtual bool getOwnPropertySlot(ExecState
*, unsigned propertyName
, PropertySlot
&);
91 virtual void put(ExecState
*, const Identifier
& propertyName
, JSValuePtr value
, PutPropertySlot
&);
92 virtual void put(ExecState
*, unsigned propertyName
, JSValuePtr value
);
94 virtual void putWithAttributes(ExecState
*, const Identifier
& propertyName
, JSValuePtr value
, unsigned attributes
);
95 virtual void putWithAttributes(ExecState
*, unsigned propertyName
, JSValuePtr value
, unsigned attributes
);
97 bool propertyIsEnumerable(ExecState
*, const Identifier
& propertyName
) const;
99 bool hasProperty(ExecState
*, const Identifier
& propertyName
) const;
100 bool hasProperty(ExecState
*, unsigned propertyName
) const;
101 bool hasOwnProperty(ExecState
*, const Identifier
& propertyName
) const;
103 virtual bool deleteProperty(ExecState
*, const Identifier
& propertyName
);
104 virtual bool deleteProperty(ExecState
*, unsigned propertyName
);
106 virtual JSValuePtr
defaultValue(ExecState
*, PreferredPrimitiveType
) const;
108 virtual bool hasInstance(ExecState
*, JSValuePtr
, JSValuePtr prototypeProperty
);
110 virtual void getPropertyNames(ExecState
*, PropertyNameArray
&);
112 virtual JSValuePtr
toPrimitive(ExecState
*, PreferredPrimitiveType
= NoPreference
) const;
113 virtual bool getPrimitiveNumber(ExecState
*, double& number
, JSValuePtr
& value
);
114 virtual bool toBoolean(ExecState
*) const;
115 virtual double toNumber(ExecState
*) const;
116 virtual UString
toString(ExecState
*) const;
117 virtual JSObject
* toObject(ExecState
*) const;
119 virtual JSObject
* toThisObject(ExecState
*) const;
120 virtual JSObject
* unwrappedObject();
122 virtual bool getPropertyAttributes(ExecState
*, const Identifier
& propertyName
, unsigned& attributes
) const;
124 // This get function only looks at the property map.
125 JSValuePtr
getDirect(const Identifier
& propertyName
) const
127 size_t offset
= m_structure
->get(propertyName
);
128 return offset
!= WTF::notFound
? m_propertyStorage
[offset
] : noValue();
131 JSValuePtr
* getDirectLocation(const Identifier
& propertyName
)
133 size_t offset
= m_structure
->get(propertyName
);
134 return offset
!= WTF::notFound
? locationForOffset(offset
) : 0;
137 JSValuePtr
* getDirectLocation(const Identifier
& propertyName
, unsigned& attributes
)
139 size_t offset
= m_structure
->get(propertyName
, attributes
);
140 return offset
!= WTF::notFound
? locationForOffset(offset
) : 0;
143 size_t offsetForLocation(JSValuePtr
* location
)
145 return location
- m_propertyStorage
;
148 JSValuePtr
* locationForOffset(size_t offset
)
150 return &m_propertyStorage
[offset
];
153 void transitionTo(Structure
*);
155 void removeDirect(const Identifier
& propertyName
);
156 bool hasCustomProperties() { return !m_structure
->isEmpty(); }
157 bool hasGetterSetterProperties() { return m_structure
->hasGetterSetterProperties(); }
159 void putDirect(const Identifier
& propertyName
, JSValuePtr value
, unsigned attr
= 0);
160 void putDirect(const Identifier
& propertyName
, JSValuePtr value
, unsigned attr
, bool checkReadOnly
, PutPropertySlot
& slot
);
161 void putDirectFunction(ExecState
* exec
, InternalFunction
* function
, unsigned attr
= 0);
162 void putDirectWithoutTransition(const Identifier
& propertyName
, JSValuePtr value
, unsigned attr
= 0);
163 void putDirectFunctionWithoutTransition(ExecState
* exec
, InternalFunction
* function
, unsigned attr
= 0);
165 // Fast access to known property offsets.
166 JSValuePtr
getDirectOffset(size_t offset
) { return m_propertyStorage
[offset
]; }
167 void putDirectOffset(size_t offset
, JSValuePtr value
) { m_propertyStorage
[offset
] = value
; }
169 void fillGetterPropertySlot(PropertySlot
&, JSValuePtr
* location
);
171 virtual void defineGetter(ExecState
*, const Identifier
& propertyName
, JSObject
* getterFunction
);
172 virtual void defineSetter(ExecState
*, const Identifier
& propertyName
, JSObject
* setterFunction
);
173 virtual JSValuePtr
lookupGetter(ExecState
*, const Identifier
& propertyName
);
174 virtual JSValuePtr
lookupSetter(ExecState
*, const Identifier
& propertyName
);
176 virtual bool isGlobalObject() const { return false; }
177 virtual bool isVariableObject() const { return false; }
178 virtual bool isActivationObject() const { return false; }
179 virtual bool isWatchdogException() const { return false; }
180 virtual bool isNotAnObjectErrorStub() const { return false; }
182 void allocatePropertyStorage(size_t oldSize
, size_t newSize
);
183 void allocatePropertyStorageInline(size_t oldSize
, size_t newSize
);
184 bool usingInlineStorage() const { return m_propertyStorage
== m_inlineStorage
; }
186 static const size_t inlineStorageCapacity
= 2;
187 static const size_t nonInlineBaseStorageCapacity
= 16;
189 static PassRefPtr
<Structure
> createStructure(JSValuePtr prototype
)
191 return Structure::create(prototype
, TypeInfo(ObjectType
, HasStandardGetOwnPropertySlot
));
195 bool getOwnPropertySlotForWrite(ExecState
*, const Identifier
&, PropertySlot
&, bool& slotIsWriteable
);
198 bool inlineGetOwnPropertySlot(ExecState
*, const Identifier
& propertyName
, PropertySlot
&);
200 const HashEntry
* findPropertyHashEntry(ExecState
*, const Identifier
& propertyName
) const;
201 Structure
* createInheritorID();
203 RefPtr
<Structure
> m_inheritorID
;
205 PropertyStorage m_propertyStorage
;
206 JSValuePtr m_inlineStorage
[inlineStorageCapacity
];
209 JSObject
* asObject(JSValuePtr
);
211 JSObject
* constructEmptyObject(ExecState
*);
213 inline JSObject
* asObject(JSValuePtr value
)
215 ASSERT(asCell(value
)->isObject());
216 return static_cast<JSObject
*>(asCell(value
));
219 inline JSObject::JSObject(PassRefPtr
<Structure
> structure
)
220 : JSCell(structure
.releaseRef()) // ~JSObject balances this ref()
221 , m_propertyStorage(m_inlineStorage
)
224 ASSERT(m_structure
->propertyStorageCapacity() == inlineStorageCapacity
);
225 ASSERT(m_structure
->isEmpty());
226 ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype()));
229 inline JSObject::~JSObject()
232 if (m_propertyStorage
!= m_inlineStorage
)
233 delete [] m_propertyStorage
;
234 m_structure
->deref();
237 inline JSValuePtr
JSObject::prototype() const
239 return m_structure
->storedPrototype();
242 inline void JSObject::setPrototype(JSValuePtr prototype
)
245 RefPtr
<Structure
> newStructure
= Structure::changePrototypeTransition(m_structure
, prototype
);
246 setStructure(newStructure
.release());
249 inline void JSObject::setStructure(PassRefPtr
<Structure
> structure
)
251 m_structure
->deref();
252 m_structure
= structure
.releaseRef(); // ~JSObject balances this ref()
255 inline Structure
* JSObject::inheritorID()
258 return m_inheritorID
.get();
259 return createInheritorID();
262 inline bool JSCell::isObject(const ClassInfo
* info
) const
264 for (const ClassInfo
* ci
= classInfo(); ci
; ci
= ci
->parentClass
) {
271 // this method is here to be after the inline declaration of JSCell::isObject
272 inline bool JSValuePtr::isObject(const ClassInfo
* classInfo
) const
274 return isCell() && asCell()->isObject(classInfo
);
277 ALWAYS_INLINE
bool JSObject::inlineGetOwnPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
279 if (JSValuePtr
* location
= getDirectLocation(propertyName
)) {
280 if (m_structure
->hasGetterSetterProperties() && location
[0].isGetterSetter())
281 fillGetterPropertySlot(slot
, location
);
283 slot
.setValueSlot(this, location
, offsetForLocation(location
));
287 // non-standard Netscape extension
288 if (propertyName
== exec
->propertyNames().underscoreProto
) {
289 slot
.setValue(prototype());
296 ALWAYS_INLINE
bool JSObject::getOwnPropertySlotForWrite(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
, bool& slotIsWriteable
)
299 if (JSValuePtr
* location
= getDirectLocation(propertyName
, attributes
)) {
300 if (m_structure
->hasGetterSetterProperties() && location
[0].isGetterSetter()) {
301 slotIsWriteable
= false;
302 fillGetterPropertySlot(slot
, location
);
304 slotIsWriteable
= !(attributes
& ReadOnly
);
305 slot
.setValueSlot(this, location
, offsetForLocation(location
));
310 // non-standard Netscape extension
311 if (propertyName
== exec
->propertyNames().underscoreProto
) {
312 slot
.setValue(prototype());
313 slotIsWriteable
= false;
320 // It may seem crazy to inline a function this large, especially a virtual function,
321 // but it makes a big difference to property lookup that derived classes can inline their
322 // base class call to this.
323 ALWAYS_INLINE
bool JSObject::getOwnPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
325 return inlineGetOwnPropertySlot(exec
, propertyName
, slot
);
328 ALWAYS_INLINE
bool JSCell::fastGetOwnPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
330 if (structure()->typeInfo().hasStandardGetOwnPropertySlot())
331 return asObject(this)->inlineGetOwnPropertySlot(exec
, propertyName
, slot
);
332 return getOwnPropertySlot(exec
, propertyName
, slot
);
335 // It may seem crazy to inline a function this large but it makes a big difference
336 // since this is function very hot in variable lookup
337 inline bool JSObject::getPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
339 JSObject
* object
= this;
341 if (object
->fastGetOwnPropertySlot(exec
, propertyName
, slot
))
343 JSValuePtr prototype
= object
->prototype();
344 if (!prototype
.isObject())
346 object
= asObject(prototype
);
350 inline bool JSObject::getPropertySlot(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
352 JSObject
* object
= this;
354 if (object
->getOwnPropertySlot(exec
, propertyName
, slot
))
356 JSValuePtr prototype
= object
->prototype();
357 if (!prototype
.isObject())
359 object
= asObject(prototype
);
363 inline JSValuePtr
JSObject::get(ExecState
* exec
, const Identifier
& propertyName
) const
365 PropertySlot
slot(this);
366 if (const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
))
367 return slot
.getValue(exec
, propertyName
);
369 return jsUndefined();
372 inline JSValuePtr
JSObject::get(ExecState
* exec
, unsigned propertyName
) const
374 PropertySlot
slot(this);
375 if (const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
))
376 return slot
.getValue(exec
, propertyName
);
378 return jsUndefined();
381 inline void JSObject::putDirect(const Identifier
& propertyName
, JSValuePtr value
, unsigned attr
)
383 PutPropertySlot slot
;
384 putDirect(propertyName
, value
, attr
, false, slot
);
387 inline void JSObject::putDirect(const Identifier
& propertyName
, JSValuePtr value
, unsigned attributes
, bool checkReadOnly
, PutPropertySlot
& slot
)
389 ASSERT(!Heap::heap(value
) || Heap::heap(value
) == Heap::heap(this));
391 if (m_structure
->isDictionary()) {
392 unsigned currentAttributes
;
393 size_t offset
= m_structure
->get(propertyName
, currentAttributes
);
394 if (offset
!= WTF::notFound
) {
395 if (checkReadOnly
&& currentAttributes
& ReadOnly
)
397 m_propertyStorage
[offset
] = value
;
398 slot
.setExistingProperty(this, offset
);
402 size_t currentCapacity
= m_structure
->propertyStorageCapacity();
403 offset
= m_structure
->addPropertyWithoutTransition(propertyName
, attributes
);
404 if (currentCapacity
!= m_structure
->propertyStorageCapacity())
405 allocatePropertyStorage(currentCapacity
, m_structure
->propertyStorageCapacity());
407 ASSERT(offset
< m_structure
->propertyStorageCapacity());
408 m_propertyStorage
[offset
] = value
;
409 slot
.setNewProperty(this, offset
);
414 size_t currentCapacity
= m_structure
->propertyStorageCapacity();
415 if (RefPtr
<Structure
> structure
= Structure::addPropertyTransitionToExistingStructure(m_structure
, propertyName
, attributes
, offset
)) {
416 if (currentCapacity
!= structure
->propertyStorageCapacity())
417 allocatePropertyStorage(currentCapacity
, structure
->propertyStorageCapacity());
419 ASSERT(offset
< structure
->propertyStorageCapacity());
420 m_propertyStorage
[offset
] = value
;
421 slot
.setNewProperty(this, offset
);
422 slot
.setWasTransition(true);
423 setStructure(structure
.release());
427 unsigned currentAttributes
;
428 offset
= m_structure
->get(propertyName
, currentAttributes
);
429 if (offset
!= WTF::notFound
) {
430 if (checkReadOnly
&& currentAttributes
& ReadOnly
)
432 m_propertyStorage
[offset
] = value
;
433 slot
.setExistingProperty(this, offset
);
437 RefPtr
<Structure
> structure
= Structure::addPropertyTransition(m_structure
, propertyName
, attributes
, offset
);
438 if (currentCapacity
!= structure
->propertyStorageCapacity())
439 allocatePropertyStorage(currentCapacity
, structure
->propertyStorageCapacity());
441 ASSERT(offset
< structure
->propertyStorageCapacity());
442 m_propertyStorage
[offset
] = value
;
443 slot
.setNewProperty(this, offset
);
444 slot
.setWasTransition(true);
445 setStructure(structure
.release());
448 inline void JSObject::putDirectWithoutTransition(const Identifier
& propertyName
, JSValuePtr value
, unsigned attributes
)
450 size_t currentCapacity
= m_structure
->propertyStorageCapacity();
451 size_t offset
= m_structure
->addPropertyWithoutTransition(propertyName
, attributes
);
452 if (currentCapacity
!= m_structure
->propertyStorageCapacity())
453 allocatePropertyStorage(currentCapacity
, m_structure
->propertyStorageCapacity());
454 m_propertyStorage
[offset
] = value
;
457 inline void JSObject::transitionTo(Structure
* newStructure
)
459 if (m_structure
->propertyStorageCapacity() != newStructure
->propertyStorageCapacity())
460 allocatePropertyStorage(m_structure
->propertyStorageCapacity(), newStructure
->propertyStorageCapacity());
461 setStructure(newStructure
);
464 inline JSValuePtr
JSObject::toPrimitive(ExecState
* exec
, PreferredPrimitiveType preferredType
) const
466 return defaultValue(exec
, preferredType
);
469 inline JSValuePtr
JSValuePtr::get(ExecState
* exec
, const Identifier
& propertyName
) const
471 PropertySlot
slot(asValue());
472 return get(exec
, propertyName
, slot
);
475 inline JSValuePtr
JSValuePtr::get(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
) const
477 if (UNLIKELY(!isCell())) {
478 JSObject
* prototype
= JSImmediate::prototype(asValue(), exec
);
479 if (!prototype
->getPropertySlot(exec
, propertyName
, slot
))
480 return jsUndefined();
481 return slot
.getValue(exec
, propertyName
);
483 JSCell
* cell
= asCell();
485 if (cell
->fastGetOwnPropertySlot(exec
, propertyName
, slot
))
486 return slot
.getValue(exec
, propertyName
);
487 ASSERT(cell
->isObject());
488 JSValuePtr prototype
= static_cast<JSObject
*>(cell
)->prototype();
489 if (!prototype
.isObject())
490 return jsUndefined();
491 cell
= asObject(prototype
);
495 inline JSValuePtr
JSValuePtr::get(ExecState
* exec
, unsigned propertyName
) const
497 PropertySlot
slot(asValue());
498 return get(exec
, propertyName
, slot
);
501 inline JSValuePtr
JSValuePtr::get(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
) const
503 if (UNLIKELY(!isCell())) {
504 JSObject
* prototype
= JSImmediate::prototype(asValue(), exec
);
505 if (!prototype
->getPropertySlot(exec
, propertyName
, slot
))
506 return jsUndefined();
507 return slot
.getValue(exec
, propertyName
);
509 JSCell
* cell
= const_cast<JSCell
*>(asCell());
511 if (cell
->getOwnPropertySlot(exec
, propertyName
, slot
))
512 return slot
.getValue(exec
, propertyName
);
513 ASSERT(cell
->isObject());
514 JSValuePtr prototype
= static_cast<JSObject
*>(cell
)->prototype();
515 if (!prototype
.isObject())
516 return jsUndefined();
517 cell
= prototype
.asCell();
521 inline void JSValuePtr::put(ExecState
* exec
, const Identifier
& propertyName
, JSValuePtr value
, PutPropertySlot
& slot
)
523 if (UNLIKELY(!isCell())) {
524 JSImmediate::toObject(asValue(), exec
)->put(exec
, propertyName
, value
, slot
);
527 asCell()->put(exec
, propertyName
, value
, slot
);
530 inline void JSValuePtr::put(ExecState
* exec
, unsigned propertyName
, JSValuePtr value
)
532 if (UNLIKELY(!isCell())) {
533 JSImmediate::toObject(asValue(), exec
)->put(exec
, propertyName
, value
);
536 asCell()->put(exec
, propertyName
, value
);
539 ALWAYS_INLINE
void JSObject::allocatePropertyStorageInline(size_t oldSize
, size_t newSize
)
541 ASSERT(newSize
> oldSize
);
543 JSValuePtr
* oldPropertyStorage
= m_propertyStorage
;
544 m_propertyStorage
= new JSValuePtr
[newSize
];
546 for (unsigned i
= 0; i
< oldSize
; ++i
)
547 m_propertyStorage
[i
] = oldPropertyStorage
[i
];
549 if (oldPropertyStorage
!= m_inlineStorage
)
550 delete [] oldPropertyStorage
;