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