]>
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 | * | |
81345200 | 13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
9dae56ea A |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
81345200 | 16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
9dae56ea A |
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" |
81345200 A |
30 | #include "DumpContext.h" |
31 | #include "JSCInlines.h" | |
9dae56ea | 32 | #include "JSObject.h" |
f9bf01c6 A |
33 | #include "JSPropertyNameIterator.h" |
34 | #include "Lookup.h" | |
81345200 | 35 | #include "PropertyMapHashTable.h" |
9dae56ea A |
36 | #include "PropertyNameArray.h" |
37 | #include "StructureChain.h" | |
93a37866 | 38 | #include "StructureRareDataInlines.h" |
81345200 A |
39 | #include <wtf/CommaPrinter.h> |
40 | #include <wtf/ProcessID.h> | |
9dae56ea A |
41 | #include <wtf/RefCountedLeakCounter.h> |
42 | #include <wtf/RefPtr.h> | |
9dae56ea | 43 | #include <wtf/Threading.h> |
9dae56ea A |
44 | |
45 | #define DUMP_STRUCTURE_ID_STATISTICS 0 | |
46 | ||
47 | #ifndef NDEBUG | |
48 | #define DO_PROPERTYMAP_CONSTENCY_CHECK 0 | |
49 | #else | |
50 | #define DO_PROPERTYMAP_CONSTENCY_CHECK 0 | |
51 | #endif | |
52 | ||
53 | using namespace std; | |
54 | using namespace WTF; | |
55 | ||
14957cd0 | 56 | namespace JSC { |
9dae56ea A |
57 | |
58 | #if DUMP_STRUCTURE_ID_STATISTICS | |
ba379fdc | 59 | static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>); |
9dae56ea A |
60 | #endif |
61 | ||
14957cd0 | 62 | bool StructureTransitionTable::contains(StringImpl* rep, unsigned attributes) const |
4e4e5a6f | 63 | { |
14957cd0 A |
64 | if (isUsingSingleSlot()) { |
65 | Structure* transition = singleTransition(); | |
66 | return transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes; | |
4e4e5a6f | 67 | } |
81345200 | 68 | return map()->get(std::make_pair(rep, attributes)); |
4e4e5a6f A |
69 | } |
70 | ||
14957cd0 | 71 | inline Structure* StructureTransitionTable::get(StringImpl* rep, unsigned attributes) const |
4e4e5a6f | 72 | { |
14957cd0 | 73 | if (isUsingSingleSlot()) { |
4e4e5a6f | 74 | Structure* transition = singleTransition(); |
14957cd0 | 75 | return (transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes) ? transition : 0; |
4e4e5a6f | 76 | } |
81345200 | 77 | return map()->get(std::make_pair(rep, attributes)); |
4e4e5a6f A |
78 | } |
79 | ||
93a37866 | 80 | inline void StructureTransitionTable::add(VM& vm, Structure* structure) |
4e4e5a6f | 81 | { |
14957cd0 A |
82 | if (isUsingSingleSlot()) { |
83 | Structure* existingTransition = singleTransition(); | |
84 | ||
85 | // This handles the first transition being added. | |
86 | if (!existingTransition) { | |
93a37866 | 87 | setSingleTransition(vm, structure); |
4e4e5a6f A |
88 | return; |
89 | } | |
14957cd0 A |
90 | |
91 | // This handles the second transition being added | |
92 | // (or the first transition being despecified!) | |
93 | setMap(new TransitionMap()); | |
93a37866 | 94 | add(vm, existingTransition); |
4e4e5a6f | 95 | } |
14957cd0 A |
96 | |
97 | // Add the structure to the map. | |
98 | ||
99 | // Newer versions of the STL have an std::make_pair function that takes rvalue references. | |
100 | // 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. | |
101 | // See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details | |
81345200 | 102 | map()->set(std::make_pair(structure->m_nameInPrevious.get(), +structure->m_attributesInPrevious), structure); |
4e4e5a6f A |
103 | } |
104 | ||
9dae56ea A |
105 | void Structure::dumpStatistics() |
106 | { | |
107 | #if DUMP_STRUCTURE_ID_STATISTICS | |
108 | unsigned numberLeaf = 0; | |
109 | unsigned numberUsingSingleSlot = 0; | |
110 | unsigned numberSingletons = 0; | |
111 | unsigned numberWithPropertyMaps = 0; | |
112 | unsigned totalPropertyMapsSize = 0; | |
113 | ||
114 | HashSet<Structure*>::const_iterator end = liveStructureSet.end(); | |
115 | for (HashSet<Structure*>::const_iterator it = liveStructureSet.begin(); it != end; ++it) { | |
116 | Structure* structure = *it; | |
14957cd0 A |
117 | |
118 | switch (structure->m_transitionTable.size()) { | |
119 | case 0: | |
9dae56ea | 120 | ++numberLeaf; |
93a37866 | 121 | if (!structure->previousID()) |
14957cd0 A |
122 | ++numberSingletons; |
123 | break; | |
9dae56ea | 124 | |
14957cd0 A |
125 | case 1: |
126 | ++numberUsingSingleSlot; | |
127 | break; | |
9dae56ea A |
128 | } |
129 | ||
93a37866 | 130 | if (structure->propertyTable()) { |
9dae56ea | 131 | ++numberWithPropertyMaps; |
93a37866 | 132 | totalPropertyMapsSize += structure->propertyTable()->sizeInMemory(); |
9dae56ea A |
133 | } |
134 | } | |
135 | ||
93a37866 A |
136 | dataLogF("Number of live Structures: %d\n", liveStructureSet.size()); |
137 | dataLogF("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot); | |
138 | dataLogF("Number of Structures that are leaf nodes: %d\n", numberLeaf); | |
139 | dataLogF("Number of Structures that singletons: %d\n", numberSingletons); | |
140 | dataLogF("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps); | |
9dae56ea | 141 | |
93a37866 A |
142 | dataLogF("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure))); |
143 | dataLogF("Size of sum of all property maps: %d\n", totalPropertyMapsSize); | |
144 | dataLogF("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size())); | |
9dae56ea | 145 | #else |
93a37866 | 146 | dataLogF("Dumping Structure statistics is not enabled.\n"); |
9dae56ea A |
147 | #endif |
148 | } | |
149 | ||
93a37866 A |
150 | Structure::Structure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType, unsigned inlineCapacity) |
151 | : JSCell(vm, vm.structureStructure.get()) | |
81345200 A |
152 | , m_blob(vm.heap.structureIDTable().allocateID(this), indexingType, typeInfo) |
153 | , m_outOfLineTypeFlags(typeInfo.outOfLineTypeFlags()) | |
93a37866 A |
154 | , m_globalObject(vm, this, globalObject, WriteBarrier<JSGlobalObject>::MayBeNull) |
155 | , m_prototype(vm, this, prototype) | |
14957cd0 | 156 | , m_classInfo(classInfo) |
81345200 | 157 | , m_transitionWatchpointSet(IsWatched) |
93a37866 | 158 | , m_offset(invalidOffset) |
93a37866 | 159 | , m_inlineCapacity(inlineCapacity) |
ba379fdc | 160 | , m_dictionaryKind(NoneDictionaryKind) |
81345200 | 161 | , m_hasBeenFlattenedBefore(false) |
9dae56ea | 162 | , m_isPinnedPropertyTable(false) |
81345200 A |
163 | , m_hasGetterSetterProperties(classInfo->hasStaticSetterOrReadonlyProperties(vm)) |
164 | , m_hasCustomGetterSetterProperties(false) | |
165 | , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(classInfo->hasStaticSetterOrReadonlyProperties(vm)) | |
14957cd0 | 166 | , m_hasNonEnumerableProperties(false) |
9dae56ea | 167 | , m_attributesInPrevious(0) |
f9bf01c6 | 168 | , m_specificFunctionThrashCount(0) |
14957cd0 A |
169 | , m_preventExtensions(false) |
170 | , m_didTransition(false) | |
6fe7ccc8 | 171 | , m_staticFunctionReified(false) |
81345200 | 172 | , m_hasRareData(false) |
9dae56ea | 173 | { |
93a37866 A |
174 | ASSERT(inlineCapacity <= JSFinalObject::maxInlineCapacity()); |
175 | ASSERT(static_cast<PropertyOffset>(inlineCapacity) < firstOutOfLineOffset); | |
81345200 A |
176 | ASSERT(!m_hasRareData); |
177 | ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm)); | |
178 | ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm)); | |
9dae56ea A |
179 | } |
180 | ||
6fe7ccc8 | 181 | const ClassInfo Structure::s_info = { "Structure", 0, 0, 0, CREATE_METHOD_TABLE(Structure) }; |
9dae56ea | 182 | |
93a37866 | 183 | Structure::Structure(VM& vm) |
6fe7ccc8 | 184 | : JSCell(CreatingEarlyCell) |
93a37866 | 185 | , m_prototype(vm, this, jsNull()) |
81345200 A |
186 | , m_classInfo(info()) |
187 | , m_transitionWatchpointSet(IsWatched) | |
93a37866 | 188 | , m_offset(invalidOffset) |
93a37866 | 189 | , m_inlineCapacity(0) |
14957cd0 | 190 | , m_dictionaryKind(NoneDictionaryKind) |
81345200 | 191 | , m_hasBeenFlattenedBefore(false) |
14957cd0 | 192 | , m_isPinnedPropertyTable(false) |
81345200 A |
193 | , m_hasGetterSetterProperties(m_classInfo->hasStaticSetterOrReadonlyProperties(vm)) |
194 | , m_hasCustomGetterSetterProperties(false) | |
195 | , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(m_classInfo->hasStaticSetterOrReadonlyProperties(vm)) | |
14957cd0 A |
196 | , m_hasNonEnumerableProperties(false) |
197 | , m_attributesInPrevious(0) | |
198 | , m_specificFunctionThrashCount(0) | |
14957cd0 A |
199 | , m_preventExtensions(false) |
200 | , m_didTransition(false) | |
6fe7ccc8 | 201 | , m_staticFunctionReified(false) |
81345200 | 202 | , m_hasRareData(false) |
9dae56ea | 203 | { |
81345200 A |
204 | TypeInfo typeInfo = TypeInfo(CompoundType, OverridesVisitChildren | StructureIsImmortal); |
205 | m_blob = StructureIDBlob(vm.heap.structureIDTable().allocateID(this), 0, typeInfo); | |
206 | m_outOfLineTypeFlags = typeInfo.outOfLineTypeFlags(); | |
207 | ||
208 | ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm)); | |
209 | ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm)); | |
9dae56ea A |
210 | } |
211 | ||
81345200 | 212 | Structure::Structure(VM& vm, Structure* previous) |
93a37866 A |
213 | : JSCell(vm, vm.structureStructure.get()) |
214 | , m_prototype(vm, this, previous->storedPrototype()) | |
14957cd0 | 215 | , m_classInfo(previous->m_classInfo) |
81345200 | 216 | , m_transitionWatchpointSet(IsWatched) |
93a37866 | 217 | , m_offset(invalidOffset) |
93a37866 | 218 | , m_inlineCapacity(previous->m_inlineCapacity) |
6fe7ccc8 | 219 | , m_dictionaryKind(previous->m_dictionaryKind) |
81345200 | 220 | , m_hasBeenFlattenedBefore(previous->m_hasBeenFlattenedBefore) |
14957cd0 A |
221 | , m_isPinnedPropertyTable(false) |
222 | , m_hasGetterSetterProperties(previous->m_hasGetterSetterProperties) | |
81345200 | 223 | , m_hasCustomGetterSetterProperties(previous->m_hasCustomGetterSetterProperties) |
6fe7ccc8 | 224 | , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(previous->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto) |
14957cd0 A |
225 | , m_hasNonEnumerableProperties(previous->m_hasNonEnumerableProperties) |
226 | , m_attributesInPrevious(0) | |
227 | , m_specificFunctionThrashCount(previous->m_specificFunctionThrashCount) | |
14957cd0 A |
228 | , m_preventExtensions(previous->m_preventExtensions) |
229 | , m_didTransition(true) | |
6fe7ccc8 | 230 | , m_staticFunctionReified(previous->m_staticFunctionReified) |
81345200 | 231 | , m_hasRareData(false) |
9dae56ea | 232 | { |
81345200 A |
233 | TypeInfo typeInfo = previous->typeInfo(); |
234 | m_blob = StructureIDBlob(vm.heap.structureIDTable().allocateID(this), previous->indexingTypeIncludingHistory(), typeInfo); | |
235 | m_outOfLineTypeFlags = typeInfo.outOfLineTypeFlags(); | |
236 | ||
237 | ASSERT(!previous->typeInfo().structureIsImmortal()); | |
238 | if (previous->m_hasRareData && previous->rareData()->needsCloning()) | |
93a37866 | 239 | cloneRareDataFrom(vm, previous); |
81345200 | 240 | setPreviousID(vm, previous); |
93a37866 A |
241 | |
242 | previous->notifyTransitionFromThisStructure(); | |
6fe7ccc8 | 243 | if (previous->m_globalObject) |
93a37866 | 244 | m_globalObject.set(vm, this, previous->m_globalObject.get()); |
81345200 A |
245 | ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm)); |
246 | ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm)); | |
247 | } | |
248 | ||
249 | Structure::~Structure() | |
250 | { | |
251 | if (typeInfo().structureIsImmortal()) | |
252 | return; | |
253 | Heap::heap(this)->structureIDTable().deallocateID(this, m_blob.structureID()); | |
9dae56ea A |
254 | } |
255 | ||
6fe7ccc8 | 256 | void Structure::destroy(JSCell* cell) |
9dae56ea | 257 | { |
93a37866 | 258 | static_cast<Structure*>(cell)->Structure::~Structure(); |
9dae56ea A |
259 | } |
260 | ||
81345200 | 261 | void Structure::findStructuresAndMapForMaterialization(Vector<Structure*, 8>& structures, Structure*& structure, PropertyTable*& table) |
9dae56ea | 262 | { |
81345200 A |
263 | ASSERT(structures.isEmpty()); |
264 | table = 0; | |
9dae56ea | 265 | |
81345200 A |
266 | for (structure = this; structure; structure = structure->previousID()) { |
267 | structure->m_lock.lock(); | |
268 | ||
269 | table = structure->propertyTable().get(); | |
270 | if (table) { | |
271 | // Leave the structure locked, so that the caller can do things to it atomically | |
272 | // before it loses its property table. | |
273 | return; | |
9dae56ea | 274 | } |
81345200 | 275 | |
9dae56ea | 276 | structures.append(structure); |
81345200 | 277 | structure->m_lock.unlock(); |
9dae56ea | 278 | } |
81345200 A |
279 | |
280 | ASSERT(!structure); | |
281 | ASSERT(!table); | |
282 | } | |
9dae56ea | 283 | |
81345200 A |
284 | void Structure::materializePropertyMap(VM& vm) |
285 | { | |
286 | ASSERT(structure()->classInfo() == info()); | |
287 | ASSERT(!propertyTable()); | |
9dae56ea | 288 | |
81345200 A |
289 | Vector<Structure*, 8> structures; |
290 | Structure* structure; | |
291 | PropertyTable* table; | |
292 | ||
293 | findStructuresAndMapForMaterialization(structures, structure, table); | |
294 | ||
295 | if (table) { | |
296 | table = table->copy(vm, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity)); | |
297 | structure->m_lock.unlock(); | |
298 | } | |
299 | ||
300 | // Must hold the lock on this structure, since we will be modifying this structure's | |
301 | // property map. We don't want getConcurrently() to see the property map in a half-baked | |
302 | // state. | |
303 | GCSafeConcurrentJITLocker locker(m_lock, vm.heap); | |
304 | if (!table) | |
305 | createPropertyMap(locker, vm, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity)); | |
306 | else | |
307 | propertyTable().set(vm, this, table); | |
308 | ||
309 | for (size_t i = structures.size(); i--;) { | |
9dae56ea | 310 | structure = structures[i]; |
93a37866 A |
311 | if (!structure->m_nameInPrevious) |
312 | continue; | |
313 | PropertyMapEntry entry(vm, this, structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious.get()); | |
314 | propertyTable()->add(entry, m_offset, PropertyTable::PropertyOffsetMustNotChange); | |
9dae56ea | 315 | } |
93a37866 A |
316 | |
317 | checkOffsetConsistency(); | |
9dae56ea A |
318 | } |
319 | ||
93a37866 | 320 | void Structure::despecifyDictionaryFunction(VM& vm, PropertyName propertyName) |
ba379fdc | 321 | { |
93a37866 | 322 | StringImpl* rep = propertyName.uid(); |
ba379fdc | 323 | |
81345200 A |
324 | DeferGC deferGC(vm.heap); |
325 | materializePropertyMapIfNecessary(vm, deferGC); | |
ba379fdc A |
326 | |
327 | ASSERT(isDictionary()); | |
93a37866 | 328 | ASSERT(propertyTable()); |
ba379fdc | 329 | |
81345200 | 330 | PropertyMapEntry* entry = propertyTable()->get(rep); |
14957cd0 A |
331 | ASSERT(entry); |
332 | entry->specificValue.clear(); | |
ba379fdc A |
333 | } |
334 | ||
81345200 | 335 | Structure* Structure::addPropertyTransitionToExistingStructureImpl(Structure* structure, StringImpl* uid, unsigned attributes, JSCell* specificValue, PropertyOffset& offset) |
9dae56ea | 336 | { |
ba379fdc | 337 | ASSERT(!structure->isDictionary()); |
6fe7ccc8 | 338 | ASSERT(structure->isObject()); |
9dae56ea | 339 | |
81345200 | 340 | if (Structure* existingTransition = structure->m_transitionTable.get(uid, attributes)) { |
14957cd0 A |
341 | JSCell* specificValueInPrevious = existingTransition->m_specificValueInPrevious.get(); |
342 | if (specificValueInPrevious && specificValueInPrevious != specificValue) | |
343 | return 0; | |
93a37866 | 344 | validateOffset(existingTransition->m_offset, existingTransition->inlineCapacity()); |
6fe7ccc8 | 345 | offset = existingTransition->m_offset; |
f9bf01c6 | 346 | return existingTransition; |
9dae56ea A |
347 | } |
348 | ||
349 | return 0; | |
350 | } | |
351 | ||
81345200 A |
352 | Structure* Structure::addPropertyTransitionToExistingStructure(Structure* structure, PropertyName propertyName, unsigned attributes, JSCell* specificValue, PropertyOffset& offset) |
353 | { | |
354 | ASSERT(!isCompilationThread()); | |
355 | return addPropertyTransitionToExistingStructureImpl(structure, propertyName.uid(), attributes, specificValue, offset); | |
356 | } | |
357 | ||
358 | Structure* Structure::addPropertyTransitionToExistingStructureConcurrently(Structure* structure, StringImpl* uid, unsigned attributes, JSCell* specificValue, PropertyOffset& offset) | |
359 | { | |
360 | ConcurrentJITLocker locker(structure->m_lock); | |
361 | return addPropertyTransitionToExistingStructureImpl(structure, uid, attributes, specificValue, offset); | |
362 | } | |
363 | ||
93a37866 A |
364 | bool Structure::anyObjectInChainMayInterceptIndexedAccesses() const |
365 | { | |
366 | for (const Structure* current = this; ;) { | |
367 | if (current->mayInterceptIndexedAccesses()) | |
368 | return true; | |
369 | ||
370 | JSValue prototype = current->storedPrototype(); | |
371 | if (prototype.isNull()) | |
372 | return false; | |
373 | ||
374 | current = asObject(prototype)->structure(); | |
375 | } | |
376 | } | |
377 | ||
81345200 A |
378 | bool Structure::holesMustForwardToPrototype(VM& vm) const |
379 | { | |
380 | if (this->mayInterceptIndexedAccesses()) | |
381 | return true; | |
382 | ||
383 | JSValue prototype = this->storedPrototype(); | |
384 | if (!prototype.isObject()) | |
385 | return false; | |
386 | JSObject* object = asObject(prototype); | |
387 | ||
388 | while (true) { | |
389 | Structure& structure = *object->structure(vm); | |
390 | if (hasIndexedProperties(object->indexingType()) || structure.mayInterceptIndexedAccesses()) | |
391 | return true; | |
392 | prototype = structure.storedPrototype(); | |
393 | if (!prototype.isObject()) | |
394 | return false; | |
395 | object = asObject(prototype); | |
396 | } | |
397 | ||
398 | RELEASE_ASSERT_NOT_REACHED(); | |
399 | return false; | |
400 | } | |
401 | ||
93a37866 A |
402 | bool Structure::needsSlowPutIndexing() const |
403 | { | |
404 | return anyObjectInChainMayInterceptIndexedAccesses() | |
405 | || globalObject()->isHavingABadTime(); | |
406 | } | |
407 | ||
408 | NonPropertyTransition Structure::suggestedArrayStorageTransition() const | |
409 | { | |
410 | if (needsSlowPutIndexing()) | |
411 | return AllocateSlowPutArrayStorage; | |
412 | ||
413 | return AllocateArrayStorage; | |
414 | } | |
415 | ||
81345200 | 416 | Structure* Structure::addPropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes, JSCell* specificValue, PropertyOffset& offset, PutPropertySlot::Context context) |
9dae56ea | 417 | { |
14957cd0 A |
418 | // If we have a specific function, we may have got to this point if there is |
419 | // already a transition with the correct property name and attributes, but | |
420 | // specialized to a different function. In this case we just want to give up | |
421 | // and despecialize the transition. | |
422 | // In this case we clear the value of specificFunction which will result | |
423 | // in us adding a non-specific transition, and any subsequent lookup in | |
424 | // Structure::addPropertyTransitionToExistingStructure will just use that. | |
93a37866 | 425 | if (specificValue && structure->m_transitionTable.contains(propertyName.uid(), attributes)) |
14957cd0 A |
426 | specificValue = 0; |
427 | ||
ba379fdc | 428 | ASSERT(!structure->isDictionary()); |
6fe7ccc8 | 429 | ASSERT(structure->isObject()); |
ba379fdc | 430 | ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset)); |
f9bf01c6 A |
431 | |
432 | if (structure->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) | |
433 | specificValue = 0; | |
9dae56ea | 434 | |
81345200 A |
435 | int maxTransitionLength; |
436 | if (context == PutPropertySlot::PutById) | |
437 | maxTransitionLength = s_maxTransitionLengthForNonEvalPutById; | |
438 | else | |
439 | maxTransitionLength = s_maxTransitionLength; | |
440 | if (structure->transitionCount() > maxTransitionLength) { | |
93a37866 | 441 | Structure* transition = toCacheableDictionaryTransition(vm, structure); |
ba379fdc | 442 | ASSERT(structure != transition); |
93a37866 | 443 | offset = transition->putSpecificValue(vm, propertyName, attributes, specificValue); |
14957cd0 | 444 | return transition; |
9dae56ea | 445 | } |
6fe7ccc8 | 446 | |
93a37866 | 447 | Structure* transition = create(vm, structure); |
ba379fdc | 448 | |
93a37866 | 449 | transition->m_cachedPrototypeChain.setMayBeNull(vm, transition, structure->m_cachedPrototypeChain.get()); |
93a37866 | 450 | transition->m_nameInPrevious = propertyName.uid(); |
9dae56ea | 451 | transition->m_attributesInPrevious = attributes; |
93a37866 | 452 | transition->m_specificValueInPrevious.setMayBeNull(vm, transition, specificValue); |
81345200 | 453 | transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm)); |
93a37866 | 454 | transition->m_offset = structure->m_offset; |
9dae56ea | 455 | |
93a37866 | 456 | offset = transition->putSpecificValue(vm, propertyName, attributes, specificValue); |
9dae56ea | 457 | |
93a37866 | 458 | checkOffset(transition->m_offset, transition->inlineCapacity()); |
81345200 A |
459 | { |
460 | ConcurrentJITLocker locker(structure->m_lock); | |
461 | structure->m_transitionTable.add(vm, transition); | |
462 | } | |
93a37866 A |
463 | transition->checkOffsetConsistency(); |
464 | structure->checkOffsetConsistency(); | |
14957cd0 | 465 | return transition; |
9dae56ea A |
466 | } |
467 | ||
93a37866 | 468 | Structure* Structure::removePropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, PropertyOffset& offset) |
9dae56ea | 469 | { |
ba379fdc | 470 | ASSERT(!structure->isUncacheableDictionary()); |
9dae56ea | 471 | |
93a37866 | 472 | Structure* transition = toUncacheableDictionaryTransition(vm, structure); |
9dae56ea A |
473 | |
474 | offset = transition->remove(propertyName); | |
475 | ||
93a37866 | 476 | transition->checkOffsetConsistency(); |
14957cd0 | 477 | return transition; |
9dae56ea A |
478 | } |
479 | ||
93a37866 | 480 | Structure* Structure::changePrototypeTransition(VM& vm, Structure* structure, JSValue prototype) |
9dae56ea | 481 | { |
93a37866 | 482 | Structure* transition = create(vm, structure); |
9dae56ea | 483 | |
93a37866 | 484 | transition->m_prototype.set(vm, transition, prototype); |
9dae56ea | 485 | |
81345200 A |
486 | DeferGC deferGC(vm.heap); |
487 | structure->materializePropertyMapIfNecessary(vm, deferGC); | |
488 | transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); | |
93a37866 | 489 | transition->m_offset = structure->m_offset; |
6fe7ccc8 A |
490 | transition->pin(); |
491 | ||
93a37866 | 492 | transition->checkOffsetConsistency(); |
14957cd0 | 493 | return transition; |
9dae56ea A |
494 | } |
495 | ||
93a37866 | 496 | Structure* Structure::despecifyFunctionTransition(VM& vm, Structure* structure, PropertyName replaceFunction) |
ba379fdc | 497 | { |
f9bf01c6 | 498 | ASSERT(structure->m_specificFunctionThrashCount < maxSpecificFunctionThrashCount); |
93a37866 | 499 | Structure* transition = create(vm, structure); |
ba379fdc | 500 | |
14957cd0 | 501 | ++transition->m_specificFunctionThrashCount; |
ba379fdc | 502 | |
81345200 A |
503 | DeferGC deferGC(vm.heap); |
504 | structure->materializePropertyMapIfNecessary(vm, deferGC); | |
505 | transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); | |
93a37866 | 506 | transition->m_offset = structure->m_offset; |
6fe7ccc8 | 507 | transition->pin(); |
ba379fdc | 508 | |
f9bf01c6 | 509 | if (transition->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) |
93a37866 | 510 | transition->despecifyAllFunctions(vm); |
f9bf01c6 | 511 | else { |
93a37866 | 512 | bool removed = transition->despecifyFunction(vm, replaceFunction); |
f9bf01c6 A |
513 | ASSERT_UNUSED(removed, removed); |
514 | } | |
6fe7ccc8 | 515 | |
93a37866 | 516 | transition->checkOffsetConsistency(); |
14957cd0 | 517 | return transition; |
ba379fdc A |
518 | } |
519 | ||
93a37866 | 520 | Structure* Structure::attributeChangeTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes) |
9dae56ea | 521 | { |
81345200 | 522 | DeferGC deferGC(vm.heap); |
6fe7ccc8 | 523 | if (!structure->isUncacheableDictionary()) { |
93a37866 | 524 | Structure* transition = create(vm, structure); |
9dae56ea | 525 | |
81345200 A |
526 | structure->materializePropertyMapIfNecessary(vm, deferGC); |
527 | transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); | |
93a37866 | 528 | transition->m_offset = structure->m_offset; |
6fe7ccc8 A |
529 | transition->pin(); |
530 | ||
531 | structure = transition; | |
532 | } | |
533 | ||
93a37866 | 534 | ASSERT(structure->propertyTable()); |
81345200 | 535 | PropertyMapEntry* entry = structure->propertyTable()->get(propertyName.uid()); |
6fe7ccc8 A |
536 | ASSERT(entry); |
537 | entry->attributes = attributes; | |
538 | ||
93a37866 | 539 | structure->checkOffsetConsistency(); |
6fe7ccc8 | 540 | return structure; |
9dae56ea A |
541 | } |
542 | ||
93a37866 | 543 | Structure* Structure::toDictionaryTransition(VM& vm, Structure* structure, DictionaryKind kind) |
9dae56ea | 544 | { |
ba379fdc A |
545 | ASSERT(!structure->isUncacheableDictionary()); |
546 | ||
93a37866 | 547 | Structure* transition = create(vm, structure); |
14957cd0 | 548 | |
81345200 A |
549 | DeferGC deferGC(vm.heap); |
550 | structure->materializePropertyMapIfNecessary(vm, deferGC); | |
551 | transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); | |
93a37866 | 552 | transition->m_offset = structure->m_offset; |
ba379fdc | 553 | transition->m_dictionaryKind = kind; |
6fe7ccc8 A |
554 | transition->pin(); |
555 | ||
93a37866 | 556 | transition->checkOffsetConsistency(); |
14957cd0 A |
557 | return transition; |
558 | } | |
559 | ||
93a37866 | 560 | Structure* Structure::toCacheableDictionaryTransition(VM& vm, Structure* structure) |
14957cd0 | 561 | { |
93a37866 | 562 | return toDictionaryTransition(vm, structure, CachedDictionaryKind); |
14957cd0 A |
563 | } |
564 | ||
93a37866 | 565 | Structure* Structure::toUncacheableDictionaryTransition(VM& vm, Structure* structure) |
14957cd0 | 566 | { |
93a37866 | 567 | return toDictionaryTransition(vm, structure, UncachedDictionaryKind); |
14957cd0 A |
568 | } |
569 | ||
570 | // In future we may want to cache this transition. | |
93a37866 | 571 | Structure* Structure::sealTransition(VM& vm, Structure* structure) |
14957cd0 | 572 | { |
93a37866 | 573 | Structure* transition = preventExtensionsTransition(vm, structure); |
14957cd0 | 574 | |
93a37866 A |
575 | if (transition->propertyTable()) { |
576 | PropertyTable::iterator end = transition->propertyTable()->end(); | |
577 | for (PropertyTable::iterator iter = transition->propertyTable()->begin(); iter != end; ++iter) | |
14957cd0 A |
578 | iter->attributes |= DontDelete; |
579 | } | |
580 | ||
93a37866 | 581 | transition->checkOffsetConsistency(); |
14957cd0 A |
582 | return transition; |
583 | } | |
584 | ||
585 | // In future we may want to cache this transition. | |
93a37866 | 586 | Structure* Structure::freezeTransition(VM& vm, Structure* structure) |
14957cd0 | 587 | { |
93a37866 | 588 | Structure* transition = preventExtensionsTransition(vm, structure); |
14957cd0 | 589 | |
93a37866 A |
590 | if (transition->propertyTable()) { |
591 | PropertyTable::iterator iter = transition->propertyTable()->begin(); | |
592 | PropertyTable::iterator end = transition->propertyTable()->end(); | |
6fe7ccc8 A |
593 | if (iter != end) |
594 | transition->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true; | |
595 | for (; iter != end; ++iter) | |
596 | iter->attributes |= iter->attributes & Accessor ? DontDelete : (DontDelete | ReadOnly); | |
14957cd0 A |
597 | } |
598 | ||
81345200 A |
599 | ASSERT(transition->hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties(vm)); |
600 | ASSERT(transition->hasGetterSetterProperties() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties(vm)); | |
93a37866 | 601 | transition->checkOffsetConsistency(); |
14957cd0 A |
602 | return transition; |
603 | } | |
604 | ||
605 | // In future we may want to cache this transition. | |
93a37866 | 606 | Structure* Structure::preventExtensionsTransition(VM& vm, Structure* structure) |
14957cd0 | 607 | { |
93a37866 | 608 | Structure* transition = create(vm, structure); |
14957cd0 A |
609 | |
610 | // Don't set m_offset, as one can not transition to this. | |
611 | ||
81345200 A |
612 | DeferGC deferGC(vm.heap); |
613 | structure->materializePropertyMapIfNecessary(vm, deferGC); | |
614 | transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); | |
93a37866 | 615 | transition->m_offset = structure->m_offset; |
14957cd0 | 616 | transition->m_preventExtensions = true; |
6fe7ccc8 | 617 | transition->pin(); |
14957cd0 | 618 | |
93a37866 A |
619 | transition->checkOffsetConsistency(); |
620 | return transition; | |
621 | } | |
622 | ||
81345200 | 623 | PropertyTable* Structure::takePropertyTableOrCloneIfPinned(VM& vm) |
93a37866 | 624 | { |
81345200 A |
625 | DeferGC deferGC(vm.heap); |
626 | materializePropertyMapIfNecessaryForPinning(vm, deferGC); | |
627 | ||
93a37866 | 628 | if (m_isPinnedPropertyTable) |
81345200 A |
629 | return propertyTable()->copy(vm, propertyTable()->size() + 1); |
630 | ||
631 | // Hold the lock while stealing the table - so that getConcurrently() on another thread | |
632 | // will either have to bypass this structure, or will get to use the property table | |
633 | // before it is stolen. | |
634 | ConcurrentJITLocker locker(m_lock); | |
93a37866 A |
635 | PropertyTable* takenPropertyTable = propertyTable().get(); |
636 | propertyTable().clear(); | |
637 | return takenPropertyTable; | |
638 | } | |
639 | ||
640 | Structure* Structure::nonPropertyTransition(VM& vm, Structure* structure, NonPropertyTransition transitionKind) | |
641 | { | |
642 | unsigned attributes = toAttributes(transitionKind); | |
643 | IndexingType indexingType = newIndexingType(structure->indexingTypeIncludingHistory(), transitionKind); | |
644 | ||
645 | if (JSGlobalObject* globalObject = structure->m_globalObject.get()) { | |
646 | if (globalObject->isOriginalArrayStructure(structure)) { | |
647 | Structure* result = globalObject->originalArrayStructureForIndexingType(indexingType); | |
648 | if (result->indexingTypeIncludingHistory() == indexingType) { | |
649 | structure->notifyTransitionFromThisStructure(); | |
650 | return result; | |
651 | } | |
652 | } | |
653 | } | |
654 | ||
81345200 A |
655 | Structure* existingTransition; |
656 | if (!structure->isDictionary() && (existingTransition = structure->m_transitionTable.get(0, attributes))) { | |
93a37866 A |
657 | ASSERT(existingTransition->m_attributesInPrevious == attributes); |
658 | ASSERT(existingTransition->indexingTypeIncludingHistory() == indexingType); | |
659 | return existingTransition; | |
660 | } | |
661 | ||
662 | Structure* transition = create(vm, structure); | |
93a37866 | 663 | transition->m_attributesInPrevious = attributes; |
81345200 A |
664 | transition->m_blob.setIndexingType(indexingType); |
665 | transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm)); | |
93a37866 A |
666 | transition->m_offset = structure->m_offset; |
667 | checkOffset(transition->m_offset, transition->inlineCapacity()); | |
668 | ||
81345200 A |
669 | if (structure->isDictionary()) |
670 | transition->pin(); | |
671 | else { | |
672 | ConcurrentJITLocker locker(structure->m_lock); | |
673 | structure->m_transitionTable.add(vm, transition); | |
674 | } | |
93a37866 | 675 | transition->checkOffsetConsistency(); |
14957cd0 | 676 | return transition; |
9dae56ea A |
677 | } |
678 | ||
14957cd0 | 679 | // In future we may want to cache this property. |
93a37866 | 680 | bool Structure::isSealed(VM& vm) |
9dae56ea | 681 | { |
14957cd0 A |
682 | if (isExtensible()) |
683 | return false; | |
684 | ||
81345200 A |
685 | DeferGC deferGC(vm.heap); |
686 | materializePropertyMapIfNecessary(vm, deferGC); | |
93a37866 | 687 | if (!propertyTable()) |
14957cd0 A |
688 | return true; |
689 | ||
93a37866 A |
690 | PropertyTable::iterator end = propertyTable()->end(); |
691 | for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { | |
14957cd0 A |
692 | if ((iter->attributes & DontDelete) != DontDelete) |
693 | return false; | |
694 | } | |
695 | return true; | |
ba379fdc | 696 | } |
9dae56ea | 697 | |
14957cd0 | 698 | // In future we may want to cache this property. |
93a37866 | 699 | bool Structure::isFrozen(VM& vm) |
ba379fdc | 700 | { |
14957cd0 A |
701 | if (isExtensible()) |
702 | return false; | |
703 | ||
81345200 A |
704 | DeferGC deferGC(vm.heap); |
705 | materializePropertyMapIfNecessary(vm, deferGC); | |
93a37866 | 706 | if (!propertyTable()) |
14957cd0 A |
707 | return true; |
708 | ||
93a37866 A |
709 | PropertyTable::iterator end = propertyTable()->end(); |
710 | for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { | |
6fe7ccc8 A |
711 | if (!(iter->attributes & DontDelete)) |
712 | return false; | |
713 | if (!(iter->attributes & (ReadOnly | Accessor))) | |
14957cd0 A |
714 | return false; |
715 | } | |
716 | return true; | |
ba379fdc | 717 | } |
9dae56ea | 718 | |
93a37866 | 719 | Structure* Structure::flattenDictionaryStructure(VM& vm, JSObject* object) |
ba379fdc | 720 | { |
93a37866 | 721 | checkOffsetConsistency(); |
ba379fdc | 722 | ASSERT(isDictionary()); |
81345200 A |
723 | |
724 | size_t beforeOutOfLineCapacity = this->outOfLineCapacity(); | |
ba379fdc | 725 | if (isUncacheableDictionary()) { |
93a37866 A |
726 | ASSERT(propertyTable()); |
727 | ||
728 | size_t propertyCount = propertyTable()->size(); | |
ba379fdc | 729 | |
93a37866 | 730 | // Holds our values compacted by insertion order. |
14957cd0 A |
731 | Vector<JSValue> values(propertyCount); |
732 | ||
93a37866 | 733 | // Copies out our values from their hashed locations, compacting property table offsets as we go. |
14957cd0 | 734 | unsigned i = 0; |
93a37866 A |
735 | PropertyTable::iterator end = propertyTable()->end(); |
736 | m_offset = invalidOffset; | |
737 | for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter, ++i) { | |
738 | values[i] = object->getDirect(iter->offset); | |
739 | m_offset = iter->offset = offsetForPropertyNumber(i, m_inlineCapacity); | |
ba379fdc A |
740 | } |
741 | ||
93a37866 | 742 | // Copies in our values to their compacted locations. |
ba379fdc | 743 | for (unsigned i = 0; i < propertyCount; i++) |
93a37866 | 744 | object->putDirect(vm, offsetForPropertyNumber(i, m_inlineCapacity), values[i]); |
ba379fdc | 745 | |
93a37866 A |
746 | propertyTable()->clearDeletedOffsets(); |
747 | checkOffsetConsistency(); | |
ba379fdc | 748 | } |
9dae56ea | 749 | |
ba379fdc | 750 | m_dictionaryKind = NoneDictionaryKind; |
81345200 A |
751 | m_hasBeenFlattenedBefore = true; |
752 | ||
753 | size_t afterOutOfLineCapacity = this->outOfLineCapacity(); | |
754 | ||
755 | if (beforeOutOfLineCapacity != afterOutOfLineCapacity) { | |
756 | ASSERT(beforeOutOfLineCapacity > afterOutOfLineCapacity); | |
757 | // If the object had a Butterfly but after flattening/compacting we no longer have need of it, | |
758 | // we need to zero it out because the collector depends on the Structure to know the size for copying. | |
759 | if (object->butterfly() && !afterOutOfLineCapacity && !this->hasIndexingHeader(object)) | |
760 | object->setStructureAndButterfly(vm, this, 0); | |
761 | // If the object was down-sized to the point where the base of the Butterfly is no longer within the | |
762 | // first CopiedBlock::blockSize bytes, we'll get the wrong answer if we try to mask the base back to | |
763 | // the CopiedBlock header. To prevent this case we need to memmove the Butterfly down. | |
764 | else if (object->butterfly()) | |
765 | object->shiftButterflyAfterFlattening(vm, beforeOutOfLineCapacity, afterOutOfLineCapacity); | |
766 | } | |
12899fa2 | 767 | |
ba379fdc | 768 | return this; |
9dae56ea A |
769 | } |
770 | ||
93a37866 | 771 | PropertyOffset Structure::addPropertyWithoutTransition(VM& vm, PropertyName propertyName, unsigned attributes, JSCell* specificValue) |
9dae56ea | 772 | { |
93a37866 | 773 | ASSERT(!enumerationCache()); |
f9bf01c6 A |
774 | |
775 | if (m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) | |
776 | specificValue = 0; | |
9dae56ea | 777 | |
81345200 A |
778 | DeferGC deferGC(vm.heap); |
779 | materializePropertyMapIfNecessaryForPinning(vm, deferGC); | |
6fe7ccc8 A |
780 | |
781 | pin(); | |
f9bf01c6 | 782 | |
93a37866 | 783 | return putSpecificValue(vm, propertyName, attributes, specificValue); |
9dae56ea A |
784 | } |
785 | ||
93a37866 | 786 | PropertyOffset Structure::removePropertyWithoutTransition(VM& vm, PropertyName propertyName) |
9dae56ea | 787 | { |
ba379fdc | 788 | ASSERT(isUncacheableDictionary()); |
93a37866 | 789 | ASSERT(!enumerationCache()); |
9dae56ea | 790 | |
81345200 A |
791 | DeferGC deferGC(vm.heap); |
792 | materializePropertyMapIfNecessaryForPinning(vm, deferGC); | |
9dae56ea | 793 | |
6fe7ccc8 | 794 | pin(); |
93a37866 | 795 | return remove(propertyName); |
9dae56ea A |
796 | } |
797 | ||
6fe7ccc8 A |
798 | void Structure::pin() |
799 | { | |
93a37866 | 800 | ASSERT(propertyTable()); |
6fe7ccc8 | 801 | m_isPinnedPropertyTable = true; |
93a37866 | 802 | clearPreviousID(); |
6fe7ccc8 A |
803 | m_nameInPrevious.clear(); |
804 | } | |
805 | ||
93a37866 A |
806 | void Structure::allocateRareData(VM& vm) |
807 | { | |
81345200 | 808 | ASSERT(!m_hasRareData); |
93a37866 | 809 | StructureRareData* rareData = StructureRareData::create(vm, previous()); |
93a37866 | 810 | m_previousOrRareData.set(vm, this, rareData); |
81345200 A |
811 | m_hasRareData = true; |
812 | ASSERT(m_hasRareData); | |
93a37866 A |
813 | } |
814 | ||
815 | void Structure::cloneRareDataFrom(VM& vm, const Structure* other) | |
816 | { | |
81345200 A |
817 | ASSERT(!m_hasRareData); |
818 | ASSERT(other->m_hasRareData); | |
93a37866 | 819 | StructureRareData* newRareData = StructureRareData::clone(vm, other->rareData()); |
93a37866 | 820 | m_previousOrRareData.set(vm, this, newRareData); |
81345200 A |
821 | m_hasRareData = true; |
822 | ASSERT(m_hasRareData); | |
93a37866 A |
823 | } |
824 | ||
9dae56ea A |
825 | #if DUMP_PROPERTYMAP_STATS |
826 | ||
81345200 A |
827 | PropertyMapHashTableStats* propertyMapHashTableStats = 0; |
828 | ||
9dae56ea | 829 | struct PropertyMapStatisticsExitLogger { |
81345200 | 830 | PropertyMapStatisticsExitLogger(); |
9dae56ea A |
831 | ~PropertyMapStatisticsExitLogger(); |
832 | }; | |
833 | ||
81345200 A |
834 | DEFINE_GLOBAL_FOR_LOGGING(PropertyMapStatisticsExitLogger, logger, ); |
835 | ||
836 | PropertyMapStatisticsExitLogger::PropertyMapStatisticsExitLogger() | |
837 | { | |
838 | propertyMapHashTableStats = adoptPtr(new PropertyMapHashTableStats()).leakPtr(); | |
839 | } | |
9dae56ea A |
840 | |
841 | PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger() | |
842 | { | |
81345200 A |
843 | unsigned finds = propertyMapHashTableStats->numFinds; |
844 | unsigned collisions = propertyMapHashTableStats->numCollisions; | |
845 | dataLogF("\nJSC::PropertyMap statistics for process %d\n\n", getCurrentProcessID()); | |
846 | dataLogF("%d finds\n", finds); | |
847 | dataLogF("%d collisions (%.1f%%)\n", collisions, 100.0 * collisions / finds); | |
848 | dataLogF("%d lookups\n", propertyMapHashTableStats->numLookups.load()); | |
849 | dataLogF("%d lookup probings\n", propertyMapHashTableStats->numLookupProbing.load()); | |
850 | dataLogF("%d adds\n", propertyMapHashTableStats->numAdds.load()); | |
851 | dataLogF("%d removes\n", propertyMapHashTableStats->numRemoves.load()); | |
852 | dataLogF("%d rehashes\n", propertyMapHashTableStats->numRehashes.load()); | |
853 | dataLogF("%d reinserts\n", propertyMapHashTableStats->numReinserts.load()); | |
9dae56ea A |
854 | } |
855 | ||
856 | #endif | |
857 | ||
9dae56ea A |
858 | #if !DO_PROPERTYMAP_CONSTENCY_CHECK |
859 | ||
860 | inline void Structure::checkConsistency() | |
861 | { | |
93a37866 | 862 | checkOffsetConsistency(); |
9dae56ea A |
863 | } |
864 | ||
865 | #endif | |
866 | ||
81345200 | 867 | PropertyTable* Structure::copyPropertyTable(VM& vm) |
9dae56ea | 868 | { |
93a37866 A |
869 | if (!propertyTable()) |
870 | return 0; | |
81345200 | 871 | return PropertyTable::clone(vm, *propertyTable().get()); |
9dae56ea A |
872 | } |
873 | ||
81345200 | 874 | PropertyTable* Structure::copyPropertyTableForPinning(VM& vm) |
6fe7ccc8 | 875 | { |
93a37866 | 876 | if (propertyTable()) |
81345200 | 877 | return PropertyTable::clone(vm, *propertyTable().get()); |
93a37866 | 878 | return PropertyTable::create(vm, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity)); |
6fe7ccc8 A |
879 | } |
880 | ||
81345200 | 881 | PropertyOffset Structure::getConcurrently(VM&, StringImpl* uid, unsigned& attributes, JSCell*& specificValue) |
9dae56ea | 882 | { |
81345200 A |
883 | Vector<Structure*, 8> structures; |
884 | Structure* structure; | |
885 | PropertyTable* table; | |
886 | ||
887 | findStructuresAndMapForMaterialization(structures, structure, table); | |
888 | ||
889 | if (table) { | |
890 | PropertyMapEntry* entry = table->get(uid); | |
891 | if (entry) { | |
892 | attributes = entry->attributes; | |
893 | specificValue = entry->specificValue.get(); | |
894 | PropertyOffset result = entry->offset; | |
895 | structure->m_lock.unlock(); | |
896 | return result; | |
897 | } | |
898 | structure->m_lock.unlock(); | |
899 | } | |
900 | ||
901 | for (unsigned i = structures.size(); i--;) { | |
902 | structure = structures[i]; | |
903 | if (structure->m_nameInPrevious.get() != uid) | |
904 | continue; | |
905 | ||
906 | attributes = structure->m_attributesInPrevious; | |
907 | specificValue = structure->m_specificValueInPrevious.get(); | |
908 | return structure->m_offset; | |
909 | } | |
910 | ||
911 | return invalidOffset; | |
9dae56ea A |
912 | } |
913 | ||
93a37866 | 914 | bool Structure::despecifyFunction(VM& vm, PropertyName propertyName) |
ba379fdc | 915 | { |
81345200 A |
916 | DeferGC deferGC(vm.heap); |
917 | materializePropertyMapIfNecessary(vm, deferGC); | |
93a37866 | 918 | if (!propertyTable()) |
ba379fdc A |
919 | return false; |
920 | ||
81345200 | 921 | PropertyMapEntry* entry = propertyTable()->get(propertyName.uid()); |
14957cd0 | 922 | if (!entry) |
ba379fdc A |
923 | return false; |
924 | ||
14957cd0 A |
925 | ASSERT(entry->specificValue); |
926 | entry->specificValue.clear(); | |
927 | return true; | |
ba379fdc A |
928 | } |
929 | ||
93a37866 | 930 | void Structure::despecifyAllFunctions(VM& vm) |
f9bf01c6 | 931 | { |
81345200 A |
932 | DeferGC deferGC(vm.heap); |
933 | materializePropertyMapIfNecessary(vm, deferGC); | |
93a37866 | 934 | if (!propertyTable()) |
f9bf01c6 | 935 | return; |
14957cd0 | 936 | |
93a37866 A |
937 | PropertyTable::iterator end = propertyTable()->end(); |
938 | for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) | |
14957cd0 | 939 | iter->specificValue.clear(); |
f9bf01c6 A |
940 | } |
941 | ||
93a37866 | 942 | PropertyOffset Structure::putSpecificValue(VM& vm, PropertyName propertyName, unsigned attributes, JSCell* specificValue) |
9dae56ea | 943 | { |
81345200 A |
944 | GCSafeConcurrentJITLocker locker(m_lock, vm.heap); |
945 | ||
93a37866 | 946 | ASSERT(!JSC::isValidOffset(get(vm, propertyName))); |
9dae56ea A |
947 | |
948 | checkConsistency(); | |
f9bf01c6 A |
949 | if (attributes & DontEnum) |
950 | m_hasNonEnumerableProperties = true; | |
951 | ||
93a37866 | 952 | StringImpl* rep = propertyName.uid(); |
9dae56ea | 953 | |
93a37866 | 954 | if (!propertyTable()) |
81345200 | 955 | createPropertyMap(locker, vm); |
14957cd0 | 956 | |
93a37866 | 957 | PropertyOffset newOffset = propertyTable()->nextOffset(m_inlineCapacity); |
9dae56ea | 958 | |
81345200 | 959 | propertyTable()->add(PropertyMapEntry(vm, propertyTable().get(), rep, newOffset, attributes, specificValue), m_offset, PropertyTable::PropertyOffsetMayChange); |
93a37866 | 960 | |
9dae56ea A |
961 | checkConsistency(); |
962 | return newOffset; | |
963 | } | |
964 | ||
93a37866 | 965 | PropertyOffset Structure::remove(PropertyName propertyName) |
9dae56ea | 966 | { |
81345200 A |
967 | ConcurrentJITLocker locker(m_lock); |
968 | ||
9dae56ea A |
969 | checkConsistency(); |
970 | ||
93a37866 | 971 | StringImpl* rep = propertyName.uid(); |
9dae56ea | 972 | |
93a37866 A |
973 | if (!propertyTable()) |
974 | return invalidOffset; | |
9dae56ea | 975 | |
93a37866 | 976 | PropertyTable::find_iterator position = propertyTable()->find(rep); |
14957cd0 | 977 | if (!position.first) |
93a37866 | 978 | return invalidOffset; |
9dae56ea | 979 | |
93a37866 | 980 | PropertyOffset offset = position.first->offset; |
9dae56ea | 981 | |
93a37866 A |
982 | propertyTable()->remove(position); |
983 | propertyTable()->addDeletedOffset(offset); | |
9dae56ea A |
984 | |
985 | checkConsistency(); | |
986 | return offset; | |
987 | } | |
988 | ||
81345200 | 989 | void Structure::createPropertyMap(const GCSafeConcurrentJITLocker&, VM& vm, unsigned capacity) |
9dae56ea | 990 | { |
93a37866 | 991 | ASSERT(!propertyTable()); |
9dae56ea A |
992 | |
993 | checkConsistency(); | |
93a37866 | 994 | propertyTable().set(vm, this, PropertyTable::create(vm, capacity)); |
9dae56ea A |
995 | } |
996 | ||
93a37866 | 997 | void Structure::getPropertyNamesFromStructure(VM& vm, PropertyNameArray& propertyNames, EnumerationMode mode) |
9dae56ea | 998 | { |
81345200 A |
999 | DeferGC deferGC(vm.heap); |
1000 | materializePropertyMapIfNecessary(vm, deferGC); | |
93a37866 | 1001 | if (!propertyTable()) |
14957cd0 | 1002 | return; |
9dae56ea | 1003 | |
14957cd0 | 1004 | bool knownUnique = !propertyNames.size(); |
9dae56ea | 1005 | |
93a37866 A |
1006 | PropertyTable::iterator end = propertyTable()->end(); |
1007 | for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { | |
14957cd0 | 1008 | ASSERT(m_hasNonEnumerableProperties || !(iter->attributes & DontEnum)); |
81345200 | 1009 | if (!iter->key->isEmptyUnique() && (!(iter->attributes & DontEnum) || mode == IncludeDontEnumProperties)) { |
14957cd0 A |
1010 | if (knownUnique) |
1011 | propertyNames.addKnownUnique(iter->key); | |
1012 | else | |
1013 | propertyNames.add(iter->key); | |
9dae56ea A |
1014 | } |
1015 | } | |
9dae56ea A |
1016 | } |
1017 | ||
93a37866 A |
1018 | JSValue Structure::prototypeForLookup(CodeBlock* codeBlock) const |
1019 | { | |
1020 | return prototypeForLookup(codeBlock->globalObject()); | |
1021 | } | |
1022 | ||
6fe7ccc8 A |
1023 | void Structure::visitChildren(JSCell* cell, SlotVisitor& visitor) |
1024 | { | |
1025 | Structure* thisObject = jsCast<Structure*>(cell); | |
81345200 | 1026 | ASSERT_GC_OBJECT_INHERITS(thisObject, info()); |
6fe7ccc8 | 1027 | ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); |
93a37866 | 1028 | |
6fe7ccc8 | 1029 | JSCell::visitChildren(thisObject, visitor); |
93a37866 | 1030 | visitor.append(&thisObject->m_globalObject); |
6fe7ccc8 A |
1031 | if (!thisObject->isObject()) |
1032 | thisObject->m_cachedPrototypeChain.clear(); | |
1033 | else { | |
93a37866 A |
1034 | visitor.append(&thisObject->m_prototype); |
1035 | visitor.append(&thisObject->m_cachedPrototypeChain); | |
6fe7ccc8 | 1036 | } |
93a37866 A |
1037 | visitor.append(&thisObject->m_previousOrRareData); |
1038 | visitor.append(&thisObject->m_specificValueInPrevious); | |
1039 | ||
1040 | if (thisObject->m_isPinnedPropertyTable) { | |
1041 | ASSERT(thisObject->m_propertyTableUnsafe); | |
1042 | visitor.append(&thisObject->m_propertyTableUnsafe); | |
1043 | } else if (thisObject->m_propertyTableUnsafe) | |
1044 | thisObject->m_propertyTableUnsafe.clear(); | |
1045 | } | |
1046 | ||
1047 | bool Structure::prototypeChainMayInterceptStoreTo(VM& vm, PropertyName propertyName) | |
1048 | { | |
1049 | unsigned i = propertyName.asIndex(); | |
1050 | if (i != PropertyName::NotAnIndex) | |
1051 | return anyObjectInChainMayInterceptIndexedAccesses(); | |
1052 | ||
1053 | for (Structure* current = this; ;) { | |
1054 | JSValue prototype = current->storedPrototype(); | |
1055 | if (prototype.isNull()) | |
1056 | return false; | |
1057 | ||
81345200 | 1058 | current = prototype.asCell()->structure(vm); |
93a37866 A |
1059 | |
1060 | unsigned attributes; | |
1061 | JSCell* specificValue; | |
1062 | PropertyOffset offset = current->get(vm, propertyName, attributes, specificValue); | |
1063 | if (!JSC::isValidOffset(offset)) | |
1064 | continue; | |
1065 | ||
1066 | if (attributes & (ReadOnly | Accessor)) | |
1067 | return true; | |
1068 | ||
1069 | return false; | |
9dae56ea A |
1070 | } |
1071 | } | |
1072 | ||
81345200 A |
1073 | void Structure::dump(PrintStream& out) const |
1074 | { | |
1075 | out.print(RawPointer(this), ":[", classInfo()->className, ", {"); | |
1076 | ||
1077 | Vector<Structure*, 8> structures; | |
1078 | Structure* structure; | |
1079 | PropertyTable* table; | |
1080 | ||
1081 | const_cast<Structure*>(this)->findStructuresAndMapForMaterialization( | |
1082 | structures, structure, table); | |
1083 | ||
1084 | CommaPrinter comma; | |
1085 | ||
1086 | if (table) { | |
1087 | PropertyTable::iterator iter = table->begin(); | |
1088 | PropertyTable::iterator end = table->end(); | |
1089 | for (; iter != end; ++iter) { | |
1090 | out.print(comma, iter->key, ":", static_cast<int>(iter->offset)); | |
1091 | if (iter->specificValue) { | |
1092 | DumpContext dummyContext; | |
1093 | out.print("=>", RawPointer(iter->specificValue.get())); | |
1094 | } | |
1095 | } | |
1096 | ||
1097 | structure->m_lock.unlock(); | |
1098 | } | |
1099 | ||
1100 | for (unsigned i = structures.size(); i--;) { | |
1101 | Structure* structure = structures[i]; | |
1102 | if (!structure->m_nameInPrevious) | |
1103 | continue; | |
1104 | out.print(comma, structure->m_nameInPrevious.get(), ":", static_cast<int>(structure->m_offset)); | |
1105 | if (structure->m_specificValueInPrevious) { | |
1106 | DumpContext dummyContext; | |
1107 | out.print("=>", RawPointer(structure->m_specificValueInPrevious.get())); | |
1108 | } | |
1109 | } | |
1110 | ||
1111 | out.print("}, ", IndexingTypeDump(indexingType())); | |
1112 | ||
1113 | if (m_prototype.get().isCell()) | |
1114 | out.print(", Proto:", RawPointer(m_prototype.get().asCell())); | |
1115 | ||
1116 | out.print("]"); | |
1117 | } | |
1118 | ||
1119 | void Structure::dumpInContext(PrintStream& out, DumpContext* context) const | |
1120 | { | |
1121 | if (context) | |
1122 | context->structures.dumpBrief(this, out); | |
1123 | else | |
1124 | dump(out); | |
1125 | } | |
1126 | ||
1127 | void Structure::dumpBrief(PrintStream& out, const CString& string) const | |
1128 | { | |
1129 | out.print("%", string, ":", classInfo()->className); | |
1130 | } | |
1131 | ||
1132 | void Structure::dumpContextHeader(PrintStream& out) | |
1133 | { | |
1134 | out.print("Structures:"); | |
1135 | } | |
1136 | ||
9dae56ea A |
1137 | #if DO_PROPERTYMAP_CONSTENCY_CHECK |
1138 | ||
14957cd0 | 1139 | void PropertyTable::checkConsistency() |
9dae56ea | 1140 | { |
93a37866 | 1141 | checkOffsetConsistency(); |
14957cd0 A |
1142 | ASSERT(m_indexSize >= PropertyTable::MinimumTableSize); |
1143 | ASSERT(m_indexMask); | |
1144 | ASSERT(m_indexSize == m_indexMask + 1); | |
1145 | ASSERT(!(m_indexSize & m_indexMask)); | |
9dae56ea | 1146 | |
14957cd0 A |
1147 | ASSERT(m_keyCount <= m_indexSize / 2); |
1148 | ASSERT(m_keyCount + m_deletedCount <= m_indexSize / 2); | |
1149 | ASSERT(m_deletedCount <= m_indexSize / 4); | |
9dae56ea A |
1150 | |
1151 | unsigned indexCount = 0; | |
1152 | unsigned deletedIndexCount = 0; | |
14957cd0 A |
1153 | for (unsigned a = 0; a != m_indexSize; ++a) { |
1154 | unsigned entryIndex = m_index[a]; | |
1155 | if (entryIndex == PropertyTable::EmptyEntryIndex) | |
9dae56ea | 1156 | continue; |
14957cd0 | 1157 | if (entryIndex == deletedEntryIndex()) { |
9dae56ea A |
1158 | ++deletedIndexCount; |
1159 | continue; | |
1160 | } | |
14957cd0 A |
1161 | ASSERT(entryIndex < deletedEntryIndex()); |
1162 | ASSERT(entryIndex - 1 <= usedCount()); | |
9dae56ea A |
1163 | ++indexCount; |
1164 | ||
14957cd0 A |
1165 | for (unsigned b = a + 1; b != m_indexSize; ++b) |
1166 | ASSERT(m_index[b] != entryIndex); | |
9dae56ea | 1167 | } |
14957cd0 A |
1168 | ASSERT(indexCount == m_keyCount); |
1169 | ASSERT(deletedIndexCount == m_deletedCount); | |
9dae56ea | 1170 | |
14957cd0 | 1171 | ASSERT(!table()[deletedEntryIndex() - 1].key); |
9dae56ea A |
1172 | |
1173 | unsigned nonEmptyEntryCount = 0; | |
14957cd0 A |
1174 | for (unsigned c = 0; c < usedCount(); ++c) { |
1175 | StringImpl* rep = table()[c].key; | |
1176 | if (rep == PROPERTY_MAP_DELETED_ENTRY_KEY) | |
9dae56ea A |
1177 | continue; |
1178 | ++nonEmptyEntryCount; | |
f9bf01c6 | 1179 | unsigned i = rep->existingHash(); |
9dae56ea A |
1180 | unsigned k = 0; |
1181 | unsigned entryIndex; | |
1182 | while (1) { | |
14957cd0 A |
1183 | entryIndex = m_index[i & m_indexMask]; |
1184 | ASSERT(entryIndex != PropertyTable::EmptyEntryIndex); | |
1185 | if (rep == table()[entryIndex - 1].key) | |
9dae56ea A |
1186 | break; |
1187 | if (k == 0) | |
f9bf01c6 | 1188 | k = 1 | doubleHash(rep->existingHash()); |
9dae56ea A |
1189 | i += k; |
1190 | } | |
1191 | ASSERT(entryIndex == c + 1); | |
1192 | } | |
1193 | ||
14957cd0 A |
1194 | ASSERT(nonEmptyEntryCount == m_keyCount); |
1195 | } | |
1196 | ||
1197 | void Structure::checkConsistency() | |
1198 | { | |
93a37866 | 1199 | if (!propertyTable()) |
14957cd0 A |
1200 | return; |
1201 | ||
1202 | if (!m_hasNonEnumerableProperties) { | |
93a37866 A |
1203 | PropertyTable::iterator end = propertyTable()->end(); |
1204 | for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { | |
14957cd0 | 1205 | ASSERT(!(iter->attributes & DontEnum)); |
14957cd0 A |
1206 | } |
1207 | } | |
1208 | ||
93a37866 | 1209 | propertyTable()->checkConsistency(); |
9dae56ea A |
1210 | } |
1211 | ||
1212 | #endif // DO_PROPERTYMAP_CONSTENCY_CHECK | |
1213 | ||
81345200 A |
1214 | bool ClassInfo::hasStaticSetterOrReadonlyProperties(VM& vm) const |
1215 | { | |
1216 | for (const ClassInfo* ci = this; ci; ci = ci->parentClass) { | |
1217 | if (const HashTable* table = ci->propHashTable(vm)) { | |
1218 | if (table->hasSetterOrReadonlyProperties) | |
1219 | return true; | |
1220 | } | |
1221 | } | |
1222 | return false; | |
1223 | } | |
1224 | ||
9dae56ea | 1225 | } // namespace JSC |