]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/JSObject.h
JavaScriptCore-525.tar.gz
[apple/javascriptcore.git] / runtime / JSObject.h
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, 2007, 2008 Apple Inc. All rights reserved.
5 *
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.
10 *
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.
15 *
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.
20 *
21 */
22
23 #ifndef JSObject_h
24 #define JSObject_h
25
26 #include "ArgList.h"
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"
35
36 namespace JSC {
37
38 class InternalFunction;
39 class PropertyNameArray;
40 class Structure;
41 struct HashEntry;
42 struct HashTable;
43
44 // ECMA 262-3 8.6.1
45 // Property attributes
46 enum Attribute {
47 None = 0,
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
52 };
53
54 typedef JSValuePtr* PropertyStorage;
55
56 class JSObject : public JSCell {
57 friend class BatchedTransitionOptimizer;
58 friend class JIT;
59 friend class JSCell;
60
61 public:
62 explicit JSObject(PassRefPtr<Structure>);
63
64 virtual void mark();
65
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
68 virtual ~JSObject();
69
70 bool inherits(const ClassInfo* classInfo) const { return JSCell::isObject(classInfo); }
71
72 JSValuePtr prototype() const;
73 void setPrototype(JSValuePtr prototype);
74
75 void setStructure(PassRefPtr<Structure>);
76 Structure* inheritorID();
77
78 PropertyStorage& propertyStorage() { return m_propertyStorage; }
79
80 virtual UString className() const;
81
82 JSValuePtr get(ExecState*, const Identifier& propertyName) const;
83 JSValuePtr get(ExecState*, unsigned propertyName) const;
84
85 bool getPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
86 bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
87
88 virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
89 virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
90
91 virtual void put(ExecState*, const Identifier& propertyName, JSValuePtr value, PutPropertySlot&);
92 virtual void put(ExecState*, unsigned propertyName, JSValuePtr value);
93
94 virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValuePtr value, unsigned attributes);
95 virtual void putWithAttributes(ExecState*, unsigned propertyName, JSValuePtr value, unsigned attributes);
96
97 bool propertyIsEnumerable(ExecState*, const Identifier& propertyName) const;
98
99 bool hasProperty(ExecState*, const Identifier& propertyName) const;
100 bool hasProperty(ExecState*, unsigned propertyName) const;
101 bool hasOwnProperty(ExecState*, const Identifier& propertyName) const;
102
103 virtual bool deleteProperty(ExecState*, const Identifier& propertyName);
104 virtual bool deleteProperty(ExecState*, unsigned propertyName);
105
106 virtual JSValuePtr defaultValue(ExecState*, PreferredPrimitiveType) const;
107
108 virtual bool hasInstance(ExecState*, JSValuePtr, JSValuePtr prototypeProperty);
109
110 virtual void getPropertyNames(ExecState*, PropertyNameArray&);
111
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;
118
119 virtual JSObject* toThisObject(ExecState*) const;
120 virtual JSObject* unwrappedObject();
121
122 virtual bool getPropertyAttributes(ExecState*, const Identifier& propertyName, unsigned& attributes) const;
123
124 // This get function only looks at the property map.
125 JSValuePtr getDirect(const Identifier& propertyName) const
126 {
127 size_t offset = m_structure->get(propertyName);
128 return offset != WTF::notFound ? m_propertyStorage[offset] : noValue();
129 }
130
131 JSValuePtr* getDirectLocation(const Identifier& propertyName)
132 {
133 size_t offset = m_structure->get(propertyName);
134 return offset != WTF::notFound ? locationForOffset(offset) : 0;
135 }
136
137 JSValuePtr* getDirectLocation(const Identifier& propertyName, unsigned& attributes)
138 {
139 size_t offset = m_structure->get(propertyName, attributes);
140 return offset != WTF::notFound ? locationForOffset(offset) : 0;
141 }
142
143 size_t offsetForLocation(JSValuePtr* location)
144 {
145 return location - m_propertyStorage;
146 }
147
148 JSValuePtr* locationForOffset(size_t offset)
149 {
150 return &m_propertyStorage[offset];
151 }
152
153 void transitionTo(Structure*);
154
155 void removeDirect(const Identifier& propertyName);
156 bool hasCustomProperties() { return !m_structure->isEmpty(); }
157 bool hasGetterSetterProperties() { return m_structure->hasGetterSetterProperties(); }
158
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);
164
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; }
168
169 void fillGetterPropertySlot(PropertySlot&, JSValuePtr* location);
170
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);
175
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; }
181
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; }
185
186 static const size_t inlineStorageCapacity = 2;
187 static const size_t nonInlineBaseStorageCapacity = 16;
188
189 static PassRefPtr<Structure> createStructure(JSValuePtr prototype)
190 {
191 return Structure::create(prototype, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot));
192 }
193
194 protected:
195 bool getOwnPropertySlotForWrite(ExecState*, const Identifier&, PropertySlot&, bool& slotIsWriteable);
196
197 private:
198 bool inlineGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
199
200 const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const;
201 Structure* createInheritorID();
202
203 RefPtr<Structure> m_inheritorID;
204
205 PropertyStorage m_propertyStorage;
206 JSValuePtr m_inlineStorage[inlineStorageCapacity];
207 };
208
209 JSObject* asObject(JSValuePtr);
210
211 JSObject* constructEmptyObject(ExecState*);
212
213 inline JSObject* asObject(JSValuePtr value)
214 {
215 ASSERT(asCell(value)->isObject());
216 return static_cast<JSObject*>(asCell(value));
217 }
218
219 inline JSObject::JSObject(PassRefPtr<Structure> structure)
220 : JSCell(structure.releaseRef()) // ~JSObject balances this ref()
221 , m_propertyStorage(m_inlineStorage)
222 {
223 ASSERT(m_structure);
224 ASSERT(m_structure->propertyStorageCapacity() == inlineStorageCapacity);
225 ASSERT(m_structure->isEmpty());
226 ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype()));
227 }
228
229 inline JSObject::~JSObject()
230 {
231 ASSERT(m_structure);
232 if (m_propertyStorage != m_inlineStorage)
233 delete [] m_propertyStorage;
234 m_structure->deref();
235 }
236
237 inline JSValuePtr JSObject::prototype() const
238 {
239 return m_structure->storedPrototype();
240 }
241
242 inline void JSObject::setPrototype(JSValuePtr prototype)
243 {
244 ASSERT(prototype);
245 RefPtr<Structure> newStructure = Structure::changePrototypeTransition(m_structure, prototype);
246 setStructure(newStructure.release());
247 }
248
249 inline void JSObject::setStructure(PassRefPtr<Structure> structure)
250 {
251 m_structure->deref();
252 m_structure = structure.releaseRef(); // ~JSObject balances this ref()
253 }
254
255 inline Structure* JSObject::inheritorID()
256 {
257 if (m_inheritorID)
258 return m_inheritorID.get();
259 return createInheritorID();
260 }
261
262 inline bool JSCell::isObject(const ClassInfo* info) const
263 {
264 for (const ClassInfo* ci = classInfo(); ci; ci = ci->parentClass) {
265 if (ci == info)
266 return true;
267 }
268 return false;
269 }
270
271 // this method is here to be after the inline declaration of JSCell::isObject
272 inline bool JSValuePtr::isObject(const ClassInfo* classInfo) const
273 {
274 return isCell() && asCell()->isObject(classInfo);
275 }
276
277 ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
278 {
279 if (JSValuePtr* location = getDirectLocation(propertyName)) {
280 if (m_structure->hasGetterSetterProperties() && location[0].isGetterSetter())
281 fillGetterPropertySlot(slot, location);
282 else
283 slot.setValueSlot(this, location, offsetForLocation(location));
284 return true;
285 }
286
287 // non-standard Netscape extension
288 if (propertyName == exec->propertyNames().underscoreProto) {
289 slot.setValue(prototype());
290 return true;
291 }
292
293 return false;
294 }
295
296 ALWAYS_INLINE bool JSObject::getOwnPropertySlotForWrite(ExecState* exec, const Identifier& propertyName, PropertySlot& slot, bool& slotIsWriteable)
297 {
298 unsigned attributes;
299 if (JSValuePtr* location = getDirectLocation(propertyName, attributes)) {
300 if (m_structure->hasGetterSetterProperties() && location[0].isGetterSetter()) {
301 slotIsWriteable = false;
302 fillGetterPropertySlot(slot, location);
303 } else {
304 slotIsWriteable = !(attributes & ReadOnly);
305 slot.setValueSlot(this, location, offsetForLocation(location));
306 }
307 return true;
308 }
309
310 // non-standard Netscape extension
311 if (propertyName == exec->propertyNames().underscoreProto) {
312 slot.setValue(prototype());
313 slotIsWriteable = false;
314 return true;
315 }
316
317 return false;
318 }
319
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)
324 {
325 return inlineGetOwnPropertySlot(exec, propertyName, slot);
326 }
327
328 ALWAYS_INLINE bool JSCell::fastGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
329 {
330 if (structure()->typeInfo().hasStandardGetOwnPropertySlot())
331 return asObject(this)->inlineGetOwnPropertySlot(exec, propertyName, slot);
332 return getOwnPropertySlot(exec, propertyName, slot);
333 }
334
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)
338 {
339 JSObject* object = this;
340 while (true) {
341 if (object->fastGetOwnPropertySlot(exec, propertyName, slot))
342 return true;
343 JSValuePtr prototype = object->prototype();
344 if (!prototype.isObject())
345 return false;
346 object = asObject(prototype);
347 }
348 }
349
350 inline bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
351 {
352 JSObject* object = this;
353 while (true) {
354 if (object->getOwnPropertySlot(exec, propertyName, slot))
355 return true;
356 JSValuePtr prototype = object->prototype();
357 if (!prototype.isObject())
358 return false;
359 object = asObject(prototype);
360 }
361 }
362
363 inline JSValuePtr JSObject::get(ExecState* exec, const Identifier& propertyName) const
364 {
365 PropertySlot slot(this);
366 if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
367 return slot.getValue(exec, propertyName);
368
369 return jsUndefined();
370 }
371
372 inline JSValuePtr JSObject::get(ExecState* exec, unsigned propertyName) const
373 {
374 PropertySlot slot(this);
375 if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
376 return slot.getValue(exec, propertyName);
377
378 return jsUndefined();
379 }
380
381 inline void JSObject::putDirect(const Identifier& propertyName, JSValuePtr value, unsigned attr)
382 {
383 PutPropertySlot slot;
384 putDirect(propertyName, value, attr, false, slot);
385 }
386
387 inline void JSObject::putDirect(const Identifier& propertyName, JSValuePtr value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
388 {
389 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
390
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)
396 return;
397 m_propertyStorage[offset] = value;
398 slot.setExistingProperty(this, offset);
399 return;
400 }
401
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());
406
407 ASSERT(offset < m_structure->propertyStorageCapacity());
408 m_propertyStorage[offset] = value;
409 slot.setNewProperty(this, offset);
410 return;
411 }
412
413 size_t 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());
418
419 ASSERT(offset < structure->propertyStorageCapacity());
420 m_propertyStorage[offset] = value;
421 slot.setNewProperty(this, offset);
422 slot.setWasTransition(true);
423 setStructure(structure.release());
424 return;
425 }
426
427 unsigned currentAttributes;
428 offset = m_structure->get(propertyName, currentAttributes);
429 if (offset != WTF::notFound) {
430 if (checkReadOnly && currentAttributes & ReadOnly)
431 return;
432 m_propertyStorage[offset] = value;
433 slot.setExistingProperty(this, offset);
434 return;
435 }
436
437 RefPtr<Structure> structure = Structure::addPropertyTransition(m_structure, propertyName, attributes, offset);
438 if (currentCapacity != structure->propertyStorageCapacity())
439 allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity());
440
441 ASSERT(offset < structure->propertyStorageCapacity());
442 m_propertyStorage[offset] = value;
443 slot.setNewProperty(this, offset);
444 slot.setWasTransition(true);
445 setStructure(structure.release());
446 }
447
448 inline void JSObject::putDirectWithoutTransition(const Identifier& propertyName, JSValuePtr value, unsigned attributes)
449 {
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;
455 }
456
457 inline void JSObject::transitionTo(Structure* newStructure)
458 {
459 if (m_structure->propertyStorageCapacity() != newStructure->propertyStorageCapacity())
460 allocatePropertyStorage(m_structure->propertyStorageCapacity(), newStructure->propertyStorageCapacity());
461 setStructure(newStructure);
462 }
463
464 inline JSValuePtr JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
465 {
466 return defaultValue(exec, preferredType);
467 }
468
469 inline JSValuePtr JSValuePtr::get(ExecState* exec, const Identifier& propertyName) const
470 {
471 PropertySlot slot(asValue());
472 return get(exec, propertyName, slot);
473 }
474
475 inline JSValuePtr JSValuePtr::get(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) const
476 {
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);
482 }
483 JSCell* cell = asCell();
484 while (true) {
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);
492 }
493 }
494
495 inline JSValuePtr JSValuePtr::get(ExecState* exec, unsigned propertyName) const
496 {
497 PropertySlot slot(asValue());
498 return get(exec, propertyName, slot);
499 }
500
501 inline JSValuePtr JSValuePtr::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const
502 {
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);
508 }
509 JSCell* cell = const_cast<JSCell*>(asCell());
510 while (true) {
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();
518 }
519 }
520
521 inline void JSValuePtr::put(ExecState* exec, const Identifier& propertyName, JSValuePtr value, PutPropertySlot& slot)
522 {
523 if (UNLIKELY(!isCell())) {
524 JSImmediate::toObject(asValue(), exec)->put(exec, propertyName, value, slot);
525 return;
526 }
527 asCell()->put(exec, propertyName, value, slot);
528 }
529
530 inline void JSValuePtr::put(ExecState* exec, unsigned propertyName, JSValuePtr value)
531 {
532 if (UNLIKELY(!isCell())) {
533 JSImmediate::toObject(asValue(), exec)->put(exec, propertyName, value);
534 return;
535 }
536 asCell()->put(exec, propertyName, value);
537 }
538
539 ALWAYS_INLINE void JSObject::allocatePropertyStorageInline(size_t oldSize, size_t newSize)
540 {
541 ASSERT(newSize > oldSize);
542
543 JSValuePtr* oldPropertyStorage = m_propertyStorage;
544 m_propertyStorage = new JSValuePtr[newSize];
545
546 for (unsigned i = 0; i < oldSize; ++i)
547 m_propertyStorage[i] = oldPropertyStorage[i];
548
549 if (oldPropertyStorage != m_inlineStorage)
550 delete [] oldPropertyStorage;
551 }
552
553 } // namespace JSC
554
555 #endif // JSObject_h