Structure.cpp 35 KB

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