]> git.saurik.com Git - apple/javascriptcore.git/blame - runtime/Structure.cpp
JavaScriptCore-903.tar.gz
[apple/javascriptcore.git] / runtime / Structure.cpp
CommitLineData
9dae56ea 1/*
ba379fdc 2 * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
9dae56ea
A
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "Structure.h"
28
29#include "Identifier.h"
30#include "JSObject.h"
f9bf01c6
A
31#include "JSPropertyNameIterator.h"
32#include "Lookup.h"
9dae56ea
A
33#include "PropertyNameArray.h"
34#include "StructureChain.h"
9dae56ea
A
35#include <wtf/RefCountedLeakCounter.h>
36#include <wtf/RefPtr.h>
37
38#if ENABLE(JSC_MULTIPLE_THREADS)
39#include <wtf/Threading.h>
40#endif
41
42#define DUMP_STRUCTURE_ID_STATISTICS 0
43
44#ifndef NDEBUG
45#define DO_PROPERTYMAP_CONSTENCY_CHECK 0
46#else
47#define DO_PROPERTYMAP_CONSTENCY_CHECK 0
48#endif
49
50using namespace std;
51using namespace WTF;
52
14957cd0 53#if DUMP_PROPERTYMAP_STATS
9dae56ea 54
14957cd0
A
55int numProbes;
56int numCollisions;
57int numRehashes;
58int numRemoves;
9dae56ea 59
9dae56ea
A
60#endif
61
14957cd0 62namespace JSC {
9dae56ea
A
63
64#if DUMP_STRUCTURE_ID_STATISTICS
ba379fdc 65static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>);
9dae56ea
A
66#endif
67
14957cd0 68bool StructureTransitionTable::contains(StringImpl* rep, unsigned attributes) const
4e4e5a6f 69{
14957cd0
A
70 if (isUsingSingleSlot()) {
71 Structure* transition = singleTransition();
72 return transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes;
4e4e5a6f 73 }
14957cd0 74 return map()->contains(make_pair(rep, attributes));
4e4e5a6f
A
75}
76
14957cd0 77inline Structure* StructureTransitionTable::get(StringImpl* rep, unsigned attributes) const
4e4e5a6f 78{
14957cd0 79 if (isUsingSingleSlot()) {
4e4e5a6f 80 Structure* transition = singleTransition();
14957cd0 81 return (transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes) ? transition : 0;
4e4e5a6f 82 }
14957cd0 83 return map()->get(make_pair(rep, attributes));
4e4e5a6f
A
84}
85
14957cd0 86inline void StructureTransitionTable::remove(Structure* structure)
4e4e5a6f 87{
14957cd0
A
88 if (isUsingSingleSlot()) {
89 // If more than one transition had been added, then we wouldn't be in
90 // single slot mode (even despecifying a from a specific value triggers
91 // map mode).
92 // As such, the passed structure *must* be the existing transition.
93 ASSERT(singleTransition() == structure);
94 clearSingleTransition();
95 } else {
96 // Check whether a mapping exists for structure's key, and whether the
97 // entry is structure (the latter check may fail if we initially had a
98 // transition with a specific value, and this has been despecified).
99
100 // Newer versions of the STL have an std::make_pair function that takes rvalue references.
101 // When either of the parameters are bitfields, the C++ compiler will try to bind them as lvalues, which is invalid. To work around this, use unary "+" to make the parameter an rvalue.
102 // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details
103 TransitionMap::iterator entry = map()->find(make_pair(structure->m_nameInPrevious, +structure->m_attributesInPrevious));
104 if (entry != map()->end() && structure == entry.get().second)
105 map()->remove(entry);
4e4e5a6f 106 }
4e4e5a6f
A
107}
108
14957cd0 109inline void StructureTransitionTable::add(JSGlobalData& globalData, Structure* structure)
4e4e5a6f 110{
14957cd0
A
111 if (isUsingSingleSlot()) {
112 Structure* existingTransition = singleTransition();
113
114 // This handles the first transition being added.
115 if (!existingTransition) {
116 setSingleTransition(globalData, structure);
4e4e5a6f
A
117 return;
118 }
14957cd0
A
119
120 // This handles the second transition being added
121 // (or the first transition being despecified!)
122 setMap(new TransitionMap());
123 add(globalData, existingTransition);
4e4e5a6f 124 }
14957cd0
A
125
126 // Add the structure to the map.
127
128 // Newer versions of the STL have an std::make_pair function that takes rvalue references.
129 // When either of the parameters are bitfields, the C++ compiler will try to bind them as lvalues, which is invalid. To work around this, use unary "+" to make the parameter an rvalue.
130 // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details
131 std::pair<TransitionMap::iterator, bool> result = map()->add(globalData, make_pair(structure->m_nameInPrevious, +structure->m_attributesInPrevious), structure);
132 if (!result.second) {
133 // There already is an entry! - we should only hit this when despecifying.
134 ASSERT(result.first.get().second->m_specificValueInPrevious);
135 ASSERT(!structure->m_specificValueInPrevious);
136 map()->set(result.first, structure);
4e4e5a6f
A
137 }
138}
139
9dae56ea
A
140void Structure::dumpStatistics()
141{
142#if DUMP_STRUCTURE_ID_STATISTICS
143 unsigned numberLeaf = 0;
144 unsigned numberUsingSingleSlot = 0;
145 unsigned numberSingletons = 0;
146 unsigned numberWithPropertyMaps = 0;
147 unsigned totalPropertyMapsSize = 0;
148
149 HashSet<Structure*>::const_iterator end = liveStructureSet.end();
150 for (HashSet<Structure*>::const_iterator it = liveStructureSet.begin(); it != end; ++it) {
151 Structure* structure = *it;
14957cd0
A
152
153 switch (structure->m_transitionTable.size()) {
154 case 0:
9dae56ea 155 ++numberLeaf;
14957cd0
A
156 if (!structure->m_previous)
157 ++numberSingletons;
158 break;
9dae56ea 159
14957cd0
A
160 case 1:
161 ++numberUsingSingleSlot;
162 break;
9dae56ea
A
163 }
164
165 if (structure->m_propertyTable) {
166 ++numberWithPropertyMaps;
14957cd0 167 totalPropertyMapsSize += structure->m_propertyTable->sizeInMemory();
9dae56ea
A
168 }
169 }
170
171 printf("Number of live Structures: %d\n", liveStructureSet.size());
172 printf("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot);
173 printf("Number of Structures that are leaf nodes: %d\n", numberLeaf);
174 printf("Number of Structures that singletons: %d\n", numberSingletons);
175 printf("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps);
176
177 printf("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure)));
178 printf("Size of sum of all property maps: %d\n", totalPropertyMapsSize);
179 printf("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size()));
180#else
181 printf("Dumping Structure statistics is not enabled.\n");
182#endif
183}
184
14957cd0
A
185Structure::Structure(JSGlobalData& globalData, JSValue prototype, const TypeInfo& typeInfo, unsigned anonymousSlotCount, const ClassInfo* classInfo)
186 : JSCell(globalData, globalData.structureStructure.get())
187 , m_typeInfo(typeInfo)
188 , m_prototype(globalData, this, prototype)
189 , m_classInfo(classInfo)
190 , m_propertyStorageCapacity(typeInfo.isFinal() ? JSFinalObject_inlineStorageCapacity : JSNonFinalObject_inlineStorageCapacity)
9dae56ea 191 , m_offset(noOffset)
ba379fdc 192 , m_dictionaryKind(NoneDictionaryKind)
9dae56ea
A
193 , m_isPinnedPropertyTable(false)
194 , m_hasGetterSetterProperties(false)
14957cd0 195 , m_hasNonEnumerableProperties(false)
9dae56ea 196 , m_attributesInPrevious(0)
f9bf01c6
A
197 , m_specificFunctionThrashCount(0)
198 , m_anonymousSlotCount(anonymousSlotCount)
14957cd0
A
199 , m_preventExtensions(false)
200 , m_didTransition(false)
9dae56ea
A
201{
202 ASSERT(m_prototype);
203 ASSERT(m_prototype.isObject() || m_prototype.isNull());
9dae56ea
A
204}
205
14957cd0 206const ClassInfo Structure::s_info = { "Structure", 0, 0, 0 };
9dae56ea 207
14957cd0
A
208Structure::Structure(JSGlobalData& globalData)
209 : JSCell(globalData, this, CreatingEarlyCell)
210 , m_typeInfo(CompoundType, OverridesVisitChildren)
211 , m_prototype(globalData, this, jsNull())
212 , m_classInfo(&s_info)
213 , m_propertyStorageCapacity(0)
214 , m_offset(noOffset)
215 , m_dictionaryKind(NoneDictionaryKind)
216 , m_isPinnedPropertyTable(false)
217 , m_hasGetterSetterProperties(false)
218 , m_hasNonEnumerableProperties(false)
219 , m_attributesInPrevious(0)
220 , m_specificFunctionThrashCount(0)
221 , m_anonymousSlotCount(0)
222 , m_preventExtensions(false)
223 , m_didTransition(false)
9dae56ea 224{
14957cd0
A
225 ASSERT(m_prototype);
226 ASSERT(m_prototype.isNull());
227 ASSERT(!globalData.structureStructure);
9dae56ea
A
228}
229
14957cd0
A
230Structure::Structure(JSGlobalData& globalData, const Structure* previous)
231 : JSCell(globalData, globalData.structureStructure.get())
232 , m_typeInfo(previous->typeInfo())
233 , m_prototype(globalData, this, previous->storedPrototype())
234 , m_classInfo(previous->m_classInfo)
235 , m_propertyStorageCapacity(previous->m_propertyStorageCapacity)
236 , m_offset(noOffset)
237 , m_dictionaryKind(NoneDictionaryKind)
238 , m_isPinnedPropertyTable(false)
239 , m_hasGetterSetterProperties(previous->m_hasGetterSetterProperties)
240 , m_hasNonEnumerableProperties(previous->m_hasNonEnumerableProperties)
241 , m_attributesInPrevious(0)
242 , m_specificFunctionThrashCount(previous->m_specificFunctionThrashCount)
243 , m_anonymousSlotCount(previous->anonymousSlotCount())
244 , m_preventExtensions(previous->m_preventExtensions)
245 , m_didTransition(true)
9dae56ea 246{
14957cd0
A
247 ASSERT(m_prototype);
248 ASSERT(m_prototype.isObject() || m_prototype.isNull());
9dae56ea
A
249}
250
14957cd0 251Structure::~Structure()
9dae56ea 252{
9dae56ea
A
253}
254
14957cd0 255void Structure::materializePropertyMap(JSGlobalData& globalData)
9dae56ea 256{
14957cd0 257 ASSERT(structure()->classInfo() == &s_info);
9dae56ea
A
258 ASSERT(!m_propertyTable);
259
260 Vector<Structure*, 8> structures;
261 structures.append(this);
262
263 Structure* structure = this;
264
14957cd0 265 // Search for the last Structure with a property table.
9dae56ea
A
266 while ((structure = structure->previousID())) {
267 if (structure->m_isPinnedPropertyTable) {
268 ASSERT(structure->m_propertyTable);
269 ASSERT(!structure->m_previous);
270
14957cd0 271 m_propertyTable = structure->m_propertyTable->copy(globalData, 0, m_offset + 1);
9dae56ea
A
272 break;
273 }
274
275 structures.append(structure);
276 }
277
278 if (!m_propertyTable)
14957cd0 279 createPropertyMap(m_offset + 1);
9dae56ea
A
280
281 for (ptrdiff_t i = structures.size() - 2; i >= 0; --i) {
282 structure = structures[i];
14957cd0
A
283 PropertyMapEntry entry(globalData, this, structure->m_nameInPrevious.get(), m_anonymousSlotCount + structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious.get());
284 m_propertyTable->add(entry);
9dae56ea
A
285 }
286}
287
9dae56ea
A
288void Structure::growPropertyStorageCapacity()
289{
14957cd0
A
290 if (isUsingInlineStorage())
291 m_propertyStorageCapacity = JSObject::baseExternalStorageCapacity;
9dae56ea
A
292 else
293 m_propertyStorageCapacity *= 2;
294}
295
14957cd0 296void Structure::despecifyDictionaryFunction(JSGlobalData& globalData, const Identifier& propertyName)
ba379fdc 297{
14957cd0 298 StringImpl* rep = propertyName.impl();
ba379fdc 299
14957cd0 300 materializePropertyMapIfNecessary(globalData);
ba379fdc
A
301
302 ASSERT(isDictionary());
303 ASSERT(m_propertyTable);
304
14957cd0
A
305 PropertyMapEntry* entry = m_propertyTable->find(rep).first;
306 ASSERT(entry);
307 entry->specificValue.clear();
ba379fdc
A
308}
309
14957cd0 310Structure* Structure::addPropertyTransitionToExistingStructure(Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset)
9dae56ea 311{
ba379fdc 312 ASSERT(!structure->isDictionary());
9dae56ea
A
313 ASSERT(structure->typeInfo().type() == ObjectType);
314
14957cd0
A
315 if (Structure* existingTransition = structure->m_transitionTable.get(propertyName.impl(), attributes)) {
316 JSCell* specificValueInPrevious = existingTransition->m_specificValueInPrevious.get();
317 if (specificValueInPrevious && specificValueInPrevious != specificValue)
318 return 0;
f9bf01c6
A
319 ASSERT(existingTransition->m_offset != noOffset);
320 offset = existingTransition->m_offset + existingTransition->m_anonymousSlotCount;
321 ASSERT(offset >= structure->m_anonymousSlotCount);
322 ASSERT(structure->m_anonymousSlotCount == existingTransition->m_anonymousSlotCount);
323 return existingTransition;
9dae56ea
A
324 }
325
326 return 0;
327}
328
14957cd0 329Structure* Structure::addPropertyTransition(JSGlobalData& globalData, Structure* structure, const Identifier& propertyName, unsigned attributes, JSCell* specificValue, size_t& offset)
9dae56ea 330{
14957cd0
A
331 // If we have a specific function, we may have got to this point if there is
332 // already a transition with the correct property name and attributes, but
333 // specialized to a different function. In this case we just want to give up
334 // and despecialize the transition.
335 // In this case we clear the value of specificFunction which will result
336 // in us adding a non-specific transition, and any subsequent lookup in
337 // Structure::addPropertyTransitionToExistingStructure will just use that.
338 if (specificValue && structure->m_transitionTable.contains(propertyName.impl(), attributes))
339 specificValue = 0;
340
ba379fdc 341 ASSERT(!structure->isDictionary());
9dae56ea 342 ASSERT(structure->typeInfo().type() == ObjectType);
ba379fdc 343 ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset));
f9bf01c6
A
344
345 if (structure->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
346 specificValue = 0;
9dae56ea
A
347
348 if (structure->transitionCount() > s_maxTransitionLength) {
14957cd0 349 Structure* transition = toCacheableDictionaryTransition(globalData, structure);
ba379fdc 350 ASSERT(structure != transition);
14957cd0 351 offset = transition->putSpecificValue(globalData, propertyName, attributes, specificValue);
f9bf01c6
A
352 ASSERT(offset >= structure->m_anonymousSlotCount);
353 ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount);
9dae56ea
A
354 if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
355 transition->growPropertyStorageCapacity();
14957cd0 356 return transition;
9dae56ea
A
357 }
358
14957cd0 359 Structure* transition = create(globalData, structure);
ba379fdc 360
14957cd0
A
361 transition->m_cachedPrototypeChain.setMayBeNull(globalData, transition, structure->m_cachedPrototypeChain.get());
362 transition->m_previous.set(globalData, transition, structure);
363 transition->m_nameInPrevious = propertyName.impl();
9dae56ea 364 transition->m_attributesInPrevious = attributes;
14957cd0 365 transition->m_specificValueInPrevious.setMayBeNull(globalData, transition, specificValue);
9dae56ea
A
366
367 if (structure->m_propertyTable) {
368 if (structure->m_isPinnedPropertyTable)
14957cd0
A
369 transition->m_propertyTable = structure->m_propertyTable->copy(globalData, 0, structure->m_propertyTable->size() + 1);
370 else
371 transition->m_propertyTable = structure->m_propertyTable.release();
9dae56ea
A
372 } else {
373 if (structure->m_previous)
14957cd0 374 transition->materializePropertyMap(globalData);
9dae56ea 375 else
14957cd0 376 transition->createPropertyMap();
9dae56ea
A
377 }
378
14957cd0 379 offset = transition->putSpecificValue(globalData, propertyName, attributes, specificValue);
f9bf01c6
A
380 ASSERT(offset >= structure->m_anonymousSlotCount);
381 ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount);
9dae56ea
A
382 if (transition->propertyStorageSize() > transition->propertyStorageCapacity())
383 transition->growPropertyStorageCapacity();
384
f9bf01c6
A
385 transition->m_offset = offset - structure->m_anonymousSlotCount;
386 ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
14957cd0
A
387 structure->m_transitionTable.add(globalData, transition);
388 return transition;
9dae56ea
A
389}
390
14957cd0 391Structure* Structure::removePropertyTransition(JSGlobalData& globalData, Structure* structure, const Identifier& propertyName, size_t& offset)
9dae56ea 392{
ba379fdc 393 ASSERT(!structure->isUncacheableDictionary());
9dae56ea 394
14957cd0 395 Structure* transition = toUncacheableDictionaryTransition(globalData, structure);
9dae56ea
A
396
397 offset = transition->remove(propertyName);
f9bf01c6
A
398 ASSERT(offset >= structure->m_anonymousSlotCount);
399 ASSERT(structure->m_anonymousSlotCount == transition->m_anonymousSlotCount);
9dae56ea 400
14957cd0 401 return transition;
9dae56ea
A
402}
403
14957cd0 404Structure* Structure::changePrototypeTransition(JSGlobalData& globalData, Structure* structure, JSValue prototype)
9dae56ea 405{
14957cd0 406 Structure* transition = create(globalData, structure);
9dae56ea 407
14957cd0 408 transition->m_prototype.set(globalData, transition, prototype);
9dae56ea
A
409
410 // Don't set m_offset, as one can not transition to this.
411
14957cd0
A
412 structure->materializePropertyMapIfNecessary(globalData);
413 transition->m_propertyTable = structure->copyPropertyTable(globalData, transition);
9dae56ea 414 transition->m_isPinnedPropertyTable = true;
f9bf01c6
A
415
416 ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
14957cd0 417 return transition;
9dae56ea
A
418}
419
14957cd0 420Structure* Structure::despecifyFunctionTransition(JSGlobalData& globalData, Structure* structure, const Identifier& replaceFunction)
ba379fdc 421{
f9bf01c6 422 ASSERT(structure->m_specificFunctionThrashCount < maxSpecificFunctionThrashCount);
14957cd0 423 Structure* transition = create(globalData, structure);
ba379fdc 424
14957cd0 425 ++transition->m_specificFunctionThrashCount;
ba379fdc
A
426
427 // Don't set m_offset, as one can not transition to this.
428
14957cd0
A
429 structure->materializePropertyMapIfNecessary(globalData);
430 transition->m_propertyTable = structure->copyPropertyTable(globalData, transition);
ba379fdc
A
431 transition->m_isPinnedPropertyTable = true;
432
f9bf01c6 433 if (transition->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
14957cd0 434 transition->despecifyAllFunctions(globalData);
f9bf01c6 435 else {
14957cd0 436 bool removed = transition->despecifyFunction(globalData, replaceFunction);
f9bf01c6
A
437 ASSERT_UNUSED(removed, removed);
438 }
439
440 ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
14957cd0 441 return transition;
ba379fdc
A
442}
443
14957cd0 444Structure* Structure::getterSetterTransition(JSGlobalData& globalData, Structure* structure)
9dae56ea 445{
14957cd0 446 Structure* transition = create(globalData, structure);
9dae56ea
A
447
448 // Don't set m_offset, as one can not transition to this.
449
14957cd0
A
450 structure->materializePropertyMapIfNecessary(globalData);
451 transition->m_propertyTable = structure->copyPropertyTable(globalData, transition);
9dae56ea 452 transition->m_isPinnedPropertyTable = true;
f9bf01c6
A
453
454 ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
14957cd0 455 return transition;
9dae56ea
A
456}
457
14957cd0 458Structure* Structure::toDictionaryTransition(JSGlobalData& globalData, Structure* structure, DictionaryKind kind)
9dae56ea 459{
ba379fdc
A
460 ASSERT(!structure->isUncacheableDictionary());
461
14957cd0
A
462 Structure* transition = create(globalData, structure);
463
464 structure->materializePropertyMapIfNecessary(globalData);
465 transition->m_propertyTable = structure->copyPropertyTable(globalData, transition);
466 transition->m_isPinnedPropertyTable = true;
ba379fdc 467 transition->m_dictionaryKind = kind;
ba379fdc 468
14957cd0
A
469 ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
470 return transition;
471}
472
473Structure* Structure::toCacheableDictionaryTransition(JSGlobalData& globalData, Structure* structure)
474{
475 return toDictionaryTransition(globalData, structure, CachedDictionaryKind);
476}
477
478Structure* Structure::toUncacheableDictionaryTransition(JSGlobalData& globalData, Structure* structure)
479{
480 return toDictionaryTransition(globalData, structure, UncachedDictionaryKind);
481}
482
483// In future we may want to cache this transition.
484Structure* Structure::sealTransition(JSGlobalData& globalData, Structure* structure)
485{
486 Structure* transition = preventExtensionsTransition(globalData, structure);
487
488 if (transition->m_propertyTable) {
489 PropertyTable::iterator end = transition->m_propertyTable->end();
490 for (PropertyTable::iterator iter = transition->m_propertyTable->begin(); iter != end; ++iter)
491 iter->attributes |= DontDelete;
492 }
493
494 return transition;
495}
496
497// In future we may want to cache this transition.
498Structure* Structure::freezeTransition(JSGlobalData& globalData, Structure* structure)
499{
500 Structure* transition = preventExtensionsTransition(globalData, structure);
501
502 if (transition->m_propertyTable) {
503 PropertyTable::iterator end = transition->m_propertyTable->end();
504 for (PropertyTable::iterator iter = transition->m_propertyTable->begin(); iter != end; ++iter)
505 iter->attributes |= (DontDelete | ReadOnly);
506 }
507
508 return transition;
509}
510
511// In future we may want to cache this transition.
512Structure* Structure::preventExtensionsTransition(JSGlobalData& globalData, Structure* structure)
513{
514 Structure* transition = create(globalData, structure);
515
516 // Don't set m_offset, as one can not transition to this.
517
518 structure->materializePropertyMapIfNecessary(globalData);
519 transition->m_propertyTable = structure->copyPropertyTable(globalData, transition);
9dae56ea 520 transition->m_isPinnedPropertyTable = true;
14957cd0
A
521 transition->m_preventExtensions = true;
522
f9bf01c6 523 ASSERT(structure->anonymousSlotCount() == transition->anonymousSlotCount());
14957cd0 524 return transition;
9dae56ea
A
525}
526
14957cd0
A
527// In future we may want to cache this property.
528bool Structure::isSealed(JSGlobalData& globalData)
9dae56ea 529{
14957cd0
A
530 if (isExtensible())
531 return false;
532
533 materializePropertyMapIfNecessary(globalData);
534 if (!m_propertyTable)
535 return true;
536
537 PropertyTable::iterator end = m_propertyTable->end();
538 for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) {
539 if ((iter->attributes & DontDelete) != DontDelete)
540 return false;
541 }
542 return true;
ba379fdc 543}
9dae56ea 544
14957cd0
A
545// In future we may want to cache this property.
546bool Structure::isFrozen(JSGlobalData& globalData)
ba379fdc 547{
14957cd0
A
548 if (isExtensible())
549 return false;
550
551 materializePropertyMapIfNecessary(globalData);
552 if (!m_propertyTable)
553 return true;
554
555 PropertyTable::iterator end = m_propertyTable->end();
556 for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) {
557 if ((iter->attributes & (DontDelete | ReadOnly)) != (DontDelete | ReadOnly))
558 return false;
559 }
560 return true;
ba379fdc 561}
9dae56ea 562
14957cd0 563Structure* Structure::flattenDictionaryStructure(JSGlobalData& globalData, JSObject* object)
ba379fdc
A
564{
565 ASSERT(isDictionary());
566 if (isUncacheableDictionary()) {
567 ASSERT(m_propertyTable);
ba379fdc 568
f9bf01c6 569 unsigned anonymousSlotCount = m_anonymousSlotCount;
14957cd0
A
570 size_t propertyCount = m_propertyTable->size();
571 Vector<JSValue> values(propertyCount);
572
573 unsigned i = 0;
574 PropertyTable::iterator end = m_propertyTable->end();
575 for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter, ++i) {
576 values[i] = object->getDirectOffset(iter->offset);
ba379fdc 577 // Update property table to have the new property offsets
14957cd0 578 iter->offset = anonymousSlotCount + i;
ba379fdc
A
579 }
580
581 // Copy the original property values into their final locations
582 for (unsigned i = 0; i < propertyCount; i++)
14957cd0 583 object->putDirectOffset(globalData, anonymousSlotCount + i, values[i]);
ba379fdc 584
14957cd0 585 m_propertyTable->clearDeletedOffsets();
ba379fdc 586 }
9dae56ea 587
ba379fdc
A
588 m_dictionaryKind = NoneDictionaryKind;
589 return this;
9dae56ea
A
590}
591
14957cd0 592size_t Structure::addPropertyWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
9dae56ea 593{
f9bf01c6
A
594 ASSERT(!m_enumerationCache);
595
596 if (m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
597 specificValue = 0;
9dae56ea 598
14957cd0 599 materializePropertyMapIfNecessary(globalData);
9dae56ea
A
600
601 m_isPinnedPropertyTable = true;
f9bf01c6 602
14957cd0 603 size_t offset = putSpecificValue(globalData, propertyName, attributes, specificValue);
f9bf01c6 604 ASSERT(offset >= m_anonymousSlotCount);
9dae56ea
A
605 if (propertyStorageSize() > propertyStorageCapacity())
606 growPropertyStorageCapacity();
9dae56ea
A
607 return offset;
608}
609
14957cd0 610size_t Structure::removePropertyWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName)
9dae56ea 611{
ba379fdc 612 ASSERT(isUncacheableDictionary());
f9bf01c6 613 ASSERT(!m_enumerationCache);
9dae56ea 614
14957cd0 615 materializePropertyMapIfNecessary(globalData);
9dae56ea
A
616
617 m_isPinnedPropertyTable = true;
618 size_t offset = remove(propertyName);
f9bf01c6 619 ASSERT(offset >= m_anonymousSlotCount);
9dae56ea
A
620 return offset;
621}
622
623#if DUMP_PROPERTYMAP_STATS
624
9dae56ea
A
625struct PropertyMapStatisticsExitLogger {
626 ~PropertyMapStatisticsExitLogger();
627};
628
629static PropertyMapStatisticsExitLogger logger;
630
631PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger()
632{
633 printf("\nJSC::PropertyMap statistics\n\n");
634 printf("%d probes\n", numProbes);
635 printf("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes);
636 printf("%d rehashes\n", numRehashes);
637 printf("%d removes\n", numRemoves);
638}
639
640#endif
641
9dae56ea
A
642#if !DO_PROPERTYMAP_CONSTENCY_CHECK
643
644inline void Structure::checkConsistency()
645{
646}
647
648#endif
649
14957cd0 650PassOwnPtr<PropertyTable> Structure::copyPropertyTable(JSGlobalData& globalData, Structure* owner)
9dae56ea 651{
14957cd0 652 return adoptPtr(m_propertyTable ? new PropertyTable(globalData, owner, *m_propertyTable) : 0);
9dae56ea
A
653}
654
14957cd0 655size_t Structure::get(JSGlobalData& globalData, StringImpl* propertyName, unsigned& attributes, JSCell*& specificValue)
9dae56ea 656{
14957cd0 657 materializePropertyMapIfNecessary(globalData);
9dae56ea 658 if (!m_propertyTable)
14957cd0 659 return WTF::notFound;
9dae56ea 660
14957cd0
A
661 PropertyMapEntry* entry = m_propertyTable->find(propertyName).first;
662 if (!entry)
663 return WTF::notFound;
9dae56ea 664
14957cd0
A
665 attributes = entry->attributes;
666 specificValue = entry->specificValue.get();
667 ASSERT(entry->offset >= m_anonymousSlotCount);
668 return entry->offset;
9dae56ea
A
669}
670
14957cd0 671bool Structure::despecifyFunction(JSGlobalData& globalData, const Identifier& propertyName)
ba379fdc 672{
14957cd0 673 materializePropertyMapIfNecessary(globalData);
ba379fdc
A
674 if (!m_propertyTable)
675 return false;
676
14957cd0
A
677 ASSERT(!propertyName.isNull());
678 PropertyMapEntry* entry = m_propertyTable->find(propertyName.impl()).first;
679 if (!entry)
ba379fdc
A
680 return false;
681
14957cd0
A
682 ASSERT(entry->specificValue);
683 entry->specificValue.clear();
684 return true;
ba379fdc
A
685}
686
14957cd0 687void Structure::despecifyAllFunctions(JSGlobalData& globalData)
f9bf01c6 688{
14957cd0 689 materializePropertyMapIfNecessary(globalData);
f9bf01c6
A
690 if (!m_propertyTable)
691 return;
14957cd0
A
692
693 PropertyTable::iterator end = m_propertyTable->end();
694 for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter)
695 iter->specificValue.clear();
f9bf01c6
A
696}
697
14957cd0 698size_t Structure::putSpecificValue(JSGlobalData& globalData, const Identifier& propertyName, unsigned attributes, JSCell* specificValue)
9dae56ea
A
699{
700 ASSERT(!propertyName.isNull());
14957cd0 701 ASSERT(get(globalData, propertyName) == notFound);
9dae56ea
A
702
703 checkConsistency();
f9bf01c6
A
704 if (attributes & DontEnum)
705 m_hasNonEnumerableProperties = true;
706
14957cd0 707 StringImpl* rep = propertyName.impl();
9dae56ea
A
708
709 if (!m_propertyTable)
14957cd0 710 createPropertyMap();
9dae56ea
A
711
712 unsigned newOffset;
14957cd0
A
713
714 if (m_propertyTable->hasDeletedOffset())
715 newOffset = m_propertyTable->getDeletedOffset();
716 else
717 newOffset = m_propertyTable->size() + m_anonymousSlotCount;
f9bf01c6 718 ASSERT(newOffset >= m_anonymousSlotCount);
9dae56ea 719
14957cd0 720 m_propertyTable->add(PropertyMapEntry(globalData, this, rep, newOffset, attributes, specificValue));
9dae56ea
A
721
722 checkConsistency();
723 return newOffset;
724}
725
726size_t Structure::remove(const Identifier& propertyName)
727{
728 ASSERT(!propertyName.isNull());
729
730 checkConsistency();
731
14957cd0 732 StringImpl* rep = propertyName.impl();
9dae56ea
A
733
734 if (!m_propertyTable)
735 return notFound;
736
14957cd0
A
737 PropertyTable::find_iterator position = m_propertyTable->find(rep);
738 if (!position.first)
739 return notFound;
9dae56ea 740
14957cd0 741 size_t offset = position.first->offset;
f9bf01c6 742 ASSERT(offset >= m_anonymousSlotCount);
9dae56ea 743
14957cd0
A
744 m_propertyTable->remove(position);
745 m_propertyTable->addDeletedOffset(offset);
9dae56ea
A
746
747 checkConsistency();
748 return offset;
749}
750
14957cd0 751void Structure::createPropertyMap(unsigned capacity)
9dae56ea
A
752{
753 ASSERT(!m_propertyTable);
9dae56ea
A
754
755 checkConsistency();
14957cd0 756 m_propertyTable = adoptPtr(new PropertyTable(capacity));
9dae56ea
A
757 checkConsistency();
758}
759
14957cd0 760void Structure::getPropertyNames(JSGlobalData& globalData, PropertyNameArray& propertyNames, EnumerationMode mode)
9dae56ea 761{
14957cd0
A
762 materializePropertyMapIfNecessary(globalData);
763 if (!m_propertyTable)
764 return;
9dae56ea 765
14957cd0 766 bool knownUnique = !propertyNames.size();
9dae56ea 767
14957cd0
A
768 PropertyTable::iterator end = m_propertyTable->end();
769 for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) {
770 ASSERT(m_hasNonEnumerableProperties || !(iter->attributes & DontEnum));
771 if (!(iter->attributes & DontEnum) || (mode == IncludeDontEnumProperties)) {
772 if (knownUnique)
773 propertyNames.addKnownUnique(iter->key);
774 else
775 propertyNames.add(iter->key);
9dae56ea
A
776 }
777 }
9dae56ea
A
778}
779
14957cd0 780void Structure::visitChildren(SlotVisitor& visitor)
9dae56ea 781{
14957cd0
A
782 ASSERT_GC_OBJECT_INHERITS(this, &s_info);
783 ASSERT(structure()->typeInfo().overridesVisitChildren());
784 JSCell::visitChildren(visitor);
785 if (m_prototype)
786 visitor.append(&m_prototype);
787 if (m_cachedPrototypeChain)
788 visitor.append(&m_cachedPrototypeChain);
789 if (m_previous)
790 visitor.append(&m_previous);
791 if (m_specificValueInPrevious)
792 visitor.append(&m_specificValueInPrevious);
793 if (m_enumerationCache)
794 visitor.append(&m_enumerationCache);
795 if (m_propertyTable) {
796 PropertyTable::iterator end = m_propertyTable->end();
797 for (PropertyTable::iterator ptr = m_propertyTable->begin(); ptr != end; ++ptr) {
798 if (ptr->specificValue)
799 visitor.append(&ptr->specificValue);
9dae56ea 800 }
9dae56ea
A
801 }
802}
803
9dae56ea
A
804#if DO_PROPERTYMAP_CONSTENCY_CHECK
805
14957cd0 806void PropertyTable::checkConsistency()
9dae56ea 807{
14957cd0
A
808 ASSERT(m_indexSize >= PropertyTable::MinimumTableSize);
809 ASSERT(m_indexMask);
810 ASSERT(m_indexSize == m_indexMask + 1);
811 ASSERT(!(m_indexSize & m_indexMask));
9dae56ea 812
14957cd0
A
813 ASSERT(m_keyCount <= m_indexSize / 2);
814 ASSERT(m_keyCount + m_deletedCount <= m_indexSize / 2);
815 ASSERT(m_deletedCount <= m_indexSize / 4);
9dae56ea
A
816
817 unsigned indexCount = 0;
818 unsigned deletedIndexCount = 0;
14957cd0
A
819 for (unsigned a = 0; a != m_indexSize; ++a) {
820 unsigned entryIndex = m_index[a];
821 if (entryIndex == PropertyTable::EmptyEntryIndex)
9dae56ea 822 continue;
14957cd0 823 if (entryIndex == deletedEntryIndex()) {
9dae56ea
A
824 ++deletedIndexCount;
825 continue;
826 }
14957cd0
A
827 ASSERT(entryIndex < deletedEntryIndex());
828 ASSERT(entryIndex - 1 <= usedCount());
9dae56ea
A
829 ++indexCount;
830
14957cd0
A
831 for (unsigned b = a + 1; b != m_indexSize; ++b)
832 ASSERT(m_index[b] != entryIndex);
9dae56ea 833 }
14957cd0
A
834 ASSERT(indexCount == m_keyCount);
835 ASSERT(deletedIndexCount == m_deletedCount);
9dae56ea 836
14957cd0 837 ASSERT(!table()[deletedEntryIndex() - 1].key);
9dae56ea
A
838
839 unsigned nonEmptyEntryCount = 0;
14957cd0
A
840 for (unsigned c = 0; c < usedCount(); ++c) {
841 StringImpl* rep = table()[c].key;
842 if (rep == PROPERTY_MAP_DELETED_ENTRY_KEY)
9dae56ea
A
843 continue;
844 ++nonEmptyEntryCount;
f9bf01c6 845 unsigned i = rep->existingHash();
9dae56ea
A
846 unsigned k = 0;
847 unsigned entryIndex;
848 while (1) {
14957cd0
A
849 entryIndex = m_index[i & m_indexMask];
850 ASSERT(entryIndex != PropertyTable::EmptyEntryIndex);
851 if (rep == table()[entryIndex - 1].key)
9dae56ea
A
852 break;
853 if (k == 0)
f9bf01c6 854 k = 1 | doubleHash(rep->existingHash());
9dae56ea
A
855 i += k;
856 }
857 ASSERT(entryIndex == c + 1);
858 }
859
14957cd0
A
860 ASSERT(nonEmptyEntryCount == m_keyCount);
861}
862
863void Structure::checkConsistency()
864{
865 if (!m_propertyTable)
866 return;
867
868 if (!m_hasNonEnumerableProperties) {
869 PropertyTable::iterator end = m_propertyTable->end();
870 for (PropertyTable::iterator iter = m_propertyTable->begin(); iter != end; ++iter) {
871 ASSERT(!(iter->attributes & DontEnum));
872 ASSERT(iter->offset >= m_anonymousSlotCount);
873 }
874 }
875
876 m_propertyTable->checkConsistency();
9dae56ea
A
877}
878
879#endif // DO_PROPERTYMAP_CONSTENCY_CHECK
880
881} // namespace JSC