]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 2008, 2009, 2013 Apple Inc. All rights reserved. | |
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 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 INC. OR | |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 | */ | |
25 | ||
26 | #include "config.h" | |
27 | #include "Structure.h" | |
28 | ||
29 | #include "CodeBlock.h" | |
30 | #include "DumpContext.h" | |
31 | #include "JSCInlines.h" | |
32 | #include "JSObject.h" | |
33 | #include "JSPropertyNameIterator.h" | |
34 | #include "Lookup.h" | |
35 | #include "PropertyMapHashTable.h" | |
36 | #include "PropertyNameArray.h" | |
37 | #include "StructureChain.h" | |
38 | #include "StructureRareDataInlines.h" | |
39 | #include <wtf/CommaPrinter.h> | |
40 | #include <wtf/ProcessID.h> | |
41 | #include <wtf/RefCountedLeakCounter.h> | |
42 | #include <wtf/RefPtr.h> | |
43 | #include <wtf/Threading.h> | |
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 | ||
56 | namespace JSC { | |
57 | ||
58 | #if DUMP_STRUCTURE_ID_STATISTICS | |
59 | static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>); | |
60 | #endif | |
61 | ||
62 | bool StructureTransitionTable::contains(StringImpl* rep, unsigned attributes) const | |
63 | { | |
64 | if (isUsingSingleSlot()) { | |
65 | Structure* transition = singleTransition(); | |
66 | return transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes; | |
67 | } | |
68 | return map()->get(std::make_pair(rep, attributes)); | |
69 | } | |
70 | ||
71 | inline Structure* StructureTransitionTable::get(StringImpl* rep, unsigned attributes) const | |
72 | { | |
73 | if (isUsingSingleSlot()) { | |
74 | Structure* transition = singleTransition(); | |
75 | return (transition && transition->m_nameInPrevious == rep && transition->m_attributesInPrevious == attributes) ? transition : 0; | |
76 | } | |
77 | return map()->get(std::make_pair(rep, attributes)); | |
78 | } | |
79 | ||
80 | inline void StructureTransitionTable::add(VM& vm, Structure* structure) | |
81 | { | |
82 | if (isUsingSingleSlot()) { | |
83 | Structure* existingTransition = singleTransition(); | |
84 | ||
85 | // This handles the first transition being added. | |
86 | if (!existingTransition) { | |
87 | setSingleTransition(vm, structure); | |
88 | return; | |
89 | } | |
90 | ||
91 | // This handles the second transition being added | |
92 | // (or the first transition being despecified!) | |
93 | setMap(new TransitionMap()); | |
94 | add(vm, existingTransition); | |
95 | } | |
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 | |
102 | map()->set(std::make_pair(structure->m_nameInPrevious.get(), +structure->m_attributesInPrevious), structure); | |
103 | } | |
104 | ||
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; | |
117 | ||
118 | switch (structure->m_transitionTable.size()) { | |
119 | case 0: | |
120 | ++numberLeaf; | |
121 | if (!structure->previousID()) | |
122 | ++numberSingletons; | |
123 | break; | |
124 | ||
125 | case 1: | |
126 | ++numberUsingSingleSlot; | |
127 | break; | |
128 | } | |
129 | ||
130 | if (structure->propertyTable()) { | |
131 | ++numberWithPropertyMaps; | |
132 | totalPropertyMapsSize += structure->propertyTable()->sizeInMemory(); | |
133 | } | |
134 | } | |
135 | ||
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); | |
141 | ||
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())); | |
145 | #else | |
146 | dataLogF("Dumping Structure statistics is not enabled.\n"); | |
147 | #endif | |
148 | } | |
149 | ||
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()) | |
152 | , m_blob(vm.heap.structureIDTable().allocateID(this), indexingType, typeInfo) | |
153 | , m_outOfLineTypeFlags(typeInfo.outOfLineTypeFlags()) | |
154 | , m_globalObject(vm, this, globalObject, WriteBarrier<JSGlobalObject>::MayBeNull) | |
155 | , m_prototype(vm, this, prototype) | |
156 | , m_classInfo(classInfo) | |
157 | , m_transitionWatchpointSet(IsWatched) | |
158 | , m_offset(invalidOffset) | |
159 | , m_inlineCapacity(inlineCapacity) | |
160 | , m_dictionaryKind(NoneDictionaryKind) | |
161 | , m_hasBeenFlattenedBefore(false) | |
162 | , m_isPinnedPropertyTable(false) | |
163 | , m_hasGetterSetterProperties(classInfo->hasStaticSetterOrReadonlyProperties(vm)) | |
164 | , m_hasCustomGetterSetterProperties(false) | |
165 | , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(classInfo->hasStaticSetterOrReadonlyProperties(vm)) | |
166 | , m_hasNonEnumerableProperties(false) | |
167 | , m_attributesInPrevious(0) | |
168 | , m_specificFunctionThrashCount(0) | |
169 | , m_preventExtensions(false) | |
170 | , m_didTransition(false) | |
171 | , m_staticFunctionReified(false) | |
172 | , m_hasRareData(false) | |
173 | { | |
174 | ASSERT(inlineCapacity <= JSFinalObject::maxInlineCapacity()); | |
175 | ASSERT(static_cast<PropertyOffset>(inlineCapacity) < firstOutOfLineOffset); | |
176 | ASSERT(!m_hasRareData); | |
177 | ASSERT(hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm)); | |
178 | ASSERT(hasGetterSetterProperties() || !m_classInfo->hasStaticSetterOrReadonlyProperties(vm)); | |
179 | } | |
180 | ||
181 | const ClassInfo Structure::s_info = { "Structure", 0, 0, 0, CREATE_METHOD_TABLE(Structure) }; | |
182 | ||
183 | Structure::Structure(VM& vm) | |
184 | : JSCell(CreatingEarlyCell) | |
185 | , m_prototype(vm, this, jsNull()) | |
186 | , m_classInfo(info()) | |
187 | , m_transitionWatchpointSet(IsWatched) | |
188 | , m_offset(invalidOffset) | |
189 | , m_inlineCapacity(0) | |
190 | , m_dictionaryKind(NoneDictionaryKind) | |
191 | , m_hasBeenFlattenedBefore(false) | |
192 | , m_isPinnedPropertyTable(false) | |
193 | , m_hasGetterSetterProperties(m_classInfo->hasStaticSetterOrReadonlyProperties(vm)) | |
194 | , m_hasCustomGetterSetterProperties(false) | |
195 | , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(m_classInfo->hasStaticSetterOrReadonlyProperties(vm)) | |
196 | , m_hasNonEnumerableProperties(false) | |
197 | , m_attributesInPrevious(0) | |
198 | , m_specificFunctionThrashCount(0) | |
199 | , m_preventExtensions(false) | |
200 | , m_didTransition(false) | |
201 | , m_staticFunctionReified(false) | |
202 | , m_hasRareData(false) | |
203 | { | |
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)); | |
210 | } | |
211 | ||
212 | Structure::Structure(VM& vm, Structure* previous) | |
213 | : JSCell(vm, vm.structureStructure.get()) | |
214 | , m_prototype(vm, this, previous->storedPrototype()) | |
215 | , m_classInfo(previous->m_classInfo) | |
216 | , m_transitionWatchpointSet(IsWatched) | |
217 | , m_offset(invalidOffset) | |
218 | , m_inlineCapacity(previous->m_inlineCapacity) | |
219 | , m_dictionaryKind(previous->m_dictionaryKind) | |
220 | , m_hasBeenFlattenedBefore(previous->m_hasBeenFlattenedBefore) | |
221 | , m_isPinnedPropertyTable(false) | |
222 | , m_hasGetterSetterProperties(previous->m_hasGetterSetterProperties) | |
223 | , m_hasCustomGetterSetterProperties(previous->m_hasCustomGetterSetterProperties) | |
224 | , m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(previous->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto) | |
225 | , m_hasNonEnumerableProperties(previous->m_hasNonEnumerableProperties) | |
226 | , m_attributesInPrevious(0) | |
227 | , m_specificFunctionThrashCount(previous->m_specificFunctionThrashCount) | |
228 | , m_preventExtensions(previous->m_preventExtensions) | |
229 | , m_didTransition(true) | |
230 | , m_staticFunctionReified(previous->m_staticFunctionReified) | |
231 | , m_hasRareData(false) | |
232 | { | |
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()) | |
239 | cloneRareDataFrom(vm, previous); | |
240 | setPreviousID(vm, previous); | |
241 | ||
242 | previous->notifyTransitionFromThisStructure(); | |
243 | if (previous->m_globalObject) | |
244 | m_globalObject.set(vm, this, previous->m_globalObject.get()); | |
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()); | |
254 | } | |
255 | ||
256 | void Structure::destroy(JSCell* cell) | |
257 | { | |
258 | static_cast<Structure*>(cell)->Structure::~Structure(); | |
259 | } | |
260 | ||
261 | void Structure::findStructuresAndMapForMaterialization(Vector<Structure*, 8>& structures, Structure*& structure, PropertyTable*& table) | |
262 | { | |
263 | ASSERT(structures.isEmpty()); | |
264 | table = 0; | |
265 | ||
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; | |
274 | } | |
275 | ||
276 | structures.append(structure); | |
277 | structure->m_lock.unlock(); | |
278 | } | |
279 | ||
280 | ASSERT(!structure); | |
281 | ASSERT(!table); | |
282 | } | |
283 | ||
284 | void Structure::materializePropertyMap(VM& vm) | |
285 | { | |
286 | ASSERT(structure()->classInfo() == info()); | |
287 | ASSERT(!propertyTable()); | |
288 | ||
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--;) { | |
310 | structure = structures[i]; | |
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); | |
315 | } | |
316 | ||
317 | checkOffsetConsistency(); | |
318 | } | |
319 | ||
320 | void Structure::despecifyDictionaryFunction(VM& vm, PropertyName propertyName) | |
321 | { | |
322 | StringImpl* rep = propertyName.uid(); | |
323 | ||
324 | DeferGC deferGC(vm.heap); | |
325 | materializePropertyMapIfNecessary(vm, deferGC); | |
326 | ||
327 | ASSERT(isDictionary()); | |
328 | ASSERT(propertyTable()); | |
329 | ||
330 | PropertyMapEntry* entry = propertyTable()->get(rep); | |
331 | ASSERT(entry); | |
332 | entry->specificValue.clear(); | |
333 | } | |
334 | ||
335 | Structure* Structure::addPropertyTransitionToExistingStructureImpl(Structure* structure, StringImpl* uid, unsigned attributes, JSCell* specificValue, PropertyOffset& offset) | |
336 | { | |
337 | ASSERT(!structure->isDictionary()); | |
338 | ASSERT(structure->isObject()); | |
339 | ||
340 | if (Structure* existingTransition = structure->m_transitionTable.get(uid, attributes)) { | |
341 | JSCell* specificValueInPrevious = existingTransition->m_specificValueInPrevious.get(); | |
342 | if (specificValueInPrevious && specificValueInPrevious != specificValue) | |
343 | return 0; | |
344 | validateOffset(existingTransition->m_offset, existingTransition->inlineCapacity()); | |
345 | offset = existingTransition->m_offset; | |
346 | return existingTransition; | |
347 | } | |
348 | ||
349 | return 0; | |
350 | } | |
351 | ||
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 | ||
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 | ||
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 | ||
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 | ||
416 | Structure* Structure::addPropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes, JSCell* specificValue, PropertyOffset& offset, PutPropertySlot::Context context) | |
417 | { | |
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. | |
425 | if (specificValue && structure->m_transitionTable.contains(propertyName.uid(), attributes)) | |
426 | specificValue = 0; | |
427 | ||
428 | ASSERT(!structure->isDictionary()); | |
429 | ASSERT(structure->isObject()); | |
430 | ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset)); | |
431 | ||
432 | if (structure->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) | |
433 | specificValue = 0; | |
434 | ||
435 | int maxTransitionLength; | |
436 | if (context == PutPropertySlot::PutById) | |
437 | maxTransitionLength = s_maxTransitionLengthForNonEvalPutById; | |
438 | else | |
439 | maxTransitionLength = s_maxTransitionLength; | |
440 | if (structure->transitionCount() > maxTransitionLength) { | |
441 | Structure* transition = toCacheableDictionaryTransition(vm, structure); | |
442 | ASSERT(structure != transition); | |
443 | offset = transition->putSpecificValue(vm, propertyName, attributes, specificValue); | |
444 | return transition; | |
445 | } | |
446 | ||
447 | Structure* transition = create(vm, structure); | |
448 | ||
449 | transition->m_cachedPrototypeChain.setMayBeNull(vm, transition, structure->m_cachedPrototypeChain.get()); | |
450 | transition->m_nameInPrevious = propertyName.uid(); | |
451 | transition->m_attributesInPrevious = attributes; | |
452 | transition->m_specificValueInPrevious.setMayBeNull(vm, transition, specificValue); | |
453 | transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm)); | |
454 | transition->m_offset = structure->m_offset; | |
455 | ||
456 | offset = transition->putSpecificValue(vm, propertyName, attributes, specificValue); | |
457 | ||
458 | checkOffset(transition->m_offset, transition->inlineCapacity()); | |
459 | { | |
460 | ConcurrentJITLocker locker(structure->m_lock); | |
461 | structure->m_transitionTable.add(vm, transition); | |
462 | } | |
463 | transition->checkOffsetConsistency(); | |
464 | structure->checkOffsetConsistency(); | |
465 | return transition; | |
466 | } | |
467 | ||
468 | Structure* Structure::removePropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, PropertyOffset& offset) | |
469 | { | |
470 | ASSERT(!structure->isUncacheableDictionary()); | |
471 | ||
472 | Structure* transition = toUncacheableDictionaryTransition(vm, structure); | |
473 | ||
474 | offset = transition->remove(propertyName); | |
475 | ||
476 | transition->checkOffsetConsistency(); | |
477 | return transition; | |
478 | } | |
479 | ||
480 | Structure* Structure::changePrototypeTransition(VM& vm, Structure* structure, JSValue prototype) | |
481 | { | |
482 | Structure* transition = create(vm, structure); | |
483 | ||
484 | transition->m_prototype.set(vm, transition, prototype); | |
485 | ||
486 | DeferGC deferGC(vm.heap); | |
487 | structure->materializePropertyMapIfNecessary(vm, deferGC); | |
488 | transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); | |
489 | transition->m_offset = structure->m_offset; | |
490 | transition->pin(); | |
491 | ||
492 | transition->checkOffsetConsistency(); | |
493 | return transition; | |
494 | } | |
495 | ||
496 | Structure* Structure::despecifyFunctionTransition(VM& vm, Structure* structure, PropertyName replaceFunction) | |
497 | { | |
498 | ASSERT(structure->m_specificFunctionThrashCount < maxSpecificFunctionThrashCount); | |
499 | Structure* transition = create(vm, structure); | |
500 | ||
501 | ++transition->m_specificFunctionThrashCount; | |
502 | ||
503 | DeferGC deferGC(vm.heap); | |
504 | structure->materializePropertyMapIfNecessary(vm, deferGC); | |
505 | transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); | |
506 | transition->m_offset = structure->m_offset; | |
507 | transition->pin(); | |
508 | ||
509 | if (transition->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) | |
510 | transition->despecifyAllFunctions(vm); | |
511 | else { | |
512 | bool removed = transition->despecifyFunction(vm, replaceFunction); | |
513 | ASSERT_UNUSED(removed, removed); | |
514 | } | |
515 | ||
516 | transition->checkOffsetConsistency(); | |
517 | return transition; | |
518 | } | |
519 | ||
520 | Structure* Structure::attributeChangeTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes) | |
521 | { | |
522 | DeferGC deferGC(vm.heap); | |
523 | if (!structure->isUncacheableDictionary()) { | |
524 | Structure* transition = create(vm, structure); | |
525 | ||
526 | structure->materializePropertyMapIfNecessary(vm, deferGC); | |
527 | transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); | |
528 | transition->m_offset = structure->m_offset; | |
529 | transition->pin(); | |
530 | ||
531 | structure = transition; | |
532 | } | |
533 | ||
534 | ASSERT(structure->propertyTable()); | |
535 | PropertyMapEntry* entry = structure->propertyTable()->get(propertyName.uid()); | |
536 | ASSERT(entry); | |
537 | entry->attributes = attributes; | |
538 | ||
539 | structure->checkOffsetConsistency(); | |
540 | return structure; | |
541 | } | |
542 | ||
543 | Structure* Structure::toDictionaryTransition(VM& vm, Structure* structure, DictionaryKind kind) | |
544 | { | |
545 | ASSERT(!structure->isUncacheableDictionary()); | |
546 | ||
547 | Structure* transition = create(vm, structure); | |
548 | ||
549 | DeferGC deferGC(vm.heap); | |
550 | structure->materializePropertyMapIfNecessary(vm, deferGC); | |
551 | transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); | |
552 | transition->m_offset = structure->m_offset; | |
553 | transition->m_dictionaryKind = kind; | |
554 | transition->pin(); | |
555 | ||
556 | transition->checkOffsetConsistency(); | |
557 | return transition; | |
558 | } | |
559 | ||
560 | Structure* Structure::toCacheableDictionaryTransition(VM& vm, Structure* structure) | |
561 | { | |
562 | return toDictionaryTransition(vm, structure, CachedDictionaryKind); | |
563 | } | |
564 | ||
565 | Structure* Structure::toUncacheableDictionaryTransition(VM& vm, Structure* structure) | |
566 | { | |
567 | return toDictionaryTransition(vm, structure, UncachedDictionaryKind); | |
568 | } | |
569 | ||
570 | // In future we may want to cache this transition. | |
571 | Structure* Structure::sealTransition(VM& vm, Structure* structure) | |
572 | { | |
573 | Structure* transition = preventExtensionsTransition(vm, structure); | |
574 | ||
575 | if (transition->propertyTable()) { | |
576 | PropertyTable::iterator end = transition->propertyTable()->end(); | |
577 | for (PropertyTable::iterator iter = transition->propertyTable()->begin(); iter != end; ++iter) | |
578 | iter->attributes |= DontDelete; | |
579 | } | |
580 | ||
581 | transition->checkOffsetConsistency(); | |
582 | return transition; | |
583 | } | |
584 | ||
585 | // In future we may want to cache this transition. | |
586 | Structure* Structure::freezeTransition(VM& vm, Structure* structure) | |
587 | { | |
588 | Structure* transition = preventExtensionsTransition(vm, structure); | |
589 | ||
590 | if (transition->propertyTable()) { | |
591 | PropertyTable::iterator iter = transition->propertyTable()->begin(); | |
592 | PropertyTable::iterator end = transition->propertyTable()->end(); | |
593 | if (iter != end) | |
594 | transition->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true; | |
595 | for (; iter != end; ++iter) | |
596 | iter->attributes |= iter->attributes & Accessor ? DontDelete : (DontDelete | ReadOnly); | |
597 | } | |
598 | ||
599 | ASSERT(transition->hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties(vm)); | |
600 | ASSERT(transition->hasGetterSetterProperties() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties(vm)); | |
601 | transition->checkOffsetConsistency(); | |
602 | return transition; | |
603 | } | |
604 | ||
605 | // In future we may want to cache this transition. | |
606 | Structure* Structure::preventExtensionsTransition(VM& vm, Structure* structure) | |
607 | { | |
608 | Structure* transition = create(vm, structure); | |
609 | ||
610 | // Don't set m_offset, as one can not transition to this. | |
611 | ||
612 | DeferGC deferGC(vm.heap); | |
613 | structure->materializePropertyMapIfNecessary(vm, deferGC); | |
614 | transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); | |
615 | transition->m_offset = structure->m_offset; | |
616 | transition->m_preventExtensions = true; | |
617 | transition->pin(); | |
618 | ||
619 | transition->checkOffsetConsistency(); | |
620 | return transition; | |
621 | } | |
622 | ||
623 | PropertyTable* Structure::takePropertyTableOrCloneIfPinned(VM& vm) | |
624 | { | |
625 | DeferGC deferGC(vm.heap); | |
626 | materializePropertyMapIfNecessaryForPinning(vm, deferGC); | |
627 | ||
628 | if (m_isPinnedPropertyTable) | |
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); | |
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 | ||
655 | Structure* existingTransition; | |
656 | if (!structure->isDictionary() && (existingTransition = structure->m_transitionTable.get(0, attributes))) { | |
657 | ASSERT(existingTransition->m_attributesInPrevious == attributes); | |
658 | ASSERT(existingTransition->indexingTypeIncludingHistory() == indexingType); | |
659 | return existingTransition; | |
660 | } | |
661 | ||
662 | Structure* transition = create(vm, structure); | |
663 | transition->m_attributesInPrevious = attributes; | |
664 | transition->m_blob.setIndexingType(indexingType); | |
665 | transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm)); | |
666 | transition->m_offset = structure->m_offset; | |
667 | checkOffset(transition->m_offset, transition->inlineCapacity()); | |
668 | ||
669 | if (structure->isDictionary()) | |
670 | transition->pin(); | |
671 | else { | |
672 | ConcurrentJITLocker locker(structure->m_lock); | |
673 | structure->m_transitionTable.add(vm, transition); | |
674 | } | |
675 | transition->checkOffsetConsistency(); | |
676 | return transition; | |
677 | } | |
678 | ||
679 | // In future we may want to cache this property. | |
680 | bool Structure::isSealed(VM& vm) | |
681 | { | |
682 | if (isExtensible()) | |
683 | return false; | |
684 | ||
685 | DeferGC deferGC(vm.heap); | |
686 | materializePropertyMapIfNecessary(vm, deferGC); | |
687 | if (!propertyTable()) | |
688 | return true; | |
689 | ||
690 | PropertyTable::iterator end = propertyTable()->end(); | |
691 | for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { | |
692 | if ((iter->attributes & DontDelete) != DontDelete) | |
693 | return false; | |
694 | } | |
695 | return true; | |
696 | } | |
697 | ||
698 | // In future we may want to cache this property. | |
699 | bool Structure::isFrozen(VM& vm) | |
700 | { | |
701 | if (isExtensible()) | |
702 | return false; | |
703 | ||
704 | DeferGC deferGC(vm.heap); | |
705 | materializePropertyMapIfNecessary(vm, deferGC); | |
706 | if (!propertyTable()) | |
707 | return true; | |
708 | ||
709 | PropertyTable::iterator end = propertyTable()->end(); | |
710 | for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { | |
711 | if (!(iter->attributes & DontDelete)) | |
712 | return false; | |
713 | if (!(iter->attributes & (ReadOnly | Accessor))) | |
714 | return false; | |
715 | } | |
716 | return true; | |
717 | } | |
718 | ||
719 | Structure* Structure::flattenDictionaryStructure(VM& vm, JSObject* object) | |
720 | { | |
721 | checkOffsetConsistency(); | |
722 | ASSERT(isDictionary()); | |
723 | ||
724 | size_t beforeOutOfLineCapacity = this->outOfLineCapacity(); | |
725 | if (isUncacheableDictionary()) { | |
726 | ASSERT(propertyTable()); | |
727 | ||
728 | size_t propertyCount = propertyTable()->size(); | |
729 | ||
730 | // Holds our values compacted by insertion order. | |
731 | Vector<JSValue> values(propertyCount); | |
732 | ||
733 | // Copies out our values from their hashed locations, compacting property table offsets as we go. | |
734 | unsigned i = 0; | |
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); | |
740 | } | |
741 | ||
742 | // Copies in our values to their compacted locations. | |
743 | for (unsigned i = 0; i < propertyCount; i++) | |
744 | object->putDirect(vm, offsetForPropertyNumber(i, m_inlineCapacity), values[i]); | |
745 | ||
746 | propertyTable()->clearDeletedOffsets(); | |
747 | checkOffsetConsistency(); | |
748 | } | |
749 | ||
750 | m_dictionaryKind = NoneDictionaryKind; | |
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 | } | |
767 | ||
768 | return this; | |
769 | } | |
770 | ||
771 | PropertyOffset Structure::addPropertyWithoutTransition(VM& vm, PropertyName propertyName, unsigned attributes, JSCell* specificValue) | |
772 | { | |
773 | ASSERT(!enumerationCache()); | |
774 | ||
775 | if (m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) | |
776 | specificValue = 0; | |
777 | ||
778 | DeferGC deferGC(vm.heap); | |
779 | materializePropertyMapIfNecessaryForPinning(vm, deferGC); | |
780 | ||
781 | pin(); | |
782 | ||
783 | return putSpecificValue(vm, propertyName, attributes, specificValue); | |
784 | } | |
785 | ||
786 | PropertyOffset Structure::removePropertyWithoutTransition(VM& vm, PropertyName propertyName) | |
787 | { | |
788 | ASSERT(isUncacheableDictionary()); | |
789 | ASSERT(!enumerationCache()); | |
790 | ||
791 | DeferGC deferGC(vm.heap); | |
792 | materializePropertyMapIfNecessaryForPinning(vm, deferGC); | |
793 | ||
794 | pin(); | |
795 | return remove(propertyName); | |
796 | } | |
797 | ||
798 | void Structure::pin() | |
799 | { | |
800 | ASSERT(propertyTable()); | |
801 | m_isPinnedPropertyTable = true; | |
802 | clearPreviousID(); | |
803 | m_nameInPrevious.clear(); | |
804 | } | |
805 | ||
806 | void Structure::allocateRareData(VM& vm) | |
807 | { | |
808 | ASSERT(!m_hasRareData); | |
809 | StructureRareData* rareData = StructureRareData::create(vm, previous()); | |
810 | m_previousOrRareData.set(vm, this, rareData); | |
811 | m_hasRareData = true; | |
812 | ASSERT(m_hasRareData); | |
813 | } | |
814 | ||
815 | void Structure::cloneRareDataFrom(VM& vm, const Structure* other) | |
816 | { | |
817 | ASSERT(!m_hasRareData); | |
818 | ASSERT(other->m_hasRareData); | |
819 | StructureRareData* newRareData = StructureRareData::clone(vm, other->rareData()); | |
820 | m_previousOrRareData.set(vm, this, newRareData); | |
821 | m_hasRareData = true; | |
822 | ASSERT(m_hasRareData); | |
823 | } | |
824 | ||
825 | #if DUMP_PROPERTYMAP_STATS | |
826 | ||
827 | PropertyMapHashTableStats* propertyMapHashTableStats = 0; | |
828 | ||
829 | struct PropertyMapStatisticsExitLogger { | |
830 | PropertyMapStatisticsExitLogger(); | |
831 | ~PropertyMapStatisticsExitLogger(); | |
832 | }; | |
833 | ||
834 | DEFINE_GLOBAL_FOR_LOGGING(PropertyMapStatisticsExitLogger, logger, ); | |
835 | ||
836 | PropertyMapStatisticsExitLogger::PropertyMapStatisticsExitLogger() | |
837 | { | |
838 | propertyMapHashTableStats = adoptPtr(new PropertyMapHashTableStats()).leakPtr(); | |
839 | } | |
840 | ||
841 | PropertyMapStatisticsExitLogger::~PropertyMapStatisticsExitLogger() | |
842 | { | |
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()); | |
854 | } | |
855 | ||
856 | #endif | |
857 | ||
858 | #if !DO_PROPERTYMAP_CONSTENCY_CHECK | |
859 | ||
860 | inline void Structure::checkConsistency() | |
861 | { | |
862 | checkOffsetConsistency(); | |
863 | } | |
864 | ||
865 | #endif | |
866 | ||
867 | PropertyTable* Structure::copyPropertyTable(VM& vm) | |
868 | { | |
869 | if (!propertyTable()) | |
870 | return 0; | |
871 | return PropertyTable::clone(vm, *propertyTable().get()); | |
872 | } | |
873 | ||
874 | PropertyTable* Structure::copyPropertyTableForPinning(VM& vm) | |
875 | { | |
876 | if (propertyTable()) | |
877 | return PropertyTable::clone(vm, *propertyTable().get()); | |
878 | return PropertyTable::create(vm, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity)); | |
879 | } | |
880 | ||
881 | PropertyOffset Structure::getConcurrently(VM&, StringImpl* uid, unsigned& attributes, JSCell*& specificValue) | |
882 | { | |
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; | |
912 | } | |
913 | ||
914 | bool Structure::despecifyFunction(VM& vm, PropertyName propertyName) | |
915 | { | |
916 | DeferGC deferGC(vm.heap); | |
917 | materializePropertyMapIfNecessary(vm, deferGC); | |
918 | if (!propertyTable()) | |
919 | return false; | |
920 | ||
921 | PropertyMapEntry* entry = propertyTable()->get(propertyName.uid()); | |
922 | if (!entry) | |
923 | return false; | |
924 | ||
925 | ASSERT(entry->specificValue); | |
926 | entry->specificValue.clear(); | |
927 | return true; | |
928 | } | |
929 | ||
930 | void Structure::despecifyAllFunctions(VM& vm) | |
931 | { | |
932 | DeferGC deferGC(vm.heap); | |
933 | materializePropertyMapIfNecessary(vm, deferGC); | |
934 | if (!propertyTable()) | |
935 | return; | |
936 | ||
937 | PropertyTable::iterator end = propertyTable()->end(); | |
938 | for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) | |
939 | iter->specificValue.clear(); | |
940 | } | |
941 | ||
942 | PropertyOffset Structure::putSpecificValue(VM& vm, PropertyName propertyName, unsigned attributes, JSCell* specificValue) | |
943 | { | |
944 | GCSafeConcurrentJITLocker locker(m_lock, vm.heap); | |
945 | ||
946 | ASSERT(!JSC::isValidOffset(get(vm, propertyName))); | |
947 | ||
948 | checkConsistency(); | |
949 | if (attributes & DontEnum) | |
950 | m_hasNonEnumerableProperties = true; | |
951 | ||
952 | StringImpl* rep = propertyName.uid(); | |
953 | ||
954 | if (!propertyTable()) | |
955 | createPropertyMap(locker, vm); | |
956 | ||
957 | PropertyOffset newOffset = propertyTable()->nextOffset(m_inlineCapacity); | |
958 | ||
959 | propertyTable()->add(PropertyMapEntry(vm, propertyTable().get(), rep, newOffset, attributes, specificValue), m_offset, PropertyTable::PropertyOffsetMayChange); | |
960 | ||
961 | checkConsistency(); | |
962 | return newOffset; | |
963 | } | |
964 | ||
965 | PropertyOffset Structure::remove(PropertyName propertyName) | |
966 | { | |
967 | ConcurrentJITLocker locker(m_lock); | |
968 | ||
969 | checkConsistency(); | |
970 | ||
971 | StringImpl* rep = propertyName.uid(); | |
972 | ||
973 | if (!propertyTable()) | |
974 | return invalidOffset; | |
975 | ||
976 | PropertyTable::find_iterator position = propertyTable()->find(rep); | |
977 | if (!position.first) | |
978 | return invalidOffset; | |
979 | ||
980 | PropertyOffset offset = position.first->offset; | |
981 | ||
982 | propertyTable()->remove(position); | |
983 | propertyTable()->addDeletedOffset(offset); | |
984 | ||
985 | checkConsistency(); | |
986 | return offset; | |
987 | } | |
988 | ||
989 | void Structure::createPropertyMap(const GCSafeConcurrentJITLocker&, VM& vm, unsigned capacity) | |
990 | { | |
991 | ASSERT(!propertyTable()); | |
992 | ||
993 | checkConsistency(); | |
994 | propertyTable().set(vm, this, PropertyTable::create(vm, capacity)); | |
995 | } | |
996 | ||
997 | void Structure::getPropertyNamesFromStructure(VM& vm, PropertyNameArray& propertyNames, EnumerationMode mode) | |
998 | { | |
999 | DeferGC deferGC(vm.heap); | |
1000 | materializePropertyMapIfNecessary(vm, deferGC); | |
1001 | if (!propertyTable()) | |
1002 | return; | |
1003 | ||
1004 | bool knownUnique = !propertyNames.size(); | |
1005 | ||
1006 | PropertyTable::iterator end = propertyTable()->end(); | |
1007 | for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { | |
1008 | ASSERT(m_hasNonEnumerableProperties || !(iter->attributes & DontEnum)); | |
1009 | if (!iter->key->isEmptyUnique() && (!(iter->attributes & DontEnum) || mode == IncludeDontEnumProperties)) { | |
1010 | if (knownUnique) | |
1011 | propertyNames.addKnownUnique(iter->key); | |
1012 | else | |
1013 | propertyNames.add(iter->key); | |
1014 | } | |
1015 | } | |
1016 | } | |
1017 | ||
1018 | JSValue Structure::prototypeForLookup(CodeBlock* codeBlock) const | |
1019 | { | |
1020 | return prototypeForLookup(codeBlock->globalObject()); | |
1021 | } | |
1022 | ||
1023 | void Structure::visitChildren(JSCell* cell, SlotVisitor& visitor) | |
1024 | { | |
1025 | Structure* thisObject = jsCast<Structure*>(cell); | |
1026 | ASSERT_GC_OBJECT_INHERITS(thisObject, info()); | |
1027 | ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); | |
1028 | ||
1029 | JSCell::visitChildren(thisObject, visitor); | |
1030 | visitor.append(&thisObject->m_globalObject); | |
1031 | if (!thisObject->isObject()) | |
1032 | thisObject->m_cachedPrototypeChain.clear(); | |
1033 | else { | |
1034 | visitor.append(&thisObject->m_prototype); | |
1035 | visitor.append(&thisObject->m_cachedPrototypeChain); | |
1036 | } | |
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 | ||
1058 | current = prototype.asCell()->structure(vm); | |
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; | |
1070 | } | |
1071 | } | |
1072 | ||
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 | ||
1137 | #if DO_PROPERTYMAP_CONSTENCY_CHECK | |
1138 | ||
1139 | void PropertyTable::checkConsistency() | |
1140 | { | |
1141 | checkOffsetConsistency(); | |
1142 | ASSERT(m_indexSize >= PropertyTable::MinimumTableSize); | |
1143 | ASSERT(m_indexMask); | |
1144 | ASSERT(m_indexSize == m_indexMask + 1); | |
1145 | ASSERT(!(m_indexSize & m_indexMask)); | |
1146 | ||
1147 | ASSERT(m_keyCount <= m_indexSize / 2); | |
1148 | ASSERT(m_keyCount + m_deletedCount <= m_indexSize / 2); | |
1149 | ASSERT(m_deletedCount <= m_indexSize / 4); | |
1150 | ||
1151 | unsigned indexCount = 0; | |
1152 | unsigned deletedIndexCount = 0; | |
1153 | for (unsigned a = 0; a != m_indexSize; ++a) { | |
1154 | unsigned entryIndex = m_index[a]; | |
1155 | if (entryIndex == PropertyTable::EmptyEntryIndex) | |
1156 | continue; | |
1157 | if (entryIndex == deletedEntryIndex()) { | |
1158 | ++deletedIndexCount; | |
1159 | continue; | |
1160 | } | |
1161 | ASSERT(entryIndex < deletedEntryIndex()); | |
1162 | ASSERT(entryIndex - 1 <= usedCount()); | |
1163 | ++indexCount; | |
1164 | ||
1165 | for (unsigned b = a + 1; b != m_indexSize; ++b) | |
1166 | ASSERT(m_index[b] != entryIndex); | |
1167 | } | |
1168 | ASSERT(indexCount == m_keyCount); | |
1169 | ASSERT(deletedIndexCount == m_deletedCount); | |
1170 | ||
1171 | ASSERT(!table()[deletedEntryIndex() - 1].key); | |
1172 | ||
1173 | unsigned nonEmptyEntryCount = 0; | |
1174 | for (unsigned c = 0; c < usedCount(); ++c) { | |
1175 | StringImpl* rep = table()[c].key; | |
1176 | if (rep == PROPERTY_MAP_DELETED_ENTRY_KEY) | |
1177 | continue; | |
1178 | ++nonEmptyEntryCount; | |
1179 | unsigned i = rep->existingHash(); | |
1180 | unsigned k = 0; | |
1181 | unsigned entryIndex; | |
1182 | while (1) { | |
1183 | entryIndex = m_index[i & m_indexMask]; | |
1184 | ASSERT(entryIndex != PropertyTable::EmptyEntryIndex); | |
1185 | if (rep == table()[entryIndex - 1].key) | |
1186 | break; | |
1187 | if (k == 0) | |
1188 | k = 1 | doubleHash(rep->existingHash()); | |
1189 | i += k; | |
1190 | } | |
1191 | ASSERT(entryIndex == c + 1); | |
1192 | } | |
1193 | ||
1194 | ASSERT(nonEmptyEntryCount == m_keyCount); | |
1195 | } | |
1196 | ||
1197 | void Structure::checkConsistency() | |
1198 | { | |
1199 | if (!propertyTable()) | |
1200 | return; | |
1201 | ||
1202 | if (!m_hasNonEnumerableProperties) { | |
1203 | PropertyTable::iterator end = propertyTable()->end(); | |
1204 | for (PropertyTable::iterator iter = propertyTable()->begin(); iter != end; ++iter) { | |
1205 | ASSERT(!(iter->attributes & DontEnum)); | |
1206 | } | |
1207 | } | |
1208 | ||
1209 | propertyTable()->checkConsistency(); | |
1210 | } | |
1211 | ||
1212 | #endif // DO_PROPERTYMAP_CONSTENCY_CHECK | |
1213 | ||
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 | ||
1225 | } // namespace JSC |