]>
Commit | Line | Data |
---|---|---|
9dae56ea | 1 | /* |
93a37866 | 2 | * Copyright (C) 2008, 2009, 2013 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 | ||
93a37866 | 29 | #include "CodeBlock.h" |
9dae56ea | 30 | #include "JSObject.h" |
f9bf01c6 A |
31 | #include "JSPropertyNameIterator.h" |
32 | #include "Lookup.h" | |
9dae56ea A |
33 | #include "PropertyNameArray.h" |
34 | #include "StructureChain.h" | |
93a37866 | 35 | #include "StructureRareDataInlines.h" |
9dae56ea A |
36 | #include <wtf/RefCountedLeakCounter.h> |
37 | #include <wtf/RefPtr.h> | |
9dae56ea | 38 | #include <wtf/Threading.h> |
9dae56ea A |
39 | |
40 | #define DUMP_STRUCTURE_ID_STATISTICS 0 | |
41 | ||
42 | #ifndef NDEBUG | |
43 | #define DO_PROPERTYMAP_CONSTENCY_CHECK 0 | |
44 | #else | |
45 | #define DO_PROPERTYMAP_CONSTENCY_CHECK 0 | |
46 | #endif | |
47 | ||
48 | using namespace std; | |
49 | using namespace WTF; | |
50 | ||
14957cd0 | 51 | #if DUMP_PROPERTYMAP_STATS |
9dae56ea | 52 | |
14957cd0 A |
53 | int numProbes; |
54 | int numCollisions; | |
55 | int numRehashes; | |
56 | int numRemoves; | |
9dae56ea | 57 | |
9dae56ea A |
58 | #endif |
59 | ||
14957cd0 | 60 | namespace JSC { |
9dae56ea A |
61 | |
62 | #if DUMP_STRUCTURE_ID_STATISTICS | |
ba379fdc | 63 | static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>); |
9dae56ea A |
64 | #endif |
65 | ||
14957cd0 | 66 | bool StructureTransitionTable::contains(StringImpl* rep, unsigned attributes) const |
4e4e5a6f | 67 | { |
14957cd0 A |
68 | if (isUsingSingleSlot()) { |
69 | Structure* transition = singleTransition(); | |
70 | return transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes; | |
4e4e5a6f | 71 | } |
93a37866 | 72 | return map()->get(make_pair(rep, attributes)); |
4e4e5a6f A |
73 | } |
74 | ||
14957cd0 | 75 | inline Structure* StructureTransitionTable::get(StringImpl* rep, unsigned attributes) const |
4e4e5a6f | 76 | { |
14957cd0 | 77 | if (isUsingSingleSlot()) { |
4e4e5a6f | 78 | Structure* transition = singleTransition(); |
14957cd0 | 79 | return (transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes) ? transition : 0; |
4e4e5a6f | 80 | } |
14957cd0 | 81 | return map()->get(make_pair(rep, attributes)); |
4e4e5a6f A |
82 | } |
83 | ||
93a37866 | 84 | inline void StructureTransitionTable::add(VM& vm, Structure* structure) |
4e4e5a6f | 85 | { |
14957cd0 A |
86 | if (isUsingSingleSlot()) { |
87 | Structure* existingTransition = singleTransition(); | |
88 | ||
89 | // This handles the first transition being added. | |
90 | if (!existingTransition) { | |
93a37866 | 91 | setSingleTransition(vm, structure); |
4e4e5a6f A |
92 | return; |
93 | } | |
14957cd0 A |
94 | |
95 | // This handles the second transition being added | |
96 | // (or the first transition being despecified!) | |
97 | setMap(new TransitionMap()); | |
93a37866 | 98 | add(vm, existingTransition); |
4e4e5a6f | 99 | } |
14957cd0 A |
100 | |
101 | // Add the structure to the map. | |
102 | ||
103 | // Newer versions of the STL have an std::make_pair function that takes rvalue references. | |
104 | // 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. | |
105 | // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details | |
93a37866 | 106 | map()->set(make_pair(structure->m_nameInPrevious, +structure->m_attributesInPrevious), structure); |
4e4e5a6f A |
107 | } |
108 | ||
9dae56ea A |
109 | void Structure::dumpStatistics() |
110 | { | |
111 | #if DUMP_STRUCTURE_ID_STATISTICS | |
112 | unsigned numberLeaf = 0; | |
113 | unsigned numberUsingSingleSlot = 0; | |
114 | unsigned numberSingletons = 0; | |
115 | unsigned numberWithPropertyMaps = 0; | |
116 | unsigned totalPropertyMapsSize = 0; | |
117 | ||
118 | HashSet<Structure*>::const_iterator end = liveStructureSet.end(); | |
119 | for (HashSet<Structure*>::const_iterator it = liveStructureSet.begin(); it != end; ++it) { | |
120 | Structure* structure = *it; | |
14957cd0 A |
121 | |
122 | switch (structure->m_transitionTable.size()) { | |
123 | case 0: | |
9dae56ea | 124 | ++numberLeaf; |
93a37866 | 125 | if (!structure->previousID()) |
14957cd0 A |
126 | ++numberSingletons; |
127 | break; | |
9dae56ea | 128 | |
14957cd0 A |
129 | case 1: |
130 | ++numberUsingSingleSlot; | |
131 | break; | |
9dae56ea A |
132 | } |
133 | ||
93a37866 | 134 | if (structure->propertyTable()) { |
9dae56ea | 135 | ++numberWithPropertyMaps; |
93a37866 | 136 | totalPropertyMapsSize += structure->propertyTable()->sizeInMemory(); |
9dae56ea A |
137 | } |
138 | } | |
139 | ||
93a37866 A |
140 | dataLogF("Number of live Structures: %d\n", liveStructureSet.size()); |
141 | dataLogF("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot); | |
142 | dataLogF("Number of Structures that are leaf nodes: %d\n", numberLeaf); | |
143 | dataLogF("Number of Structures that singletons: %d\n", numberSingletons); | |
144 | dataLogF("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps); | |
9dae56ea | 145 | |
93a37866 A |
146 | dataLogF("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure))); |
147 | dataLogF("Size of sum of all property maps: %d\n", totalPropertyMapsSize); | |
148 | dataLogF("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size())); | |
9dae56ea | 149 | #else |
93a37866 | 150 | dataLogF("Dumping Structure statistics is not enabled.\n"); |
9dae56ea A |
151 | #endif |
152 | } | |
153 | ||
93a37866 A |
154 | Structure::Structure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType, unsigned inlineCapacity) |
155 | : JSCell(vm, vm.structureStructure.get()) | |
156 | , m_globalObject(vm, this, globalObject, WriteBarrier<JSGlobalObject>::MayBeNull) | |
157 | , m_prototype(vm, this, prototype) | |
14957cd0 | 158 | , m_classInfo(classInfo) |
93a37866 A |
159 | , m_transitionWatchpointSet(InitializedWatching) |
160 | , m_offset(invalidOffset) | |
161 | , m_typeInfo(typeInfo) | |
162 | , m_indexingType(indexingType) | |
163 | , m_inlineCapacity(inlineCapacity) | |
ba379fdc | 164 | , m_dictionaryKind(NoneDictionaryKind) |
9dae56ea A |
165 | , m_isPinnedPropertyTable(false) |
166 | , m_hasGetterSetterProperties(false) | |
6fe7ccc8 | 167 | , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(false) |
14957cd0 | 168 | , m_hasNonEnumerableProperties(false) |
9dae56ea | 169 | , m_attributesInPrevious(0) |
f9bf01c6 | 170 | , m_specificFunctionThrashCount(0) |
14957cd0 A |
171 | , m_preventExtensions(false) |
172 | , m_didTransition(false) | |
6fe7ccc8 | 173 | , m_staticFunctionReified(false) |
9dae56ea | 174 | { |
93a37866 A |
175 | ASSERT(inlineCapacity <= JSFinalObject::maxInlineCapacity()); |
176 | ASSERT(static_cast<PropertyOffset>(inlineCapacity) < firstOutOfLineOffset); | |
177 | ASSERT(!typeInfo.structureHasRareData()); | |
9dae56ea A |
178 | } |
179 | ||
6fe7ccc8 | 180 | const ClassInfo Structure::s_info = { "Structure", 0, 0, 0, CREATE_METHOD_TABLE(Structure) }; |
9dae56ea | 181 | |
93a37866 | 182 | Structure::Structure(VM& vm) |
6fe7ccc8 | 183 | : JSCell(CreatingEarlyCell) |
93a37866 | 184 | , m_prototype(vm, this, jsNull()) |
14957cd0 | 185 | , m_classInfo(&s_info) |
93a37866 A |
186 | , m_transitionWatchpointSet(InitializedWatching) |
187 | , m_offset(invalidOffset) | |
188 | , m_typeInfo(CompoundType, OverridesVisitChildren) | |
189 | , m_indexingType(0) | |
190 | , m_inlineCapacity(0) | |
14957cd0 A |
191 | , m_dictionaryKind(NoneDictionaryKind) |
192 | , m_isPinnedPropertyTable(false) | |
193 | , m_hasGetterSetterProperties(false) | |
6fe7ccc8 | 194 | , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(false) |
14957cd0 A |
195 | , m_hasNonEnumerableProperties(false) |
196 | , m_attributesInPrevious(0) | |
197 | , m_specificFunctionThrashCount(0) | |
14957cd0 A |
198 | , m_preventExtensions(false) |
199 | , m_didTransition(false) | |
6fe7ccc8 | 200 | , m_staticFunctionReified(false) |
9dae56ea | 201 | { |
9dae56ea A |
202 | } |
203 | ||
93a37866 A |
204 | Structure::Structure(VM& vm, const Structure* previous) |
205 | : JSCell(vm, vm.structureStructure.get()) | |
206 | , m_prototype(vm, this, previous->storedPrototype()) | |
14957cd0 | 207 | , m_classInfo(previous->m_classInfo) |
93a37866 A |
208 | , m_transitionWatchpointSet(InitializedWatching) |
209 | , m_offset(invalidOffset) | |
210 | , m_typeInfo(previous->typeInfo().type(), previous->typeInfo().flags() & ~StructureHasRareData) | |
211 | , m_indexingType(previous->indexingTypeIncludingHistory()) | |
212 | , m_inlineCapacity(previous->m_inlineCapacity) | |
6fe7ccc8 | 213 | , m_dictionaryKind(previous->m_dictionaryKind) |
14957cd0 A |
214 | , m_isPinnedPropertyTable(false) |
215 | , m_hasGetterSetterProperties(previous->m_hasGetterSetterProperties) | |
6fe7ccc8 | 216 | , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(previous->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto) |
14957cd0 A |
217 | , m_hasNonEnumerableProperties(previous->m_hasNonEnumerableProperties) |
218 | , m_attributesInPrevious(0) | |
219 | , m_specificFunctionThrashCount(previous->m_specificFunctionThrashCount) | |
14957cd0 A |
220 | , m_preventExtensions(previous->m_preventExtensions) |
221 | , m_didTransition(true) | |
6fe7ccc8 | 222 | , m_staticFunctionReified(previous->m_staticFunctionReified) |
9dae56ea | 223 | { |
93a37866 A |
224 | if (previous->typeInfo().structureHasRareData() && previous->rareData()->needsCloning()) |
225 | cloneRareDataFrom(vm, previous); | |
226 | else if (previous->previousID()) | |
227 | m_previousOrRareData.set(vm, this, previous->previousID()); | |
228 | ||
229 | previous->notifyTransitionFromThisStructure(); | |
6fe7ccc8 | 230 | if (previous->m_globalObject) |
93a37866 | 231 | m_globalObject.set(vm, this, previous->m_globalObject.get()); |
9dae56ea A |
232 | } |
233 | ||
6fe7ccc8 | 234 | void Structure::destroy(JSCell* cell) |
9dae56ea | 235 | { |
93a37866 | 236 | static_cast<Structure*>(cell)->Structure::~Structure(); |
9dae56ea A |
237 | } |
238 | ||
93a37866 | 239 | void Structure::materializePropertyMap(VM& vm) |
9dae56ea | 240 | { |
14957cd0 | 241 | ASSERT(structure()->classInfo() == &s_info); |
93a37866 | 242 | ASSERT(!propertyTable()); |
9dae56ea A |
243 | |
244 | Vector<Structure*, 8> structures; | |
245 | structures.append(this); | |
246 | ||
247 | Structure* structure = this; | |
248 | ||
14957cd0 | 249 | // Search for the last Structure with a property table. |
9dae56ea A |
250 | while ((structure = structure->previousID())) { |
251 | if (structure->m_isPinnedPropertyTable) { | |
93a37866 A |
252 | ASSERT(structure->propertyTable()); |
253 | ASSERT(!structure->previousID()); | |
9dae56ea | 254 | |
93a37866 | 255 | propertyTable().set(vm, this, structure->propertyTable()->copy(vm, 0, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity))); |
9dae56ea A |
256 | break; |
257 | } | |
258 | ||
259 | structures.append(structure); | |
260 | } | |
261 | ||
93a37866 A |
262 | if (!propertyTable()) |
263 | createPropertyMap(vm, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity)); | |
9dae56ea | 264 | |
93a37866 | 265 | for (ptrdiff_t i = structures.size() - 1; i >= 0; --i) { |
9dae56ea | 266 | structure = structures[i]; |
93a37866 A |
267 | if (!structure->m_nameInPrevious) |
268 | continue; | |
269 | PropertyMapEntry entry(vm, this, structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious.get()); | |
270 | propertyTable()->add(entry, m_offset, PropertyTable::PropertyOffsetMustNotChange); | |
9dae56ea | 271 | } |
93a37866 A |
272 | |
273 | checkOffsetConsistency(); | |
9dae56ea A |
274 | } |
275 | ||
93a37866 | 276 | inline size_t nextOutOfLineStorageCapacity(size_t currentCapacity) |
9dae56ea | 277 | { |
93a37866 A |
278 | if (!currentCapacity) |
279 | return initialOutOfLineCapacity; | |
280 | return currentCapacity * outOfLineGrowthFactor; | |
9dae56ea A |
281 | } |
282 | ||
93a37866 | 283 | size_t Structure::suggestedNewOutOfLineStorageCapacity() |
6fe7ccc8 | 284 | { |
93a37866 | 285 | return nextOutOfLineStorageCapacity(outOfLineCapacity()); |
6fe7ccc8 | 286 | } |
93a37866 A |
287 | |
288 | void Structure::despecifyDictionaryFunction(VM& vm, PropertyName propertyName) | |
ba379fdc | 289 | { |
93a37866 | 290 | StringImpl* rep = propertyName.uid(); |
ba379fdc | 291 | |
93a37866 | 292 | materializePropertyMapIfNecessary(vm); |
ba379fdc A |
293 | |
294 | ASSERT(isDictionary()); | |
93a37866 | 295 | ASSERT(propertyTable()); |
ba379fdc | 296 | |
93a37866 | 297 | PropertyMapEntry* entry = propertyTable()->find(rep).first; |
14957cd0 A |
298 | ASSERT(entry); |
299 | entry->specificValue.clear(); | |
ba379fdc A |
300 | } |
301 | ||
93a37866 | 302 | Structure* Structure::addPropertyTransitionToExistingStructure(Structure* structure, PropertyName propertyName, unsigned attributes, JSCell* specificValue, PropertyOffset& offset) |
9dae56ea | 303 | { |
ba379fdc | 304 | ASSERT(!structure->isDictionary()); |
6fe7ccc8 | 305 | ASSERT(structure->isObject()); |
9dae56ea | 306 | |
93a37866 | 307 | if (Structure* existingTransition = structure->m_transitionTable.get(propertyName.uid(), attributes)) { |
14957cd0 A |
308 | JSCell* specificValueInPrevious = existingTransition->m_specificValueInPrevious.get(); |
309 | if (specificValueInPrevious && specificValueInPrevious != specificValue) | |
310 | return 0; | |
93a37866 | 311 | validateOffset(existingTransition->m_offset, existingTransition->inlineCapacity()); |
6fe7ccc8 | 312 | offset = existingTransition->m_offset; |
f9bf01c6 | 313 | return existingTransition; |
9dae56ea A |
314 | } |
315 | ||
316 | return 0; | |
317 | } | |
318 | ||
93a37866 A |
319 | bool Structure::anyObjectInChainMayInterceptIndexedAccesses() const |
320 | { | |
321 | for (const Structure* current = this; ;) { | |
322 | if (current->mayInterceptIndexedAccesses()) | |
323 | return true; | |
324 | ||
325 | JSValue prototype = current->storedPrototype(); | |
326 | if (prototype.isNull()) | |
327 | return false; | |
328 | ||
329 | current = asObject(prototype)->structure(); | |
330 | } | |
331 | } | |
332 | ||
333 | bool Structure::needsSlowPutIndexing() const | |
334 | { | |
335 | return anyObjectInChainMayInterceptIndexedAccesses() | |
336 | || globalObject()->isHavingABadTime(); | |
337 | } | |
338 | ||
339 | NonPropertyTransition Structure::suggestedArrayStorageTransition() const | |
340 | { | |
341 | if (needsSlowPutIndexing()) | |
342 | return AllocateSlowPutArrayStorage; | |
343 | ||
344 | return AllocateArrayStorage; | |
345 | } | |
346 | ||
347 | Structure* Structure::addPropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes, JSCell* specificValue, PropertyOffset& offset) | |
9dae56ea | 348 | { |
14957cd0 A |
349 | // If we have a specific function, we may have got to this point if there is |
350 | // already a transition with the correct property name and attributes, but | |
351 | // specialized to a different function. In this case we just want to give up | |
352 | // and despecialize the transition. | |
353 | // In this case we clear the value of specificFunction which will result | |
354 | // in us adding a non-specific transition, and any subsequent lookup in | |
355 | // Structure::addPropertyTransitionToExistingStructure will just use that. | |
93a37866 | 356 | if (specificValue && structure->m_transitionTable.contains(propertyName.uid(), attributes)) |
14957cd0 A |
357 | specificValue = 0; |
358 | ||
ba379fdc | 359 | ASSERT(!structure->isDictionary()); |
6fe7ccc8 | 360 | ASSERT(structure->isObject()); |
ba379fdc | 361 | ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset)); |
f9bf01c6 A |
362 | |
363 | if (structure->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) | |
364 | specificValue = 0; | |
9dae56ea A |
365 | |
366 | if (structure->transitionCount() > s_maxTransitionLength) { | |
93a37866 | 367 | Structure* transition = toCacheableDictionaryTransition(vm, structure); |
ba379fdc | 368 | ASSERT(structure != transition); |
93a37866 | 369 | offset = transition->putSpecificValue(vm, propertyName, attributes, specificValue); |
14957cd0 | 370 | return transition; |
9dae56ea | 371 | } |
6fe7ccc8 | 372 | |
93a37866 | 373 | Structure* transition = create(vm, structure); |
ba379fdc | 374 | |
93a37866 A |
375 | transition->m_cachedPrototypeChain.setMayBeNull(vm, transition, structure->m_cachedPrototypeChain.get()); |
376 | transition->setPreviousID(vm, transition, structure); | |
377 | transition->m_nameInPrevious = propertyName.uid(); | |
9dae56ea | 378 | transition->m_attributesInPrevious = attributes; |
93a37866 A |
379 | transition->m_specificValueInPrevious.setMayBeNull(vm, transition, specificValue); |
380 | transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm, transition)); | |
381 | transition->m_offset = structure->m_offset; | |
9dae56ea | 382 | |
93a37866 | 383 | offset = transition->putSpecificValue(vm, propertyName, attributes, specificValue); |
9dae56ea | 384 | |
93a37866 A |
385 | checkOffset(transition->m_offset, transition->inlineCapacity()); |
386 | structure->m_transitionTable.add(vm, transition); | |
387 | transition->checkOffsetConsistency(); | |
388 | structure->checkOffsetConsistency(); | |
14957cd0 | 389 | return transition; |
9dae56ea A |
390 | } |
391 | ||
93a37866 | 392 | Structure* Structure::removePropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, PropertyOffset& offset) |
9dae56ea | 393 | { |
ba379fdc | 394 | ASSERT(!structure->isUncacheableDictionary()); |
9dae56ea | 395 | |
93a37866 | 396 | Structure* transition = toUncacheableDictionaryTransition(vm, structure); |
9dae56ea A |
397 | |
398 | offset = transition->remove(propertyName); | |
399 | ||
93a37866 | 400 | transition->checkOffsetConsistency(); |
14957cd0 | 401 | return transition; |
9dae56ea A |
402 | } |
403 | ||
93a37866 | 404 | Structure* Structure::changePrototypeTransition(VM& vm, Structure* structure, JSValue prototype) |
9dae56ea | 405 | { |
93a37866 | 406 | Structure* transition = create(vm, structure); |
9dae56ea | 407 | |
93a37866 | 408 | transition->m_prototype.set(vm, transition, prototype); |
9dae56ea | 409 | |
93a37866 A |
410 | structure->materializePropertyMapIfNecessary(vm); |
411 | transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm, transition)); | |
412 | transition->m_offset = structure->m_offset; | |
6fe7ccc8 A |
413 | transition->pin(); |
414 | ||
93a37866 | 415 | transition->checkOffsetConsistency(); |
14957cd0 | 416 | return transition; |
9dae56ea A |
417 | } |
418 | ||
93a37866 | 419 | Structure* Structure::despecifyFunctionTransition(VM& vm, Structure* structure, PropertyName replaceFunction) |
ba379fdc | 420 | { |
f9bf01c6 | 421 | ASSERT(structure->m_specificFunctionThrashCount < maxSpecificFunctionThrashCount); |
93a37866 | 422 | Structure* transition = create(vm, structure); |
ba379fdc | 423 | |
14957cd0 | 424 | ++transition->m_specificFunctionThrashCount; |
ba379fdc | 425 | |
93a37866 A |
426 | structure->materializePropertyMapIfNecessary(vm); |
427 | transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm, transition)); | |
428 | transition->m_offset = structure->m_offset; | |
6fe7ccc8 | 429 | transition->pin(); |
ba379fdc | 430 | |
f9bf01c6 | 431 | if (transition->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) |
93a37866 | 432 | transition->despecifyAllFunctions(vm); |
f9bf01c6 | 433 | else { |
93a37866 | 434 | bool removed = transition->despecifyFunction(vm, replaceFunction); |
f9bf01c6 A |
435 | ASSERT_UNUSED(removed, removed); |
436 | } | |
6fe7ccc8 | 437 | |
93a37866 | 438 | transition->checkOffsetConsistency(); |
14957cd0 | 439 | return transition; |
ba379fdc A |
440 | } |
441 | ||
93a37866 | 442 | Structure* Structure::attributeChangeTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes) |
9dae56ea | 443 | { |
6fe7ccc8 | 444 | if (!structure->isUncacheableDictionary()) { |
93a37866 | 445 | Structure* transition = create(vm, structure); |
9dae56ea | 446 | |
93a37866 A |
447 | structure->materializePropertyMapIfNecessary(vm); |
448 | transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm, transition)); | |
449 | transition->m_offset = structure->m_offset; | |
6fe7ccc8 A |
450 | transition->pin(); |
451 | ||
452 | structure = transition; | |
453 | } | |
454 | ||
93a37866 A |
455 | ASSERT(structure->propertyTable()); |
456 | PropertyMapEntry* entry = structure->propertyTable()->find(propertyName.uid()).first; | |
6fe7ccc8 A |
457 | ASSERT(entry); |
458 | entry->attributes = attributes; | |
459 | ||
93a37866 | 460 | structure->checkOffsetConsistency(); |
6fe7ccc8 | 461 | return structure; |
9dae56ea A |
462 | } |
463 | ||
93a37866 | 464 | Structure* Structure::toDictionaryTransition(VM& vm, Structure* structure, DictionaryKind kind) |
9dae56ea | 465 | { |
ba379fdc A |
466 | ASSERT(!structure->isUncacheableDictionary()); |
467 | ||
93a37866 | 468 | Structure* transition = create(vm, structure); |
14957cd0 | 469 | |
93a37866 A |
470 | structure->materializePropertyMapIfNecessary(vm); |
471 | transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm, transition)); | |
472 | transition->m_offset = structure->m_offset; | |
ba379fdc | 473 | transition->m_dictionaryKind = kind; |
6fe7ccc8 A |
474 | transition->pin(); |
475 | ||
93a37866 | 476 | transition->checkOffsetConsistency(); |
14957cd0 A |
477 | return transition; |
478 | } | |
479 | ||
93a37866 | 480 | Structure* Structure::toCacheableDictionaryTransition(VM& vm, Structure* structure) |
14957cd0 | 481 | { |
93a37866 | 482 | return toDictionaryTransition(vm, structure, CachedDictionaryKind); |
14957cd0 A |
483 | } |
484 | ||
93a37866 | 485 | Structure* Structure::toUncacheableDictionaryTransition(VM& vm, Structure* structure) |
14957cd0 | 486 | { |
93a37866 | 487 | return toDictionaryTransition(vm, structure, UncachedDictionaryKind); |
14957cd0 A |
488 | } |
489 | ||
490 | // In future we may want to cache this transition. | |
93a37866 | 491 | Structure* Structure::sealTransition(VM& vm, Structure* structure) |
14957cd0 | 492 | { |
93a37866 | 493 | Structure* transition = preventExtensionsTransition(vm, structure); |
14957cd0 | 494 | |
93a37866 A |
495 | if (transition->propertyTable()) { |
496 | PropertyTable::iterator end = transition->propertyTable()->end(); | |
497 | for (PropertyTable::iterator iter = transition->propertyTable()->begin(); iter != end; ++iter) | |
14957cd0 A |
498 | iter->attributes |= DontDelete; |
499 | } | |
500 | ||
93a37866 | 501 | transition->checkOffsetConsistency(); |
14957cd0 A |
502 | return transition; |
503 | } | |
504 | ||
505 | // In future we may want to cache this transition. | |
93a37866 | 506 | Structure* Structure::freezeTransition(VM& vm, Structure* structure) |
14957cd0 | 507 | { |
93a37866 | 508 | Structure* transition = preventExtensionsTransition(vm, structure); |
14957cd0 | 509 | |
93a37866 A |
510 | if (transition->propertyTable()) { |
511 | PropertyTable::iterator iter = transition->propertyTable()->begin(); | |
512 | PropertyTable::iterator end = transition->propertyTable()->end(); | |
6fe7ccc8 A |
513 | if (iter != end) |
514 | transition->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true; | |
515 | for (; iter != end; ++iter) | |
516 | iter->attributes |= iter->attributes & Accessor ? DontDelete : (DontDelete | ReadOnly); | |
14957cd0 A |
517 | } |
518 | ||
93a37866 | 519 | transition->checkOffsetConsistency(); |
14957cd0 A |
520 | return transition; |
521 | } | |
522 | ||
523 | // In future we may want to cache this transition. | |
93a37866 | 524 | Structure* Structure::preventExtensionsTransition(VM& vm, Structure* structure) |
14957cd0 | 525 | { |
93a37866 | 526 | Structure* transition = create(vm, structure); |
14957cd0 A |
527 | |
528 | // Don't set m_offset, as one can not transition to this. | |
529 | ||
93a37866 A |
530 | structure->materializePropertyMapIfNecessary(vm); |
531 | transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm, transition)); | |
532 | transition->m_offset = structure->m_offset; | |
14957cd0 | 533 | transition->m_preventExtensions = true; |
6fe7ccc8 | 534 | transition->pin(); |
14957cd0 | 535 | |
93a37866 A |
536 | transition->checkOffsetConsistency(); |
537 | return transition; | |
538 | } | |
539 | ||
540 | PropertyTable* Structure::takePropertyTableOrCloneIfPinned(VM& vm, Structure* owner) | |
541 | { | |
542 | materializePropertyMapIfNecessaryForPinning(vm); | |
543 | if (m_isPinnedPropertyTable) | |
544 | return propertyTable()->copy(vm, owner, propertyTable()->size() + 1); | |
545 | PropertyTable* takenPropertyTable = propertyTable().get(); | |
546 | propertyTable().clear(); | |
547 | return takenPropertyTable; | |
548 | } | |
549 | ||
550 | Structure* Structure::nonPropertyTransition(VM& vm, Structure* structure, NonPropertyTransition transitionKind) | |
551 | { | |
552 | unsigned attributes = toAttributes(transitionKind); | |
553 | IndexingType indexingType = newIndexingType(structure->indexingTypeIncludingHistory(), transitionKind); | |
554 | ||
555 | if (JSGlobalObject* globalObject = structure->m_globalObject.get()) { | |
556 | if (globalObject->isOriginalArrayStructure(structure)) { | |
557 | Structure* result = globalObject->originalArrayStructureForIndexingType(indexingType); | |
558 | if (result->indexingTypeIncludingHistory() == indexingType) { | |
559 | structure->notifyTransitionFromThisStructure(); | |
560 | return result; | |
561 | } | |
562 | } | |
563 | } | |
564 | ||
565 | if (Structure* existingTransition = structure->m_transitionTable.get(0, attributes)) { | |
566 | ASSERT(existingTransition->m_attributesInPrevious == attributes); | |
567 | ASSERT(existingTransition->indexingTypeIncludingHistory() == indexingType); | |
568 | return existingTransition; | |
569 | } | |
570 | ||
571 | Structure* transition = create(vm, structure); | |
572 | transition->setPreviousID(vm, transition, structure); | |
573 | transition->m_attributesInPrevious = attributes; | |
574 | transition->m_indexingType = indexingType; | |
575 | transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm, transition)); | |
576 | transition->m_offset = structure->m_offset; | |
577 | checkOffset(transition->m_offset, transition->inlineCapacity()); | |
578 | ||
579 | structure->m_transitionTable.add(vm, transition); | |
580 | transition->checkOffsetConsistency(); | |
14957cd0 | 581 | return transition; |
9dae56ea A |
582 | } |
583 | ||
14957cd0 | 584 | // In future we may want to cache this property. |
93a37866 | 585 | bool Structure::isSealed(VM& vm) |
9dae56ea | 586 | { |
14957cd0 A |
587 | if (isExtensible()) |
588 | return false; | |
589 | ||
93a37866 A |
590 | materializePropertyMapIfNecessary(vm); |
591 | if (!propertyTable()) | |
14957cd0 A |
592 | return true; |
593 | ||
93a37866 A |
594 | PropertyTable::iterator end = propertyTable()->end(); |
595 | for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { | |
14957cd0 A |
596 | if ((iter->attributes & DontDelete) != DontDelete) |
597 | return false; | |
598 | } | |
599 | return true; | |
ba379fdc | 600 | } |
9dae56ea | 601 | |
14957cd0 | 602 | // In future we may want to cache this property. |
93a37866 | 603 | bool Structure::isFrozen(VM& vm) |
ba379fdc | 604 | { |
14957cd0 A |
605 | if (isExtensible()) |
606 | return false; | |
607 | ||
93a37866 A |
608 | materializePropertyMapIfNecessary(vm); |
609 | if (!propertyTable()) | |
14957cd0 A |
610 | return true; |
611 | ||
93a37866 A |
612 | PropertyTable::iterator end = propertyTable()->end(); |
613 | for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { | |
6fe7ccc8 A |
614 | if (!(iter->attributes & DontDelete)) |
615 | return false; | |
616 | if (!(iter->attributes & (ReadOnly | Accessor))) | |
14957cd0 A |
617 | return false; |
618 | } | |
619 | return true; | |
ba379fdc | 620 | } |
9dae56ea | 621 | |
93a37866 | 622 | Structure* Structure::flattenDictionaryStructure(VM& vm, JSObject* object) |
ba379fdc | 623 | { |
93a37866 | 624 | checkOffsetConsistency(); |
ba379fdc A |
625 | ASSERT(isDictionary()); |
626 | if (isUncacheableDictionary()) { | |
93a37866 A |
627 | ASSERT(propertyTable()); |
628 | ||
629 | size_t propertyCount = propertyTable()->size(); | |
ba379fdc | 630 | |
93a37866 | 631 | // Holds our values compacted by insertion order. |
14957cd0 A |
632 | Vector<JSValue> values(propertyCount); |
633 | ||
93a37866 | 634 | // Copies out our values from their hashed locations, compacting property table offsets as we go. |
14957cd0 | 635 | unsigned i = 0; |
93a37866 A |
636 | PropertyTable::iterator end = propertyTable()->end(); |
637 | m_offset = invalidOffset; | |
638 | for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter, ++i) { | |
639 | values[i] = object->getDirect(iter->offset); | |
640 | m_offset = iter->offset = offsetForPropertyNumber(i, m_inlineCapacity); | |
ba379fdc A |
641 | } |
642 | ||
93a37866 | 643 | // Copies in our values to their compacted locations. |
ba379fdc | 644 | for (unsigned i = 0; i < propertyCount; i++) |
93a37866 | 645 | object->putDirect(vm, offsetForPropertyNumber(i, m_inlineCapacity), values[i]); |
ba379fdc | 646 | |
93a37866 A |
647 | propertyTable()->clearDeletedOffsets(); |
648 | checkOffsetConsistency(); | |
ba379fdc | 649 | } |
9dae56ea | 650 | |
ba379fdc A |
651 | m_dictionaryKind = NoneDictionaryKind; |
652 | return this; | |
9dae56ea A |
653 | } |
654 | ||
93a37866 | 655 | PropertyOffset Structure::addPropertyWithoutTransition(VM& vm, PropertyName propertyName, unsigned attributes, JSCell* specificValue) |
9dae56ea | 656 | { |
93a37866 | 657 | ASSERT(!enumerationCache()); |
f9bf01c6 A |
658 | |
659 | if (m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) | |
660 | specificValue = 0; | |
9dae56ea | 661 | |
93a37866 | 662 | materializePropertyMapIfNecessaryForPinning(vm); |
6fe7ccc8 A |
663 | |
664 | pin(); | |
f9bf01c6 | 665 | |
93a37866 | 666 | return putSpecificValue(vm, propertyName, attributes, specificValue); |
9dae56ea A |
667 | } |
668 | ||
93a37866 | 669 | PropertyOffset Structure::removePropertyWithoutTransition(VM& vm, PropertyName propertyName) |
9dae56ea | 670 | { |
ba379fdc | 671 | ASSERT(isUncacheableDictionary()); |
93a37866 | 672 | ASSERT(!enumerationCache()); |
9dae56ea | 673 | |
93a37866 | 674 | materializePropertyMapIfNecessaryForPinning(vm); |
9dae56ea | 675 | |
6fe7ccc8 | 676 | pin(); |
93a37866 | 677 | return remove(propertyName); |
9dae56ea A |
678 | } |
679 | ||
6fe7ccc8 A |
680 | void Structure::pin() |
681 | { | |
93a37866 | 682 | ASSERT(propertyTable()); |
6fe7ccc8 | 683 | m_isPinnedPropertyTable = true; |
93a37866 | 684 | clearPreviousID(); |
6fe7ccc8 A |
685 | m_nameInPrevious.clear(); |
686 | } | |
687 | ||
93a37866 A |
688 | void Structure::allocateRareData(VM& vm) |
689 | { | |
690 | ASSERT(!typeInfo().structureHasRareData()); | |
691 | StructureRareData* rareData = StructureRareData::create(vm, previous()); | |
692 | m_typeInfo = TypeInfo(typeInfo().type(), typeInfo().flags() | StructureHasRareData); | |
693 | m_previousOrRareData.set(vm, this, rareData); | |
694 | } | |
695 | ||
696 | void Structure::cloneRareDataFrom(VM& vm, const Structure* other) | |
697 | { | |
698 | ASSERT(other->typeInfo().structureHasRareData()); | |
699 | StructureRareData* newRareData = StructureRareData::clone(vm, other->rareData()); | |
700 | m_typeInfo = TypeInfo(typeInfo().type(), typeInfo().flags() | StructureHasRareData); | |
701 | m_previousOrRareData.set(vm, this, newRareData); | |
702 | } | |
703 | ||
9dae56ea A |
704 | #if DUMP_PROPERTYMAP_STATS |
705 | ||
9dae56ea A |
706 | struct PropertyMapStatisticsExitLogger { |
707 | ~PropertyMapStatisticsExitLogger(); | |
708 | }; | |
709 | ||
710 | static PropertyMapStatisticsExitLogger logger; | |
711 | ||
712 | PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger() | |
713 | { | |
93a37866 A |
714 | dataLogF("\nJSC::PropertyMap statistics\n\n"); |
715 | dataLogF("%d probes\n", numProbes); | |
716 | dataLogF("%d collisions (%.1f%%)\n", numCollisions, 100.0 * numCollisions / numProbes); | |
717 | dataLogF("%d rehashes\n", numRehashes); | |
718 | dataLogF("%d removes\n", numRemoves); | |
9dae56ea A |
719 | } |
720 | ||
721 | #endif | |
722 | ||
9dae56ea A |
723 | #if !DO_PROPERTYMAP_CONSTENCY_CHECK |
724 | ||
725 | inline void Structure::checkConsistency() | |
726 | { | |
93a37866 | 727 | checkOffsetConsistency(); |
9dae56ea A |
728 | } |
729 | ||
730 | #endif | |
731 | ||
93a37866 | 732 | PropertyTable* Structure::copyPropertyTable(VM& vm, Structure* owner) |
9dae56ea | 733 | { |
93a37866 A |
734 | if (!propertyTable()) |
735 | return 0; | |
736 | return PropertyTable::clone(vm, owner, *propertyTable().get()); | |
9dae56ea A |
737 | } |
738 | ||
93a37866 | 739 | PropertyTable* Structure::copyPropertyTableForPinning(VM& vm, Structure* owner) |
6fe7ccc8 | 740 | { |
93a37866 A |
741 | if (propertyTable()) |
742 | return PropertyTable::clone(vm, owner, *propertyTable().get()); | |
743 | return PropertyTable::create(vm, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity)); | |
6fe7ccc8 A |
744 | } |
745 | ||
93a37866 | 746 | PropertyOffset Structure::get(VM& vm, PropertyName propertyName, unsigned& attributes, JSCell*& specificValue) |
9dae56ea | 747 | { |
93a37866 A |
748 | ASSERT(structure()->classInfo() == &s_info); |
749 | ||
750 | materializePropertyMapIfNecessary(vm); | |
751 | if (!propertyTable()) | |
752 | return invalidOffset; | |
9dae56ea | 753 | |
93a37866 | 754 | PropertyMapEntry* entry = propertyTable()->find(propertyName.uid()).first; |
14957cd0 | 755 | if (!entry) |
93a37866 | 756 | return invalidOffset; |
9dae56ea | 757 | |
14957cd0 A |
758 | attributes = entry->attributes; |
759 | specificValue = entry->specificValue.get(); | |
14957cd0 | 760 | return entry->offset; |
9dae56ea A |
761 | } |
762 | ||
93a37866 | 763 | bool Structure::despecifyFunction(VM& vm, PropertyName propertyName) |
ba379fdc | 764 | { |
93a37866 A |
765 | materializePropertyMapIfNecessary(vm); |
766 | if (!propertyTable()) | |
ba379fdc A |
767 | return false; |
768 | ||
93a37866 | 769 | PropertyMapEntry* entry = propertyTable()->find(propertyName.uid()).first; |
14957cd0 | 770 | if (!entry) |
ba379fdc A |
771 | return false; |
772 | ||
14957cd0 A |
773 | ASSERT(entry->specificValue); |
774 | entry->specificValue.clear(); | |
775 | return true; | |
ba379fdc A |
776 | } |
777 | ||
93a37866 | 778 | void Structure::despecifyAllFunctions(VM& vm) |
f9bf01c6 | 779 | { |
93a37866 A |
780 | materializePropertyMapIfNecessary(vm); |
781 | if (!propertyTable()) | |
f9bf01c6 | 782 | return; |
14957cd0 | 783 | |
93a37866 A |
784 | PropertyTable::iterator end = propertyTable()->end(); |
785 | for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) | |
14957cd0 | 786 | iter->specificValue.clear(); |
f9bf01c6 A |
787 | } |
788 | ||
93a37866 | 789 | PropertyOffset Structure::putSpecificValue(VM& vm, PropertyName propertyName, unsigned attributes, JSCell* specificValue) |
9dae56ea | 790 | { |
93a37866 | 791 | ASSERT(!JSC::isValidOffset(get(vm, propertyName))); |
9dae56ea A |
792 | |
793 | checkConsistency(); | |
f9bf01c6 A |
794 | if (attributes & DontEnum) |
795 | m_hasNonEnumerableProperties = true; | |
796 | ||
93a37866 | 797 | StringImpl* rep = propertyName.uid(); |
9dae56ea | 798 | |
93a37866 A |
799 | if (!propertyTable()) |
800 | createPropertyMap(vm); | |
14957cd0 | 801 | |
93a37866 | 802 | PropertyOffset newOffset = propertyTable()->nextOffset(m_inlineCapacity); |
9dae56ea | 803 | |
93a37866 A |
804 | propertyTable()->add(PropertyMapEntry(vm, this, rep, newOffset, attributes, specificValue), m_offset, PropertyTable::PropertyOffsetMayChange); |
805 | ||
9dae56ea A |
806 | checkConsistency(); |
807 | return newOffset; | |
808 | } | |
809 | ||
93a37866 | 810 | PropertyOffset Structure::remove(PropertyName propertyName) |
9dae56ea | 811 | { |
9dae56ea A |
812 | checkConsistency(); |
813 | ||
93a37866 | 814 | StringImpl* rep = propertyName.uid(); |
9dae56ea | 815 | |
93a37866 A |
816 | if (!propertyTable()) |
817 | return invalidOffset; | |
9dae56ea | 818 | |
93a37866 | 819 | PropertyTable::find_iterator position = propertyTable()->find(rep); |
14957cd0 | 820 | if (!position.first) |
93a37866 | 821 | return invalidOffset; |
9dae56ea | 822 | |
93a37866 | 823 | PropertyOffset offset = position.first->offset; |
9dae56ea | 824 | |
93a37866 A |
825 | propertyTable()->remove(position); |
826 | propertyTable()->addDeletedOffset(offset); | |
9dae56ea A |
827 | |
828 | checkConsistency(); | |
829 | return offset; | |
830 | } | |
831 | ||
93a37866 | 832 | void Structure::createPropertyMap(VM& vm, unsigned capacity) |
9dae56ea | 833 | { |
93a37866 | 834 | ASSERT(!propertyTable()); |
9dae56ea A |
835 | |
836 | checkConsistency(); | |
93a37866 | 837 | propertyTable().set(vm, this, PropertyTable::create(vm, capacity)); |
9dae56ea A |
838 | } |
839 | ||
93a37866 | 840 | void Structure::getPropertyNamesFromStructure(VM& vm, PropertyNameArray& propertyNames, EnumerationMode mode) |
9dae56ea | 841 | { |
93a37866 A |
842 | materializePropertyMapIfNecessary(vm); |
843 | if (!propertyTable()) | |
14957cd0 | 844 | return; |
9dae56ea | 845 | |
14957cd0 | 846 | bool knownUnique = !propertyNames.size(); |
9dae56ea | 847 | |
93a37866 A |
848 | PropertyTable::iterator end = propertyTable()->end(); |
849 | for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { | |
14957cd0 | 850 | ASSERT(m_hasNonEnumerableProperties || !(iter->attributes & DontEnum)); |
93a37866 | 851 | if (iter->key->isIdentifier() && (!(iter->attributes & DontEnum) || mode == IncludeDontEnumProperties)) { |
14957cd0 A |
852 | if (knownUnique) |
853 | propertyNames.addKnownUnique(iter->key); | |
854 | else | |
855 | propertyNames.add(iter->key); | |
9dae56ea A |
856 | } |
857 | } | |
9dae56ea A |
858 | } |
859 | ||
93a37866 A |
860 | JSValue Structure::prototypeForLookup(CodeBlock* codeBlock) const |
861 | { | |
862 | return prototypeForLookup(codeBlock->globalObject()); | |
863 | } | |
864 | ||
6fe7ccc8 A |
865 | void Structure::visitChildren(JSCell* cell, SlotVisitor& visitor) |
866 | { | |
867 | Structure* thisObject = jsCast<Structure*>(cell); | |
868 | ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); | |
869 | ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); | |
93a37866 | 870 | |
6fe7ccc8 | 871 | JSCell::visitChildren(thisObject, visitor); |
93a37866 | 872 | visitor.append(&thisObject->m_globalObject); |
6fe7ccc8 A |
873 | if (!thisObject->isObject()) |
874 | thisObject->m_cachedPrototypeChain.clear(); | |
875 | else { | |
93a37866 A |
876 | visitor.append(&thisObject->m_prototype); |
877 | visitor.append(&thisObject->m_cachedPrototypeChain); | |
6fe7ccc8 | 878 | } |
93a37866 A |
879 | visitor.append(&thisObject->m_previousOrRareData); |
880 | visitor.append(&thisObject->m_specificValueInPrevious); | |
881 | ||
882 | if (thisObject->m_isPinnedPropertyTable) { | |
883 | ASSERT(thisObject->m_propertyTableUnsafe); | |
884 | visitor.append(&thisObject->m_propertyTableUnsafe); | |
885 | } else if (thisObject->m_propertyTableUnsafe) | |
886 | thisObject->m_propertyTableUnsafe.clear(); | |
887 | } | |
888 | ||
889 | bool Structure::prototypeChainMayInterceptStoreTo(VM& vm, PropertyName propertyName) | |
890 | { | |
891 | unsigned i = propertyName.asIndex(); | |
892 | if (i != PropertyName::NotAnIndex) | |
893 | return anyObjectInChainMayInterceptIndexedAccesses(); | |
894 | ||
895 | for (Structure* current = this; ;) { | |
896 | JSValue prototype = current->storedPrototype(); | |
897 | if (prototype.isNull()) | |
898 | return false; | |
899 | ||
900 | current = prototype.asCell()->structure(); | |
901 | ||
902 | unsigned attributes; | |
903 | JSCell* specificValue; | |
904 | PropertyOffset offset = current->get(vm, propertyName, attributes, specificValue); | |
905 | if (!JSC::isValidOffset(offset)) | |
906 | continue; | |
907 | ||
908 | if (attributes & (ReadOnly | Accessor)) | |
909 | return true; | |
910 | ||
911 | return false; | |
9dae56ea A |
912 | } |
913 | } | |
914 | ||
9dae56ea A |
915 | #if DO_PROPERTYMAP_CONSTENCY_CHECK |
916 | ||
14957cd0 | 917 | void PropertyTable::checkConsistency() |
9dae56ea | 918 | { |
93a37866 | 919 | checkOffsetConsistency(); |
14957cd0 A |
920 | ASSERT(m_indexSize >= PropertyTable::MinimumTableSize); |
921 | ASSERT(m_indexMask); | |
922 | ASSERT(m_indexSize == m_indexMask + 1); | |
923 | ASSERT(!(m_indexSize & m_indexMask)); | |
9dae56ea | 924 | |
14957cd0 A |
925 | ASSERT(m_keyCount <= m_indexSize / 2); |
926 | ASSERT(m_keyCount + m_deletedCount <= m_indexSize / 2); | |
927 | ASSERT(m_deletedCount <= m_indexSize / 4); | |
9dae56ea A |
928 | |
929 | unsigned indexCount = 0; | |
930 | unsigned deletedIndexCount = 0; | |
14957cd0 A |
931 | for (unsigned a = 0; a != m_indexSize; ++a) { |
932 | unsigned entryIndex = m_index[a]; | |
933 | if (entryIndex == PropertyTable::EmptyEntryIndex) | |
9dae56ea | 934 | continue; |
14957cd0 | 935 | if (entryIndex == deletedEntryIndex()) { |
9dae56ea A |
936 | ++deletedIndexCount; |
937 | continue; | |
938 | } | |
14957cd0 A |
939 | ASSERT(entryIndex < deletedEntryIndex()); |
940 | ASSERT(entryIndex - 1 <= usedCount()); | |
9dae56ea A |
941 | ++indexCount; |
942 | ||
14957cd0 A |
943 | for (unsigned b = a + 1; b != m_indexSize; ++b) |
944 | ASSERT(m_index[b] != entryIndex); | |
9dae56ea | 945 | } |
14957cd0 A |
946 | ASSERT(indexCount == m_keyCount); |
947 | ASSERT(deletedIndexCount == m_deletedCount); | |
9dae56ea | 948 | |
14957cd0 | 949 | ASSERT(!table()[deletedEntryIndex() - 1].key); |
9dae56ea A |
950 | |
951 | unsigned nonEmptyEntryCount = 0; | |
14957cd0 A |
952 | for (unsigned c = 0; c < usedCount(); ++c) { |
953 | StringImpl* rep = table()[c].key; | |
954 | if (rep == PROPERTY_MAP_DELETED_ENTRY_KEY) | |
9dae56ea A |
955 | continue; |
956 | ++nonEmptyEntryCount; | |
f9bf01c6 | 957 | unsigned i = rep->existingHash(); |
9dae56ea A |
958 | unsigned k = 0; |
959 | unsigned entryIndex; | |
960 | while (1) { | |
14957cd0 A |
961 | entryIndex = m_index[i & m_indexMask]; |
962 | ASSERT(entryIndex != PropertyTable::EmptyEntryIndex); | |
963 | if (rep == table()[entryIndex - 1].key) | |
9dae56ea A |
964 | break; |
965 | if (k == 0) | |
f9bf01c6 | 966 | k = 1 | doubleHash(rep->existingHash()); |
9dae56ea A |
967 | i += k; |
968 | } | |
969 | ASSERT(entryIndex == c + 1); | |
970 | } | |
971 | ||
14957cd0 A |
972 | ASSERT(nonEmptyEntryCount == m_keyCount); |
973 | } | |
974 | ||
975 | void Structure::checkConsistency() | |
976 | { | |
93a37866 | 977 | if (!propertyTable()) |
14957cd0 A |
978 | return; |
979 | ||
980 | if (!m_hasNonEnumerableProperties) { | |
93a37866 A |
981 | PropertyTable::iterator end = propertyTable()->end(); |
982 | for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { | |
14957cd0 | 983 | ASSERT(!(iter->attributes & DontEnum)); |
14957cd0 A |
984 | } |
985 | } | |
986 | ||
93a37866 | 987 | propertyTable()->checkConsistency(); |
9dae56ea A |
988 | } |
989 | ||
990 | #endif // DO_PROPERTYMAP_CONSTENCY_CHECK | |
991 | ||
992 | } // namespace JSC |