JavascriptArray.inl 76 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. #define Assert_FailFast(x) if (!(x)) { Assert(x); Js::Throw::FatalInternalError(); }
  7. namespace Js
  8. {
  9. //
  10. // Walks all the nodes in this BTree in sorted order.
  11. //
  12. template<typename Func>
  13. void SegmentBTree::Walk(Func& func) const
  14. {
  15. if (!IsLeaf())
  16. {
  17. children[0].Walk(func);
  18. }
  19. for (unsigned int i = 0; i < segmentCount; i++)
  20. {
  21. Assert(keys[i] == segments[i]->left);
  22. func(segments[i]);
  23. if (!IsLeaf())
  24. {
  25. children[i + 1].Walk(func);
  26. }
  27. }
  28. }
  29. template <typename Fn>
  30. SparseArraySegmentBase *
  31. JavascriptArray::ForEachSegment(Fn fn) const
  32. {
  33. return ForEachSegment(this->head, fn);
  34. }
  35. template <typename Fn>
  36. SparseArraySegmentBase *
  37. JavascriptArray::ForEachSegment(SparseArraySegmentBase * segment, Fn fn)
  38. {
  39. DebugOnly(uint32 lastindex = segment? segment->left : 0);
  40. SparseArraySegmentBase * current = segment;
  41. while (current)
  42. {
  43. // Verify that all the segment are sorted
  44. Assert(current->left >= lastindex);
  45. if (fn(current))
  46. {
  47. break;
  48. }
  49. DebugOnly(lastindex = current->left + current->length);
  50. current = current->next;
  51. }
  52. return current;
  53. }
  54. //
  55. // Link prev and current. If prev is NULL, make current the head segment.
  56. //
  57. template<>
  58. inline void JavascriptArray::LinkSegments(SparseArraySegment<int>* prev, SparseArraySegment<int>* current)
  59. {
  60. if (prev && prev->next == nullptr && SparseArraySegmentBase::IsLeafSegment(prev, this->GetScriptContext()->GetRecycler()))
  61. {
  62. prev = this->ReallocNonLeafSegment(prev, current);
  63. }
  64. else
  65. {
  66. LinkSegmentsCommon(prev, current);
  67. }
  68. }
  69. template<>
  70. inline void JavascriptArray::LinkSegments(SparseArraySegment<double>* prev, SparseArraySegment<double>* current)
  71. {
  72. if (prev && prev->next == nullptr && SparseArraySegmentBase::IsLeafSegment(prev, this->GetScriptContext()->GetRecycler()))
  73. {
  74. prev = this->ReallocNonLeafSegment(prev, current);
  75. }
  76. else
  77. {
  78. LinkSegmentsCommon(prev, current);
  79. }
  80. }
  81. template<typename T>
  82. inline void JavascriptArray::LinkSegments(SparseArraySegment<T>* prev, SparseArraySegment<T>* current)
  83. {
  84. LinkSegmentsCommon(prev, current);
  85. }
  86. template<typename T>
  87. inline SparseArraySegment<T>* JavascriptArray::ReallocNonLeafSegment(SparseArraySegment<T> *seg, SparseArraySegmentBase* nextSeg, bool forceNonLeaf)
  88. {
  89. // Find the segment prior to seg.
  90. SparseArraySegmentBase *prior = nullptr;
  91. if (seg != this->head)
  92. {
  93. for (prior = this->head; prior->next != seg; prior = prior->next)
  94. {
  95. Assert(prior->next);
  96. }
  97. }
  98. bool isInlineSegment = JavascriptArray::IsInlineSegment(seg, this);
  99. SparseArraySegment<T> *newSeg = nullptr;
  100. Recycler *recycler = this->GetScriptContext()->GetRecycler();
  101. if (forceNonLeaf)
  102. {
  103. newSeg = SparseArraySegment<T>::template AllocateSegmentImpl<false /*isLeaf*/>(recycler, seg->left, seg->length, nextSeg);
  104. }
  105. else
  106. {
  107. newSeg = SparseArraySegment<T>::AllocateSegment(recycler, seg->left, seg->length, nextSeg);
  108. }
  109. CopyArray(newSeg->elements, seg->length, seg->elements, seg->length);
  110. LinkSegmentsCommon(prior, newSeg);
  111. LinkSegmentsCommon(newSeg, nextSeg);
  112. if (GetLastUsedSegment() == seg)
  113. {
  114. SetLastUsedSegment(newSeg);
  115. }
  116. SegmentBTree * segmentMap = GetSegmentMap();
  117. if (segmentMap)
  118. {
  119. segmentMap->SwapSegment(seg->left, seg, newSeg);
  120. }
  121. if (isInlineSegment)
  122. {
  123. this->ClearElements(seg, 0);
  124. }
  125. return newSeg;
  126. }
  127. /*static*/
  128. template<typename T, uint InlinePropertySlots>
  129. inline SparseArraySegment<typename T::TElement> *JavascriptArray::InitArrayAndHeadSegment(
  130. T *const array,
  131. const uint32 length,
  132. const uint32 size,
  133. const bool wasZeroAllocated)
  134. {
  135. Assert(!array->HasSegmentMap());
  136. SparseArraySegment<typename T::TElement>* head =
  137. DetermineInlineHeadSegmentPointer<T, InlinePropertySlots, false>(array);
  138. if(wasZeroAllocated)
  139. {
  140. AssertOrFailFast(size <= SparseArraySegmentBase::INLINE_CHUNK_SIZE);
  141. if(length != 0)
  142. {
  143. head->length = length;
  144. }
  145. head->size = size;
  146. head->CheckLengthvsSize();
  147. }
  148. else
  149. {
  150. new(head) SparseArraySegment<typename T::TElement>(0, length, size);
  151. }
  152. array->SetHeadAndLastUsedSegment(head);
  153. array->SetHasNoMissingValues();
  154. return head;
  155. }
  156. template<typename unitType, typename className>
  157. inline className * JavascriptArray::New(Recycler * recycler, DynamicType * type)
  158. {
  159. size_t allocationPlusSize;
  160. uint alignedInlineElementSlots;
  161. DetermineAllocationSizeForArrayObjects<className, 0>(
  162. SparseArraySegmentBase::SMALL_CHUNK_SIZE,
  163. &allocationPlusSize,
  164. &alignedInlineElementSlots);
  165. return RecyclerNewPlusZ(recycler, allocationPlusSize, className, type, alignedInlineElementSlots);
  166. }
  167. /*static*/
  168. template<typename unitType, typename className, uint inlineSlots>
  169. className* JavascriptArray::New(uint32 length, DynamicType* arrayType, Recycler* recycler)
  170. {
  171. CompileAssert(static_cast<PropertyIndex>(inlineSlots) == inlineSlots);
  172. Assert(DynamicTypeHandler::RoundUpInlineSlotCapacity(static_cast<PropertyIndex>(inlineSlots)) == inlineSlots);
  173. if(length > SparseArraySegmentBase::HEAD_CHUNK_SIZE)
  174. {
  175. // Use empty segment until we try to store something. Call AllocateHead() at that point.
  176. return RecyclerNew(recycler, className, length, arrayType);
  177. }
  178. size_t allocationPlusSize;
  179. uint alignedInlineElementSlots;
  180. className* array;
  181. DetermineAllocationSizeForArrayObjects<className, inlineSlots>(length, &allocationPlusSize, &alignedInlineElementSlots);
  182. array = RecyclerNewPlusZ(recycler, allocationPlusSize, className, length, arrayType);
  183. SparseArraySegment<unitType> *head =
  184. InitArrayAndHeadSegment<className, inlineSlots>(array, 0, alignedInlineElementSlots, true);
  185. head->FillSegmentBuffer(0, alignedInlineElementSlots);
  186. return array;
  187. }
  188. //
  189. // Allocates the segment inline up to the length of SparseArraySegmentBase::INLINE_CHUNK_SIZE. The downside of having the segment
  190. // inline is that the segment space will never get freed unless the Array is collected.
  191. //
  192. /*static*/
  193. template<typename unitType, typename className, uint inlineSlots>
  194. className* JavascriptArray::NewLiteral(uint32 length, DynamicType* arrayType, Recycler* recycler)
  195. {
  196. CompileAssert(static_cast<PropertyIndex>(inlineSlots) == inlineSlots);
  197. Assert(DynamicTypeHandler::RoundUpInlineSlotCapacity(static_cast<PropertyIndex>(inlineSlots)) == inlineSlots);
  198. className* array;
  199. if(HasInlineHeadSegment(length))
  200. {
  201. size_t allocationPlusSize;
  202. uint alignedInlineElementSlots;
  203. if(!length)
  204. {
  205. DetermineAllocationSize<className, inlineSlots>(
  206. SparseArraySegmentBase::SMALL_CHUNK_SIZE,
  207. &allocationPlusSize,
  208. &alignedInlineElementSlots);
  209. }
  210. else
  211. {
  212. DetermineAllocationSize<className, inlineSlots>(length, &allocationPlusSize, &alignedInlineElementSlots);
  213. }
  214. // alignedInlineElementSlots is actually the 'size' of the segment. The size of the segment should not be greater than InlineHead segment limit, otherwise the inline
  215. // segment may not be interpreted as inline segment if the length extends to the size.
  216. // the size could increase because of allignment.
  217. // Update the size so that it does not exceed SparseArraySegmentBase::INLINE_CHUNK_SIZE.
  218. uint inlineChunkSize = SparseArraySegmentBase::INLINE_CHUNK_SIZE;
  219. uint size = min(alignedInlineElementSlots, inlineChunkSize);
  220. array = RecyclerNewPlusZ(recycler, allocationPlusSize, className, length, arrayType);
  221. // An new array's head segment length is initialized to zero despite the array length being nonzero because the segment
  222. // doesn't have any values to begin with. An array literal though, is initialized with special op-codes that just store
  223. // the values and don't update the length, so update the length here.
  224. //
  225. // An array literal is also guaranteed to be fully initialized, so even though the head segment currently will have
  226. // missing values (after this update to length), it won't have missing values once the initialization is complete, so
  227. // maintain the state saying "does not have missing values". Furthermore, since the new array literal is not assigned to
  228. // a variable until it is fully initialized, there is no way for script code to use the array while it still has missing
  229. // values.
  230. SparseArraySegment<unitType> *head =
  231. InitArrayAndHeadSegment<className, inlineSlots>(array, length, size, true);
  232. head->FillSegmentBuffer(length, size);
  233. Assert(array->HasNoMissingValues());
  234. return array;
  235. }
  236. size_t allocationPlusSize;
  237. DetermineAllocationSize<className, inlineSlots>(0, &allocationPlusSize);
  238. array = RecyclerNewPlusZ(recycler, allocationPlusSize, className, length, arrayType);
  239. SparseArraySegment<unitType> *seg = SparseArraySegment<unitType>::AllocateLiteralHeadSegment(recycler, length);
  240. array->SetHeadAndLastUsedSegment(seg);
  241. array->SetHasNoMissingValues();
  242. // An new array's head segment length is initialized to zero despite the array length being nonzero because the segment
  243. // doesn't have any values to begin with. An array literal though, is initialized with special op-codes that just store
  244. // the values and don't update the length, so update the length here.
  245. //
  246. // An array literal is also guaranteed to be fully initialized, so even though the head segment currently will have
  247. // missing values (after this update to length), it won't have missing values once the initialization is complete, so
  248. // maintain the state saying "does not have missing values". Furthermore, since the new array literal is not assigned to
  249. // a variable until it is fully initialized, there is no way for script code to use the array while it still has missing
  250. // values.
  251. array->head->length = length;
  252. array->head->CheckLengthvsSize();
  253. return array;
  254. }
  255. #if ENABLE_COPYONACCESS_ARRAY
  256. //
  257. // Allocates the segment inline up to the length of SparseArraySegmentBase::INLINE_CHUNK_SIZE. The downside of having the segment
  258. // inline is that the segment space will never get freed unless the Array is collected.
  259. //
  260. /*static*/
  261. template<typename unitType, typename className, uint inlineSlots>
  262. className* JavascriptArray::NewCopyOnAccessLiteral(DynamicType* arrayType, ArrayCallSiteInfo *arrayInfo, FunctionBody *functionBody, const Js::AuxArray<int32> *ints, Recycler* recycler)
  263. {
  264. CompileAssert(static_cast<PropertyIndex>(inlineSlots) == inlineSlots);
  265. Assert(DynamicTypeHandler::RoundUpInlineSlotCapacity(static_cast<PropertyIndex>(inlineSlots)) == inlineSlots);
  266. Assert(arrayInfo->IsNativeIntArray());
  267. className* array = RecyclerNewZ(recycler, JavascriptCopyOnAccessNativeIntArray, ints->count, arrayType);
  268. JavascriptLibrary *lib = functionBody->GetScriptContext()->GetLibrary();
  269. SparseArraySegment<unitType> *seg;
  270. if (JavascriptLibrary::IsCachedCopyOnAccessArrayCallSite(functionBody->GetScriptContext()->GetLibrary() , arrayInfo))
  271. {
  272. seg = lib->cacheForCopyOnAccessArraySegments->GetSegmentByIndex(arrayInfo->copyOnAccessArrayCacheIndex);
  273. }
  274. else
  275. {
  276. seg = SparseArraySegment<unitType>::AllocateLiteralHeadSegment(recycler, ints->count);
  277. }
  278. if (!JavascriptLibrary::IsCachedCopyOnAccessArrayCallSite(lib, arrayInfo))
  279. {
  280. JavascriptOperators::AddIntsToArraySegment(seg, ints);
  281. arrayInfo->copyOnAccessArrayCacheIndex = lib->cacheForCopyOnAccessArraySegments->AddSegment(seg);
  282. }
  283. array->SetHeadAndLastUsedSegment(reinterpret_cast<SparseArraySegmentBase *>(arrayInfo->copyOnAccessArrayCacheIndex)); // storing index in head on purpose: expect AV if treated as other array objects
  284. #if ENABLE_DEBUG_CONFIG_OPTIONS
  285. if (Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::CopyOnAccessArrayPhase))
  286. {
  287. Output::Print(_u("Create copy-on-access array: func(#%2d) index(%d) length(%d)\n"),
  288. functionBody->GetFunctionNumber(), lib->cacheForCopyOnAccessArraySegments->GetCount(), ints->count);
  289. Output::Flush();
  290. }
  291. #endif
  292. return array;
  293. }
  294. #endif
  295. template<class T, uint InlinePropertySlots>
  296. inline T *JavascriptArray::New(
  297. void *const stackAllocationPointer,
  298. const uint32 length,
  299. DynamicType *const arrayType)
  300. {
  301. Assert(arrayType);
  302. if(stackAllocationPointer)
  303. {
  304. bool isSufficientSpaceForInlinePropertySlots;
  305. const uint availableInlineElementSlots =
  306. DetermineAvailableInlineElementSlots<T, InlinePropertySlots>(
  307. T::StackAllocationSize,
  308. &isSufficientSpaceForInlinePropertySlots);
  309. if(isSufficientSpaceForInlinePropertySlots)
  310. {
  311. T *const array = new(stackAllocationPointer) T(length, arrayType);
  312. if(length <= availableInlineElementSlots)
  313. {
  314. SparseArraySegment<typename T::TElement> *const head =
  315. InitArrayAndHeadSegment<T, InlinePropertySlots>(array, 0, availableInlineElementSlots, false);
  316. head->FillSegmentBuffer(0, availableInlineElementSlots);
  317. }
  318. else
  319. {
  320. // Not enough room to allocate all required element slots inline. Leave the head segment as the empty
  321. // segment and let it be allocated as necessary.
  322. }
  323. Assert(array->HasNoMissingValues());
  324. return array;
  325. }
  326. }
  327. return New<typename T::TElement, T, InlinePropertySlots>(length, arrayType, arrayType->GetRecycler());
  328. }
  329. template<class T, uint InlinePropertySlots>
  330. inline T *JavascriptArray::NewLiteral(
  331. void *const stackAllocationPointer,
  332. const uint32 length,
  333. DynamicType *const arrayType)
  334. {
  335. Assert(arrayType);
  336. if(stackAllocationPointer)
  337. {
  338. bool isSufficientSpaceForInlinePropertySlots;
  339. const uint availableInlineElementSlots =
  340. DetermineAvailableInlineElementSlots<T, InlinePropertySlots>(
  341. T::StackAllocationSize,
  342. &isSufficientSpaceForInlinePropertySlots);
  343. if(isSufficientSpaceForInlinePropertySlots)
  344. {
  345. T *const array = new(stackAllocationPointer) T(length, arrayType);
  346. if(length <= availableInlineElementSlots)
  347. {
  348. SparseArraySegment<typename T::TElement> *const head =
  349. InitArrayAndHeadSegment<T, InlinePropertySlots>(array, length, availableInlineElementSlots, false);
  350. head->FillSegmentBuffer(length, availableInlineElementSlots);
  351. Assert(array->HasNoMissingValues());
  352. return array;
  353. }
  354. // Not enough room to allocate all required element slots inline. Allocate the head segment separately.
  355. SparseArraySegment<typename T::TElement> *const head =
  356. SparseArraySegment<typename T::TElement>::AllocateLiteralHeadSegment(arrayType->GetRecycler(), length);
  357. array->SetHeadAndLastUsedSegment(head);
  358. array->SetHasNoMissingValues();
  359. return array;
  360. }
  361. }
  362. return NewLiteral<typename T::TElement, T, InlinePropertySlots>(length, arrayType, arrayType->GetRecycler());
  363. }
  364. template <>
  365. inline void JavascriptArray::DirectSetItemAt<int32>(uint32 itemIndex, int32 newValue)
  366. {
  367. Assert_FailFast(this->GetTypeId() == TypeIds_NativeIntArray);
  368. Assert(itemIndex < InvalidIndex); // Otherwise the code below could overflow and set length = 0
  369. SparseArraySegment<int32> *seg = (SparseArraySegment<int32>*)this->GetLastUsedSegment();
  370. uint32 offset = itemIndex - seg->left;
  371. if(itemIndex >= seg->left && offset < seg->size)
  372. {
  373. DirectSetItemInLastUsedSegmentAt(offset, newValue);
  374. return;
  375. }
  376. DirectSetItem_Full(itemIndex, newValue);
  377. }
  378. template <>
  379. inline void JavascriptArray::DirectSetItemAt<double>(uint32 itemIndex, double newValue)
  380. {
  381. Assert_FailFast(this->GetTypeId() == TypeIds_NativeFloatArray);
  382. Assert(itemIndex < InvalidIndex); // Otherwise the code below could overflow and set length = 0
  383. SparseArraySegment<double> *seg = (SparseArraySegment<double>*)this->GetLastUsedSegment();
  384. uint32 offset = itemIndex - seg->left;
  385. if (itemIndex >= seg->left && offset < seg->size)
  386. {
  387. DirectSetItemInLastUsedSegmentAt(offset, newValue);
  388. return;
  389. }
  390. DirectSetItem_Full(itemIndex, newValue);
  391. }
  392. template <>
  393. inline void JavascriptArray::DirectSetItemAt<Var>(uint32 itemIndex, Var newValue)
  394. {
  395. Assert_FailFast(this->GetTypeId() == TypeIds_Array || this->GetTypeId() == TypeIds_ES5Array);
  396. Assert(itemIndex < InvalidIndex); // Otherwise the code below could overflow and set length = 0
  397. SparseArraySegment<Var> *seg = (SparseArraySegment<Var>*)this->GetLastUsedSegment();
  398. uint32 offset = itemIndex - seg->left;
  399. if (itemIndex >= seg->left && offset < seg->size)
  400. {
  401. DirectSetItemInLastUsedSegmentAt(offset, newValue);
  402. return;
  403. }
  404. DirectSetItem_Full(itemIndex, newValue);
  405. }
  406. template <typename T>
  407. inline void JavascriptArray::DirectSetItemAt(uint32 itemIndex, T newValue)
  408. {
  409. Assert(itemIndex < InvalidIndex); // Otherwise the code below could overflow and set length = 0
  410. SparseArraySegment<T> *seg = (SparseArraySegment<T>*)this->GetLastUsedSegment();
  411. uint32 offset = itemIndex - seg->left;
  412. if (itemIndex >= seg->left && offset < seg->size)
  413. {
  414. DirectSetItemInLastUsedSegmentAt(offset, newValue);
  415. return;
  416. }
  417. DirectSetItem_Full(itemIndex, newValue);
  418. }
  419. inline void JavascriptArray::GenericDirectSetItemAt(const uint32 index, Var newValue)
  420. {
  421. newValue = CrossSite::MarshalVar(this->GetScriptContext(), newValue);
  422. this->DirectSetItemAt(index, newValue);
  423. }
  424. template<typename T>
  425. inline void JavascriptArray::DirectSetItemInLastUsedSegmentAt(const uint32 offset, const T newValue)
  426. {
  427. Assert(!SparseArraySegment<T>::IsMissingItem(&newValue));
  428. SparseArraySegment<T> *const seg = (SparseArraySegment<T>*)GetLastUsedSegment();
  429. Assert(seg);
  430. Assert(offset < seg->size);
  431. Assert(!(HasNoMissingValues() &&
  432. offset < seg->length &&
  433. SparseArraySegment<T>::IsMissingItem(&seg->elements[offset]) &&
  434. seg == head));
  435. const bool scanForMissingValues = NeedScanForMissingValuesUponSetItem(seg, offset);
  436. DebugOnly(VerifyNotNeedMarshal(newValue));
  437. seg->elements[offset] = newValue;
  438. if (offset >= seg->length)
  439. {
  440. if(offset > seg->length && seg == head)
  441. {
  442. SetHasNoMissingValues(false);
  443. }
  444. seg->length = offset + 1;
  445. seg->CheckLengthvsSize();
  446. const uint32 itemIndex = seg->left + offset;
  447. if (this->length <= itemIndex)
  448. {
  449. this->length = itemIndex + 1;
  450. }
  451. }
  452. else if(scanForMissingValues)
  453. {
  454. ScanForMissingValues<T>();
  455. }
  456. }
  457. #if ENABLE_PROFILE_INFO
  458. template<typename T>
  459. inline void JavascriptArray::DirectProfiledSetItemInHeadSegmentAt(
  460. const uint32 offset,
  461. const T newValue,
  462. StElemInfo *const stElemInfo)
  463. {
  464. Assert(!SparseArraySegment<T>::IsMissingItem(&newValue));
  465. SparseArraySegment<T> *const seg = SparseArraySegment<T>::From(head);
  466. Assert(seg);
  467. Assert(offset < seg->size);
  468. Assert(!(HasNoMissingValues() &&
  469. offset < seg->length &&
  470. SparseArraySegment<T>::IsMissingItem(&seg->elements[offset])));
  471. Assert(stElemInfo);
  472. stElemInfo->filledMissingValue = offset < seg->length && SparseArraySegment<T>::IsMissingItem(&seg->elements[offset]);
  473. const bool scanForMissingValues = NeedScanForMissingValuesUponSetItem(seg, offset);
  474. DebugOnly(VerifyNotNeedMarshal(newValue));
  475. seg->elements[offset] = newValue;
  476. if (offset >= seg->length)
  477. {
  478. if(offset > seg->length)
  479. {
  480. SetHasNoMissingValues(false);
  481. }
  482. seg->length = offset + 1;
  483. seg->CheckLengthvsSize();
  484. const uint32 itemIndex = seg->left + offset;
  485. if (this->length <= itemIndex)
  486. {
  487. this->length = itemIndex + 1;
  488. }
  489. }
  490. else if(scanForMissingValues)
  491. {
  492. ScanForMissingValues<T>();
  493. }
  494. }
  495. #endif
  496. template<typename T>
  497. inline BOOL JavascriptArray::DirectGetItemAt(uint32 index, T* outVal)
  498. {
  499. #ifdef VALIDATE_ARRAY
  500. ValidateArray();
  501. #endif
  502. if (index >= length)
  503. {
  504. return false;
  505. }
  506. #ifdef VALIDATE_ARRAY
  507. T v_btree = NULL;
  508. SparseArraySegmentBase* seg_btree = nullptr;
  509. bool first_pass = true;
  510. #endif
  511. SparseArraySegmentBase* nextSeg;
  512. SegmentBTreeRoot * segmentMap = GetSegmentMap();
  513. if (segmentMap)
  514. {
  515. SparseArraySegmentBase* matchOrNextSeg;
  516. segmentMap->Find(index, nextSeg, matchOrNextSeg);
  517. if (!nextSeg)
  518. {
  519. nextSeg = matchOrNextSeg;
  520. }
  521. }
  522. else
  523. {
  524. #ifdef VALIDATE_ARRAY
  525. SECOND_PASS:
  526. #endif
  527. nextSeg = this->GetBeginLookupSegment(index, false);
  528. }
  529. uint probeCost = 0;
  530. while (nextSeg != nullptr && nextSeg->left <= index)
  531. {
  532. uint32 limit = nextSeg->left + nextSeg->length;
  533. if (index < limit)
  534. {
  535. const T * v = AddressOf(((SparseArraySegment<T>*)nextSeg)->elements[index - nextSeg->left]);
  536. this->SetLastUsedSegment(nextSeg);
  537. #ifdef VALIDATE_ARRAY
  538. Assert(segmentMap == GetSegmentMap());
  539. if (segmentMap && first_pass)
  540. {
  541. v_btree = *v;
  542. seg_btree= nextSeg;
  543. first_pass = false;
  544. goto SECOND_PASS;
  545. }
  546. else if (segmentMap && !first_pass)
  547. {
  548. Assert(seg_btree == nextSeg);
  549. }
  550. #endif
  551. if (SparseArraySegment<T>::IsMissingItem(v))
  552. {
  553. Assert(!(HasNoMissingValues() && nextSeg == head));
  554. return false;
  555. }
  556. *outVal = *v;
  557. return true;
  558. }
  559. nextSeg = nextSeg->next;
  560. Assert(segmentMap == GetSegmentMap());
  561. if (!segmentMap)
  562. {
  563. probeCost++;
  564. if (probeCost > SegmentBTree::GetLazyCrossOverLimit() && this->head != EmptySegment)
  565. {
  566. // Build a SegmentMap
  567. segmentMap = BuildSegmentMap();
  568. // Find the right segment
  569. SparseArraySegmentBase* matchOrNextSeg;
  570. segmentMap->Find(index, nextSeg, matchOrNextSeg);
  571. if (!nextSeg)
  572. {
  573. nextSeg = matchOrNextSeg;
  574. }
  575. }
  576. }
  577. }
  578. #ifdef VALIDATE_ARRAY
  579. Assert(segmentMap == GetSegmentMap());
  580. if (segmentMap && first_pass)
  581. {
  582. v_btree = NULL;
  583. seg_btree= nullptr;
  584. first_pass = false;
  585. goto SECOND_PASS;
  586. }
  587. else if (segmentMap && !first_pass)
  588. {
  589. Assert(v_btree == NULL && seg_btree == nullptr);
  590. }
  591. #endif
  592. return false;
  593. }
  594. template<typename T>
  595. void JavascriptArray::EnsureHead()
  596. {
  597. if (this->head == EmptySegment)
  598. {
  599. this->AllocateHead<T>();
  600. }
  601. }
  602. template<typename T>
  603. void JavascriptArray::AllocateHead()
  604. {
  605. Recycler* recycler = GetRecycler();
  606. uint32 allocLength;
  607. Assert(this->head == EmptySegment);
  608. if (this->length)
  609. {
  610. allocLength = this->length <= MaxInitialDenseLength ? this->length : SparseArraySegmentBase::HEAD_CHUNK_SIZE;
  611. this->head = SparseArraySegment<T>::AllocateSegment(recycler, 0, 0, allocLength, nullptr);
  612. }
  613. else
  614. {
  615. allocLength = SparseArraySegmentBase::HEAD_CHUNK_SIZE;
  616. this->head = SparseArraySegment<T>::AllocateSegment(recycler, 0, 0, allocLength, nullptr);
  617. }
  618. this->SetLastUsedSegment(this->head);
  619. SetHasNoMissingValues();
  620. }
  621. template<typename T>
  622. SparseArraySegment<T>* JavascriptArray::PrepareSegmentForMemOp(uint32 startIndex, uint32 length)
  623. {
  624. uint32 endIndex;
  625. if (UInt32Math::Add(startIndex, length - 1, &endIndex))
  626. {
  627. return nullptr;
  628. }
  629. if (endIndex >= this->length)
  630. {
  631. if (endIndex < JavascriptArray::InvalidIndex)
  632. {
  633. this->length = endIndex + 1;
  634. }
  635. else
  636. {
  637. return nullptr;
  638. }
  639. }
  640. // We are messing with the segments and the length of the array.
  641. // We must be certain we reach the end of this function without
  642. // any interruption to guaranty coherence of the array
  643. AutoDisableInterrupt autoDisableInterrupt(this->GetScriptContext()->GetThreadContext());
  644. this->EnsureHead<T>();
  645. Recycler* recycler = GetRecycler();
  646. //Find the segment where itemIndex is present or is at the boundary
  647. SparseArraySegment<T>* current = (SparseArraySegment<T>*)this->GetLastUsedSegment();
  648. SparseArraySegmentBase* prev = nullptr;
  649. SparseArraySegmentBase* startSeg = nullptr;
  650. SparseArraySegmentBase* endSeg = nullptr;
  651. SparseArraySegmentBase* startPrev = nullptr;
  652. uint32 growby, startOffset, endOffset;
  653. bool isAllocationSolelyInLastUsedSegment = false;
  654. // FindStartAndEndSegment
  655. {
  656. if (current->left > startIndex || endIndex >= current->left + current->size)
  657. {
  658. // The allocation may touch other segments, just start looking from head
  659. current = SparseArraySegment<T>::From(head);
  660. }
  661. else
  662. {
  663. // We are allocating solely in the last used segment
  664. startSeg = endSeg = current;
  665. current = nullptr;
  666. isAllocationSolelyInLastUsedSegment = true;
  667. }
  668. while (current != nullptr)
  669. {
  670. startOffset = startIndex - current->left;
  671. endOffset = endIndex - current->left;
  672. if (!startSeg)
  673. {
  674. if (startIndex <= current->left)
  675. {
  676. startPrev = prev;
  677. startSeg = current;
  678. }
  679. else if (startOffset <= current->size)
  680. {
  681. if ((nullptr == current->next) || (startIndex < current->next->left))
  682. {
  683. startPrev = prev;
  684. startSeg = current;
  685. }
  686. }
  687. }
  688. if (!endSeg)
  689. {
  690. if (endIndex <= current->left)
  691. {
  692. endSeg = current;
  693. break;
  694. }
  695. else if (endOffset <= current->size)
  696. {
  697. if ((nullptr == current->next) || (endIndex < current->next->left))
  698. {
  699. endSeg = current;
  700. break;
  701. }
  702. }
  703. }
  704. prev = current;
  705. current = SparseArraySegment<T>::From(current->next);
  706. }
  707. if (!startSeg && !endSeg)
  708. {
  709. startPrev = prev;
  710. }
  711. }
  712. if (startSeg == nullptr)
  713. {
  714. // if start index is greater than array length then we can add a new segment (or extend the last segment based on some heuristics)
  715. // ResizeArrayIfStartIsOutsideArrayLength;
  716. Assert(endSeg == nullptr);
  717. Assert(startIndex >= head->size);
  718. // Reallocate head if it meets a heuristics
  719. if (startPrev == head // prev segment is the head segment
  720. && !head->next // There is only one head segment in the array
  721. && startIndex - head->size <= MergeSegmentsLengthHeuristics // Distance to next index is relatively small
  722. )
  723. {
  724. SparseArraySegmentBase *oldHead = head;
  725. bool isInlineSegment = JavascriptArray::IsInlineSegment(oldHead, this);
  726. current = SparseArraySegment<T>::From(head)->GrowByMin(recycler, startIndex + length - head->size);
  727. current->length = endIndex + 1;
  728. current->CheckLengthvsSize();
  729. head = current;
  730. if (isInlineSegment)
  731. {
  732. this->ClearElements(oldHead, 0);
  733. }
  734. SetHasNoMissingValues(false);
  735. }
  736. else
  737. {
  738. //itemIndex is greater than the (left + size) of last segment in the linked list
  739. current = SparseArraySegment<T>::AllocateSegment(recycler, startIndex, length, (SparseArraySegment<T> *)nullptr);
  740. LinkSegments((Js::SparseArraySegment<T>*)startPrev, current);
  741. current->length = length;
  742. current->CheckLengthvsSize();
  743. if (current == head)
  744. {
  745. Assert(startIndex == 0);
  746. Assert(current->length == length);
  747. SetHasNoMissingValues();
  748. }
  749. }
  750. }
  751. else
  752. {
  753. // once we found the start segment we extend the start segment until startIndex+length . We don't care about what was there
  754. // as they will be overwritten by the memset/ memcopy. Then we need to append items from the (startIndex+length) to array.length
  755. // from the end segment to the new array
  756. // ExtendStartSegmentForMemOp
  757. SparseArraySegmentBase *oldStartSeg = startSeg;
  758. bool isInlineSegment = false;
  759. startOffset = startIndex - startSeg->left;
  760. if ((startIndex >= startSeg->left) && (startOffset < startSeg->size))
  761. {
  762. // startIndex is within startSeg
  763. if ((startOffset + length) > startSeg->size)
  764. {
  765. isInlineSegment = JavascriptArray::IsInlineSegment(startSeg, this);
  766. // if we don't have enough space in startSeg
  767. growby = length - (startSeg->size - startOffset);
  768. current = ((Js::SparseArraySegment<T>*)startSeg)->GrowByMin(recycler, growby);
  769. LinkSegments((Js::SparseArraySegment<T>*)startPrev, current);
  770. if (current == head)
  771. {
  772. if (startIndex > current->length)
  773. {
  774. // if it's the head segment and memset starts after the segment length, missing value is introduced
  775. SetHasNoMissingValues(false);
  776. }
  777. else if (!HasNoMissingValues())
  778. {
  779. // Have we overwritten all the missing values?
  780. if (!ScanForMissingValues<T>(0, startOffset))
  781. {
  782. SetHasNoMissingValues();
  783. }
  784. }
  785. }
  786. current->length = startOffset + length;
  787. current->CheckLengthvsSize();
  788. }
  789. else
  790. {
  791. // if we have enough space in the startseg
  792. current = (Js::SparseArraySegment<T>*)startSeg;
  793. if (current == head)
  794. {
  795. if (startIndex > current->length)
  796. {
  797. // if it's the head segment and memset starts after the segment length, missing value is introduced
  798. SetHasNoMissingValues(false);
  799. }
  800. else if (!HasNoMissingValues())
  801. {
  802. // Have we overwritten all the missing values?
  803. if (!ScanForMissingValues<T>(0, startOffset) && !ScanForMissingValues<T>(startOffset + length, current->length))
  804. {
  805. SetHasNoMissingValues();
  806. }
  807. }
  808. }
  809. current->length = current->length > (startOffset + length) ? current->length : (startOffset + length);
  810. current->CheckLengthvsSize();
  811. Assert(current == oldStartSeg);
  812. }
  813. }
  814. else if ((startIndex + 1) <= startSeg->left)
  815. {
  816. isInlineSegment = JavascriptArray::IsInlineSegment(startSeg, this);
  817. // startIndex is in between prev and startIndex
  818. current = SparseArraySegment<T>::template AllocateSegmentImpl<false>(recycler, startIndex, length, nullptr);
  819. LinkSegments((Js::SparseArraySegment<T>*)startPrev, current);
  820. if (current == head)
  821. {
  822. SetHasNoMissingValues();
  823. }
  824. }
  825. else
  826. {
  827. isInlineSegment = JavascriptArray::IsInlineSegment(startSeg, this);
  828. Assert(startIndex == startSeg->left + startSeg->size);
  829. current = ((Js::SparseArraySegment<T>*)startSeg)->GrowByMin(recycler, length);
  830. LinkSegments((Js::SparseArraySegment<T>*)startPrev, current);
  831. if (current == head)
  832. {
  833. if (startIndex > current->length)
  834. {
  835. // if it's the head segment and memset starts after the segment length, missing value is introduced
  836. SetHasNoMissingValues(false);
  837. }
  838. }
  839. current->length = startOffset + length;
  840. current->CheckLengthvsSize();
  841. }
  842. startSeg = current;
  843. Assert(startSeg != oldStartSeg || !isInlineSegment); // ensure isInlineSegment implies startSeg != oldStartSeg
  844. if (isInlineSegment)
  845. {
  846. this->ClearElements(oldStartSeg, 0);
  847. }
  848. // AppendLeftOverItemsFromEndSegment
  849. SparseArraySegmentBase *oldCurrent = current;
  850. isInlineSegment = false;
  851. if (!endSeg)
  852. {
  853. // end is beyond the length of the array
  854. Assert(endIndex == (current->left + current->length - 1));
  855. current->next = nullptr;
  856. Assert(oldCurrent == current);
  857. }
  858. else
  859. {
  860. endOffset = endIndex - endSeg->left;
  861. startOffset = startIndex - current->left;
  862. if ((endIndex >= endSeg->left) && (endOffset < endSeg->size))
  863. {
  864. // endIndex is within endSeg
  865. if (endSeg->length - 1 > endOffset)
  866. {
  867. if (startSeg != endSeg)
  868. {
  869. isInlineSegment = JavascriptArray::IsInlineSegment(current, this);
  870. // we have some leftover items on endseg
  871. growby = (endSeg->length - endOffset - 1);
  872. current = current->GrowByMin(recycler, growby);
  873. CopyArray(current->elements + startOffset + length, growby,
  874. ((Js::SparseArraySegment<T>*)endSeg)->elements + endOffset + 1, growby);
  875. LinkSegments((Js::SparseArraySegment<T>*)startPrev, current);
  876. current->length = startOffset + length + growby;
  877. current->CheckLengthvsSize();
  878. if (current == head && HasNoMissingValues())
  879. {
  880. if (ScanForMissingValues<T>(startOffset + length, current->length))
  881. {
  882. SetHasNoMissingValues(false);
  883. }
  884. }
  885. }
  886. }
  887. current->next = endSeg->next;
  888. }
  889. else if ((endIndex + 1) <= endSeg->left)
  890. {
  891. // endIndex is between endSeg and the segment before
  892. if (endIndex + 1 == endSeg->left && current == head)
  893. {
  894. isInlineSegment = JavascriptArray::IsInlineSegment(current, this);
  895. // extend current to hold endSeg
  896. growby = endSeg->length;
  897. current = current->GrowByMin(recycler, growby);
  898. CopyArray(current->elements + endIndex + 1, endSeg->length,
  899. ((Js::SparseArraySegment<T>*)endSeg)->elements, endSeg->length);
  900. LinkSegments((Js::SparseArraySegment<T>*)startPrev, current);
  901. if (current == head && HasNoMissingValues())
  902. {
  903. if (ScanForMissingValues<T>(endIndex + 1, endIndex + growby))
  904. {
  905. SetHasNoMissingValues(false);
  906. }
  907. }
  908. current->length = endIndex + growby + 1;
  909. current->CheckLengthvsSize();
  910. current->next = endSeg->next;
  911. }
  912. else
  913. {
  914. current->next = endSeg;
  915. Assert(oldCurrent == current);
  916. }
  917. }
  918. else
  919. {
  920. //endIndex is at the boundary of endSeg segment at the left + size
  921. Assert(endIndex == endSeg->left + endSeg->size);
  922. current->next = endSeg->next;
  923. Assert(oldCurrent == current);
  924. }
  925. }
  926. Assert(oldCurrent != current || !isInlineSegment); // ensure isInlineSegment implies oldCurrent != current
  927. if (isInlineSegment)
  928. {
  929. this->ClearElements(oldCurrent, 0);
  930. }
  931. }
  932. Assert(current);
  933. Assert(current->left <= startIndex);
  934. Assert((startIndex - current->left) < current->size);
  935. // If we are solely using the last used segment, make sure there were no allocation done
  936. Assert(!isAllocationSolelyInLastUsedSegment || (current == startSeg && current == endSeg));
  937. autoDisableInterrupt.Completed();
  938. return current;
  939. }
  940. template<typename T>
  941. bool JavascriptArray::DirectSetItemAtRangeFromArray(uint32 toStartIndex, uint32 length, JavascriptArray *fromArray, uint32 fromStartIndex)
  942. {
  943. if (length == 0)
  944. {
  945. return true;
  946. }
  947. // Do not do a memcopy from an array that has missing values
  948. if (fromArray == nullptr || fromArray == this || !fromArray->HasNoMissingValues())
  949. {
  950. return false;
  951. }
  952. bool isBtree = false;
  953. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  954. isBtree = Js::Configuration::Global.flags.ForceArrayBTree;
  955. #endif
  956. if (GetSegmentMap() || fromArray->GetSegmentMap() || isBtree)
  957. {
  958. for (uint i = 0; i < length; i++)
  959. {
  960. T val;
  961. if (!fromArray->DirectGetItemAt(fromStartIndex + i, &val))
  962. {
  963. return false;
  964. }
  965. DirectSetItem_Full(toStartIndex + i, val);
  966. }
  967. return true;
  968. }
  969. const auto isSegmentValid = [length](Js::SparseArraySegment<T>* segment, uint32 startIndex) {
  970. uint32 end, segmentEnd;
  971. // Check the segment is big enough
  972. return (
  973. segment &&
  974. !UInt32Math::Add(startIndex, length, &end) &&
  975. !UInt32Math::Add(segment->left, segment->length, &segmentEnd) &&
  976. startIndex >= segment->left &&
  977. startIndex < segmentEnd &&
  978. segmentEnd >= end
  979. );
  980. };
  981. // Check if the head segment of the fromArray has everything we need to copy
  982. Js::SparseArraySegment<T>* fromSegment = (Js::SparseArraySegment<T>*)fromArray->GetHead();
  983. if (!isSegmentValid(fromSegment, fromStartIndex))
  984. {
  985. return false;
  986. }
  987. // Check the from segment first so we don't prepare the toSegment in case it is invalid
  988. SparseArraySegment<T> *toSegment = PrepareSegmentForMemOp<T>(toStartIndex, length);
  989. if (!isSegmentValid(toSegment, toStartIndex))
  990. {
  991. return false;
  992. }
  993. Assert(GetSegmentMap() == nullptr && fromArray->GetSegmentMap() == nullptr);
  994. int memcopySize = length;
  995. int toStartOffset = toStartIndex - toSegment->left;
  996. int fromStartOffset = fromStartIndex - fromSegment->left;
  997. Assert((fromStartOffset + length) <= fromSegment->length);
  998. CopyArray(
  999. toSegment->elements + toStartOffset,
  1000. toSegment->size - toStartOffset,
  1001. fromSegment->elements + fromStartOffset,
  1002. memcopySize
  1003. );
  1004. fromArray->SetLastUsedSegment(fromSegment);
  1005. this->SetLastUsedSegment(toSegment);
  1006. #if DBG
  1007. if (Js::Configuration::Global.flags.MemOpMissingValueValidate)
  1008. {
  1009. if (toSegment == head)
  1010. {
  1011. Assert(ScanForMissingValues<T>(0, this->length) != HasNoMissingValues());
  1012. }
  1013. }
  1014. #endif
  1015. return true;
  1016. }
  1017. template<typename T>
  1018. bool JavascriptArray::DirectSetItemAtRange(uint32 startIndex, uint32 length, T newValue)
  1019. {
  1020. bool isBtree = false;
  1021. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  1022. isBtree = Js::Configuration::Global.flags.ForceArrayBTree;
  1023. #endif
  1024. if (GetSegmentMap() || isBtree)
  1025. {
  1026. for (uint i = startIndex; i < startIndex + length; i++)
  1027. {
  1028. DirectSetItem_Full<T>(i, newValue);
  1029. }
  1030. return true;
  1031. }
  1032. if (startIndex == 0 && head != EmptySegment && length < head->size)
  1033. {
  1034. CopyValueToSegmentBuferNoCheck(SparseArraySegment<T>::From(head)->elements, length, newValue);
  1035. if (length > this->length)
  1036. {
  1037. this->length = length;
  1038. }
  1039. if (length > head->length)
  1040. {
  1041. head->length = length;
  1042. head->CheckLengthvsSize();
  1043. }
  1044. if (!HasNoMissingValues())
  1045. {
  1046. if (ScanForMissingValues<T>(length, head->length) == false)
  1047. {
  1048. SetHasNoMissingValues(true);
  1049. }
  1050. }
  1051. this->SetLastUsedSegment(head);
  1052. }
  1053. else if (startIndex == 0 && length > this->length && (head == EmptySegment || length > head->size))
  1054. {
  1055. Recycler *recycler = GetRecycler();
  1056. SparseArraySegmentBase* current = SparseArraySegment<T>::AllocateSegment(recycler, 0, length, (SparseArraySegment<T> *)nullptr);
  1057. this->SetHeadAndLastUsedSegment(current);
  1058. this->length = length;
  1059. Assert(!HasSegmentMap());
  1060. SetHasNoMissingValues(true);
  1061. CopyValueToSegmentBuferNoCheck(((Js::SparseArraySegment<T>*)current)->elements, length, newValue);
  1062. }
  1063. else
  1064. {
  1065. return DirectSetItemAtRangeFull<T>(startIndex, length, newValue);
  1066. }
  1067. return true;
  1068. }
  1069. template<typename T>
  1070. bool JavascriptArray::DirectSetItemAtRangeFull(uint32 startIndex, uint32 length, T newValue)
  1071. {
  1072. if (length == 0)
  1073. {
  1074. return true;
  1075. }
  1076. Assert(!GetSegmentMap());
  1077. SparseArraySegment<T> *current = PrepareSegmentForMemOp<T>(startIndex, length);
  1078. if (current == nullptr)
  1079. {
  1080. return false;
  1081. }
  1082. Assert(current->left + current->length >= startIndex + length);
  1083. Field(T)* segmentCopyStart = current->elements + (startIndex - current->left);
  1084. CopyValueToSegmentBuferNoCheck(segmentCopyStart, length, newValue);
  1085. this->SetLastUsedSegment(current);
  1086. #if DBG
  1087. if (Js::Configuration::Global.flags.MemOpMissingValueValidate)
  1088. {
  1089. if (current == head)
  1090. {
  1091. Assert(ScanForMissingValues<T>(0, this->length) != HasNoMissingValues());
  1092. }
  1093. }
  1094. #endif
  1095. return true;
  1096. }
  1097. template<typename T>
  1098. void JavascriptArray::DirectSetItem_Full(uint32 itemIndex, T newValue)
  1099. {
  1100. Assert(!SparseArraySegment<T>::IsMissingItem(&newValue));
  1101. DebugOnly(VerifyNotNeedMarshal(newValue));
  1102. this->EnsureHead<T>();
  1103. AnalysisAssert(head);
  1104. #ifdef VALIDATE_ARRAY
  1105. ValidateArray();
  1106. #endif
  1107. if (itemIndex >= this->length)
  1108. {
  1109. if (itemIndex != JavascriptArray::InvalidIndex)
  1110. {
  1111. this->length = itemIndex + 1;
  1112. }
  1113. else
  1114. {
  1115. JavascriptError::ThrowRangeError(this->GetScriptContext(), JSERR_ArrayLengthAssignIncorrect);
  1116. }
  1117. }
  1118. Recycler* recycler = GetRecycler();
  1119. //Find the segment where itemIndex is present or is at the boundary
  1120. SparseArraySegment<T>* current = (SparseArraySegment<T>*)this->GetBeginLookupSegment(itemIndex, false);
  1121. // If it doesn't fit in current chunk (watch for overflow), start from beginning as we'll
  1122. // need the prev
  1123. if (current->left + current->size > current->left || itemIndex >= current->left + current->size)
  1124. {
  1125. current = SparseArraySegment<T>::From(head);
  1126. }
  1127. SparseArraySegmentBase* prev = nullptr;
  1128. #ifdef VALIDATE_ARRAY
  1129. SparseArraySegmentBase* current_btree = nullptr;
  1130. SparseArraySegmentBase* prev_btree = nullptr;
  1131. bool first_pass = true;
  1132. #endif
  1133. SegmentBTreeRoot * segmentMap = GetSegmentMap();
  1134. if (segmentMap)
  1135. {
  1136. SparseArraySegmentBase* prevSeg = nullptr;
  1137. SparseArraySegmentBase* currentBase = current;
  1138. segmentMap->Find(itemIndex, prevSeg, currentBase);
  1139. current = (SparseArraySegment<T>*)currentBase;
  1140. Assert(!prevSeg || prevSeg->next == current);
  1141. if (prevSeg)
  1142. {
  1143. bool noExactMatch = !current || itemIndex < current->left;
  1144. Assert(prevSeg->left + prevSeg->size >= prevSeg->left);
  1145. bool extendPrevSeg = itemIndex <= prevSeg->left + prevSeg->size;
  1146. if (noExactMatch && extendPrevSeg)
  1147. {
  1148. current = SparseArraySegment<T>::From(head);
  1149. prev = nullptr;
  1150. if (prevSeg != head)
  1151. {
  1152. // Since we are going to extend prevSeg we need the
  1153. // address of it's left neighbor's next pointer
  1154. currentBase = current;
  1155. segmentMap->Find(prevSeg->left, prevSeg, currentBase);
  1156. current = (SparseArraySegment<T>*)currentBase;
  1157. Assert(prevSeg && prevSeg->next == current);
  1158. prev = prevSeg;
  1159. }
  1160. }
  1161. else
  1162. {
  1163. prev = prevSeg;
  1164. }
  1165. }
  1166. else
  1167. {
  1168. Assert(current == head);
  1169. }
  1170. }
  1171. #ifdef VALIDATE_ARRAY
  1172. SECOND_PASS:
  1173. if (!first_pass)
  1174. {
  1175. current = (SparseArraySegment<T>*)this->GetBeginLookupSegment(itemIndex, false);
  1176. // If it doesn't fit in current chunk (watch for overflow), start from beginning as we'll
  1177. // need the prev
  1178. if (current->left + current->size > current->left || itemIndex >= current->left + current->size)
  1179. {
  1180. current = SparseArraySegment<T>::From(head);
  1181. }
  1182. prev = nullptr;
  1183. }
  1184. #endif
  1185. uint probeCost = 0;
  1186. while(current != nullptr)
  1187. {
  1188. uint32 offset = itemIndex - current->left;
  1189. if (itemIndex < current->left)
  1190. {
  1191. break;
  1192. }
  1193. else if (offset <= current->size)
  1194. {
  1195. if ((nullptr == current->next) || (itemIndex < current->next->left))
  1196. {
  1197. break;
  1198. }
  1199. }
  1200. prev = current;
  1201. current = SparseArraySegment<T>::From(current->next);
  1202. Assert(segmentMap == GetSegmentMap());
  1203. if (!segmentMap)
  1204. {
  1205. probeCost++;
  1206. if (probeCost > SegmentBTree::GetLazyCrossOverLimit())
  1207. {
  1208. // Build a SegmentMap
  1209. segmentMap = BuildSegmentMap();
  1210. SparseArraySegmentBase* prevSeg = nullptr;
  1211. SparseArraySegmentBase* currentBase = current;
  1212. segmentMap->Find(itemIndex, prevSeg, currentBase);
  1213. current = (SparseArraySegment<T>*)currentBase;
  1214. Assert(prevSeg->next == current);
  1215. if (prevSeg)
  1216. {
  1217. bool noExactMatch = !current || itemIndex < current->left;
  1218. Assert(prevSeg->left + prevSeg->size >= prevSeg->left);
  1219. bool extendPrevSeg = itemIndex <= prevSeg->left + prevSeg->size;
  1220. if (noExactMatch && extendPrevSeg)
  1221. {
  1222. current = SparseArraySegment<T>::From(head);
  1223. prev = nullptr;
  1224. if (prevSeg != head)
  1225. {
  1226. // Since we are going to extend prevSeg we need the
  1227. // address of its left neighbor's next pointer
  1228. currentBase = current;
  1229. segmentMap->Find(prevSeg->left, prevSeg, currentBase);
  1230. current = (SparseArraySegment<T>*)currentBase;
  1231. Assert(prevSeg->next == current);
  1232. prev = prevSeg;
  1233. }
  1234. }
  1235. else
  1236. {
  1237. prev = prevSeg;
  1238. }
  1239. }
  1240. else
  1241. {
  1242. Assert(current == head);
  1243. }
  1244. }
  1245. }
  1246. }
  1247. #ifdef VALIDATE_ARRAY
  1248. Assert(segmentMap == GetSegmentMap());
  1249. if (segmentMap && first_pass)
  1250. {
  1251. current_btree = current;
  1252. prev_btree = prev;
  1253. first_pass = false;
  1254. goto SECOND_PASS;
  1255. }
  1256. else if (segmentMap)
  1257. {
  1258. Assert(current_btree == current && prev_btree == prev);
  1259. }
  1260. #endif
  1261. if (current != nullptr)
  1262. {
  1263. uint32 offset = itemIndex - current->left;
  1264. if ((itemIndex >= current->left) && (offset < current->size))
  1265. {
  1266. //itemIndex lies in the segment
  1267. Assert(!(HasNoMissingValues() &&
  1268. offset < current->length &&
  1269. SparseArraySegment<T>::IsMissingItem(&current->elements[offset]) &&
  1270. current == head));
  1271. if(offset > current->length && current == head)
  1272. {
  1273. SetHasNoMissingValues(false);
  1274. }
  1275. const bool scanForMissingValues = NeedScanForMissingValuesUponSetItem(current, offset);
  1276. ((SparseArraySegment<T>*)current)->SetElement(recycler, itemIndex, newValue);
  1277. if(scanForMissingValues)
  1278. {
  1279. ScanForMissingValues<T>();
  1280. }
  1281. }
  1282. else if ((itemIndex + 1) < current->left)
  1283. {
  1284. //itemIndex lies in between current and previous segment
  1285. SparseArraySegment<T>* newSeg = SparseArraySegment<T>::AllocateSegment(recycler, prev, itemIndex);
  1286. newSeg->SetElement(recycler, itemIndex, newValue);
  1287. newSeg->next = current;
  1288. LinkSegments((SparseArraySegment<T>*)prev, newSeg);
  1289. current = newSeg;
  1290. TryAddToSegmentMap(recycler, newSeg);
  1291. Assert(current != head);
  1292. }
  1293. else
  1294. {
  1295. //itemIndex is at boundary of current segment either at the left + size or at left - 1;
  1296. Assert((itemIndex == current->left + current->size) || (itemIndex + 1 == current->left));
  1297. SparseArraySegment<T>* next = SparseArraySegment<T>::From(current->next);
  1298. Assert(segmentMap == GetSegmentMap());
  1299. if (!segmentMap && next != nullptr && (itemIndex + 1) == next->left)
  1300. {
  1301. // Don't merge segments if we are using a segmentMap
  1302. //Special case where we need to merge two segments. itemIndex is on the size boundary
  1303. //of the current segment & left boundary of the next
  1304. const bool currentWasFull = current->length == current->size;
  1305. Assert(itemIndex == current->left + current->size);
  1306. SparseArraySegmentBase* oldSegment = current;
  1307. bool isInlineSegment = JavascriptArray::IsInlineSegment(oldSegment, this);
  1308. current = SparseArraySegment<T>::CopySegment(recycler, (SparseArraySegment<T>*)current, next->left, next, next->left, next->length);
  1309. current->next = next->next;
  1310. current->SetElement(recycler, itemIndex, newValue);
  1311. LinkSegments((SparseArraySegment<T>*)prev, current);
  1312. if(HasNoMissingValues() && current == head)
  1313. {
  1314. // We just merged the head segment and its next segment and filled the only missing value in-between the
  1315. // two segments. We already know that the previous head segment does not have any missing values. If the
  1316. // previous head segment was full, scan the new head segment starting from the merge point for missing
  1317. // values. If the previous head segment was not full, then merging the segments would have created
  1318. // missing values.
  1319. SetHasNoMissingValues(false);
  1320. if(currentWasFull)
  1321. {
  1322. ScanForMissingValues<T>(offset + 1);
  1323. }
  1324. }
  1325. if (isInlineSegment && current != oldSegment)
  1326. {
  1327. this->ClearElements(oldSegment, 0);
  1328. }
  1329. }
  1330. else
  1331. {
  1332. if(offset > current->length && current == head)
  1333. {
  1334. SetHasNoMissingValues(false);
  1335. }
  1336. const bool currentWasHead = current == head;
  1337. SparseArraySegmentBase* oldSegment = current;
  1338. bool isInlineSegment = JavascriptArray::IsInlineSegment(oldSegment, this);
  1339. uint originalKey = oldSegment->left;
  1340. current = current->SetElementGrow(recycler, prev, itemIndex, newValue);
  1341. Assert(segmentMap == GetSegmentMap());
  1342. if (segmentMap)
  1343. {
  1344. segmentMap->SwapSegment(originalKey, oldSegment, current);
  1345. }
  1346. LinkSegments((SparseArraySegment<T>*)prev, current);
  1347. // Scan for missing values when the current segment was grown at the beginning and made the head segment
  1348. if(!currentWasHead && current == head)
  1349. {
  1350. ScanForMissingValues<T>();
  1351. }
  1352. if (isInlineSegment)
  1353. {
  1354. this->ClearElements(oldSegment, 0);
  1355. }
  1356. }
  1357. }
  1358. }
  1359. else
  1360. {
  1361. // Reallocate head if need it meets a heuristics
  1362. Assert(itemIndex >= head->size);
  1363. if (prev == head // prev segment is the head segment
  1364. && !head->next // There is only one head segment in the array
  1365. && !segmentMap // There is no segmentMap which makes sure that array is not highly fragmented.
  1366. && itemIndex - head->size <= MergeSegmentsLengthHeuristics // Distance to next index is relatively small
  1367. )
  1368. {
  1369. current = SparseArraySegment<T>::From(head)->GrowByMin(recycler, itemIndex + 1 - head->size);
  1370. current->elements[itemIndex] = newValue;
  1371. current->length = itemIndex + 1;
  1372. current->CheckLengthvsSize();
  1373. if (JavascriptArray::IsInlineSegment(head, this))
  1374. {
  1375. this->ClearElements(head, 0);
  1376. }
  1377. head = current;
  1378. SetHasNoMissingValues(false);
  1379. }
  1380. else
  1381. {
  1382. //itemIndex is greater than the (left + size) of last segment in the linked list
  1383. current = SparseArraySegment<T>::AllocateSegment(recycler, itemIndex, 1, (SparseArraySegment<T> *)nullptr);
  1384. current->SetElement(recycler, itemIndex, newValue);
  1385. LinkSegments((SparseArraySegment<T>*)prev, current);
  1386. TryAddToSegmentMap(recycler, current);
  1387. if(current == head)
  1388. {
  1389. Assert(itemIndex == 0);
  1390. Assert(current->length == 1);
  1391. SetHasNoMissingValues();
  1392. }
  1393. }
  1394. }
  1395. this->SetLastUsedSegment(current);
  1396. #ifdef VALIDATE_ARRAY
  1397. ValidateArray();
  1398. #endif
  1399. }
  1400. template<typename T>
  1401. bool JavascriptArray::NeedScanForMissingValuesUponSetItem(SparseArraySegment<T> *const segment, const uint32 offset) const
  1402. {
  1403. Assert(segment);
  1404. // Scan for missing values upon SetItem when a missing value is being filled and the surrounding values are not missing,
  1405. // as this could be the last missing value that is being filled
  1406. return
  1407. offset < segment->length &&
  1408. SparseArraySegment<T>::IsMissingItem(&segment->elements[offset]) &&
  1409. (offset == 0 || !SparseArraySegment<T>::IsMissingItem(&segment->elements[offset - 1])) &&
  1410. (offset == segment->length - 1 || !SparseArraySegment<T>::IsMissingItem(&segment->elements[offset + 1])) &&
  1411. segment == head;
  1412. }
  1413. template<typename T>
  1414. void JavascriptArray::ScanForMissingValues(const uint startIndex)
  1415. {
  1416. Assert(head);
  1417. Assert(!HasNoMissingValues());
  1418. SparseArraySegment<T> *const segment = SparseArraySegment<T>::From(head);
  1419. const uint segmentLength = segment->length;
  1420. const Field(T) * const segmentElements = segment->elements;
  1421. for(uint i = startIndex; i < segmentLength; ++i)
  1422. {
  1423. if(SparseArraySegment<T>::IsMissingItem(&segmentElements[i]))
  1424. {
  1425. return;
  1426. }
  1427. }
  1428. SetHasNoMissingValues();
  1429. }
  1430. template<typename T>
  1431. bool JavascriptArray::ScanForMissingValues(const uint startIndex, const uint endIndex)
  1432. {
  1433. Assert(head);
  1434. //Assert(!HasNoMissingValues());
  1435. SparseArraySegment<T> *const segment = SparseArraySegment<T>::From(head);
  1436. const Field(T) *const segmentElements = segment->elements;
  1437. for (uint i = startIndex; i < endIndex; ++i)
  1438. {
  1439. if (SparseArraySegment<T>::IsMissingItem(&segmentElements[i]))
  1440. {
  1441. return true;
  1442. }
  1443. }
  1444. return false;
  1445. }
  1446. inline void JavascriptArray::DirectSetItemIfNotExist(uint32 index, Var newValue)
  1447. {
  1448. Assert(VirtualTableInfo<JavascriptArray>::HasVirtualTable(this));
  1449. Var oldValue;
  1450. if (!DirectGetItemAt(index, &oldValue))
  1451. {
  1452. DirectSetItemAt(index, newValue);
  1453. }
  1454. }
  1455. //Grow the array head and try to set at the boundary
  1456. template<typename unitType, typename classname>
  1457. inline BOOL JavascriptArray::TryGrowHeadSegmentAndSetItem(uint32 indexInt, unitType iValue)
  1458. {
  1459. SparseArraySegment<unitType> *current = SparseArraySegment<unitType>::From(head);
  1460. if (indexInt == current->length // index is at the boundary of size & length
  1461. && current->size // Make sure its not empty segment.
  1462. && !current->next // There is only head segment.
  1463. && current->length == current->size // Why did we miss the fastpath?
  1464. && !SparseArraySegment<unitType>::IsMissingItem(&iValue)) // value to set is not a missing value.
  1465. {
  1466. SparseArraySegmentBase *oldCurrent = current;
  1467. bool isInlineSegment = JavascriptArray::IsInlineSegment(oldCurrent, this);
  1468. current= current->GrowByMin(this->GetRecycler(), indexInt + 1);
  1469. DebugOnly(VerifyNotNeedMarshal(iValue));
  1470. current->elements[indexInt] = iValue;
  1471. current->length = indexInt + 1;
  1472. current->CheckLengthvsSize();
  1473. // There is only a head segment in this condition A segment map is not necessary
  1474. // and most likely invalid at this point. Also we are setting the head and lastUsedSegment
  1475. // to the same segment. Precedent in the rest of the code base dictates the use of
  1476. // SetHeadAndLastUsedSegment which asserts if a segment map exists.
  1477. ClearSegmentMap();
  1478. SetHeadAndLastUsedSegment(current);
  1479. if (isInlineSegment)
  1480. {
  1481. this->ClearElements(oldCurrent, 0);
  1482. }
  1483. if (this->length <= indexInt)
  1484. {
  1485. this->length = indexInt + 1;
  1486. }
  1487. #ifdef VALIDATE_ARRAY
  1488. ValidateArray();
  1489. #endif
  1490. return true;
  1491. }
  1492. return false;
  1493. }
  1494. //
  1495. // JavascriptArray::IndexTrace specialized on uint32 (small index)
  1496. //
  1497. template<>
  1498. inline Var JavascriptArray::IndexTrace<uint32>::ToNumber(const uint32& index, ScriptContext* scriptContext)
  1499. {
  1500. return JavascriptNumber::ToVar(index, scriptContext);
  1501. }
  1502. template<>
  1503. inline BOOL JavascriptArray::IndexTrace<uint32>::GetItem(JavascriptArray* arr, const uint32& index, Var* outVal)
  1504. {
  1505. return arr->DirectGetItemAt(index, outVal);
  1506. }
  1507. template<>
  1508. inline BOOL JavascriptArray::IndexTrace<uint32>::SetItem(JavascriptArray* arr, const uint32& index, Var newValue)
  1509. {
  1510. return arr->SetItem(index, newValue, PropertyOperation_None);
  1511. }
  1512. template<>
  1513. inline void JavascriptArray::IndexTrace<uint32>::SetItemIfNotExist(JavascriptArray* arr, const uint32& index, Var newValue)
  1514. {
  1515. arr->DirectSetItemIfNotExist(index, newValue);
  1516. }
  1517. template<>
  1518. inline BOOL JavascriptArray::IndexTrace<uint32>::DeleteItem(JavascriptArray* arr, const uint32& index)
  1519. {
  1520. switch (arr->GetTypeId())
  1521. {
  1522. case TypeIds_Array:
  1523. return arr->DirectDeleteItemAt<Var>(index);
  1524. case TypeIds_NativeIntArray:
  1525. return arr->DirectDeleteItemAt<int32>(index);
  1526. case TypeIds_NativeFloatArray:
  1527. return arr->DirectDeleteItemAt<double>(index);
  1528. default:
  1529. Assert(FALSE);
  1530. return FALSE;
  1531. }
  1532. }
  1533. template<>
  1534. inline BOOL JavascriptArray::IndexTrace<uint32>::SetItem(RecyclableObject* obj, const uint32& index, Var newValue, PropertyOperationFlags flags)
  1535. {
  1536. ScriptContext* requestContext = obj->GetScriptContext();
  1537. return JavascriptOperators::SetItem(obj, obj, index, newValue, requestContext, flags);
  1538. }
  1539. template<>
  1540. inline BOOL JavascriptArray::IndexTrace<uint32>::DeleteItem(RecyclableObject* obj, const uint32& index, PropertyOperationFlags flags)
  1541. {
  1542. return JavascriptOperators::DeleteItem(obj, index, flags);
  1543. }
  1544. //
  1545. // JavascriptArray::IndexTrace specialized on BigIndex
  1546. //
  1547. template<>
  1548. inline Var JavascriptArray::IndexTrace<JavascriptArray::BigIndex>::ToNumber(const JavascriptArray::BigIndex& index, ScriptContext* scriptContext)
  1549. {
  1550. return index.ToNumber(scriptContext);
  1551. }
  1552. template<>
  1553. inline BOOL JavascriptArray::IndexTrace<JavascriptArray::BigIndex>::GetItem(JavascriptArray* arr, const JavascriptArray::BigIndex& index, Var* outVal)
  1554. {
  1555. return index.GetItem(arr, outVal);
  1556. }
  1557. template<>
  1558. inline BOOL JavascriptArray::IndexTrace<JavascriptArray::BigIndex>::SetItem(JavascriptArray* arr, const JavascriptArray::BigIndex& index, Var newValue)
  1559. {
  1560. return index.SetItem(arr, newValue);
  1561. }
  1562. template<>
  1563. inline void JavascriptArray::IndexTrace<JavascriptArray::BigIndex>::SetItemIfNotExist(JavascriptArray* arr, const JavascriptArray::BigIndex& index, Var newValue)
  1564. {
  1565. index.SetItemIfNotExist(arr, newValue);
  1566. }
  1567. template<>
  1568. inline BOOL JavascriptArray::IndexTrace<JavascriptArray::BigIndex>::DeleteItem(JavascriptArray* arr, const JavascriptArray::BigIndex& index)
  1569. {
  1570. return index.DeleteItem(arr);
  1571. }
  1572. template<>
  1573. inline BOOL JavascriptArray::IndexTrace<JavascriptArray::BigIndex>::SetItem(RecyclableObject* obj, const JavascriptArray::BigIndex& index, Var newValue, PropertyOperationFlags flags)
  1574. {
  1575. return index.SetItem(obj, newValue, flags);
  1576. }
  1577. template<>
  1578. inline BOOL JavascriptArray::IndexTrace<JavascriptArray::BigIndex>::DeleteItem(RecyclableObject* obj, const JavascriptArray::BigIndex& index, PropertyOperationFlags flags)
  1579. {
  1580. return index.DeleteItem(obj, flags);
  1581. }
  1582. template<class T, uint InlinePropertySlots>
  1583. inline size_t JavascriptArray::DetermineAllocationSize(
  1584. const uint inlineElementSlots,
  1585. size_t *const allocationPlusSizeRef,
  1586. uint *const alignedInlineElementSlotsRef)
  1587. {
  1588. CompileAssert(static_cast<PropertyIndex>(InlinePropertySlots) == InlinePropertySlots);
  1589. Assert(
  1590. DynamicTypeHandler::RoundUpInlineSlotCapacity(static_cast<PropertyIndex>(InlinePropertySlots)) ==
  1591. InlinePropertySlots);
  1592. CompileAssert(
  1593. InlinePropertySlots <=
  1594. (UINT_MAX - (sizeof(T) + sizeof(SparseArraySegment<typename T::TElement>))) / sizeof(Var));
  1595. const uint objectSize =
  1596. sizeof(T) + sizeof(SparseArraySegment<typename T::TElement>) + InlinePropertySlots * sizeof(Var);
  1597. size_t totalSize = UInt32Math::MulAdd<sizeof(typename T::TElement), objectSize>(inlineElementSlots);
  1598. #if defined(TARGET_64)
  1599. // On x64, the total size won't be anywhere near AllocSizeMath::MaxMemory on x64, so no need to check
  1600. totalSize = HeapInfo::GetAlignedSizeNoCheck(totalSize);
  1601. #else
  1602. totalSize = HeapInfo::GetAlignedSize(totalSize);
  1603. #endif
  1604. if(allocationPlusSizeRef)
  1605. {
  1606. *allocationPlusSizeRef = totalSize - sizeof(T);
  1607. }
  1608. if(alignedInlineElementSlotsRef)
  1609. {
  1610. const size_t alignedInlineElementSlots = (totalSize - objectSize) / sizeof(typename T::TElement);
  1611. *alignedInlineElementSlotsRef = static_cast<uint>(alignedInlineElementSlots);
  1612. Assert(*alignedInlineElementSlotsRef == alignedInlineElementSlots); // ensure no truncation above
  1613. }
  1614. return totalSize;
  1615. }
  1616. template<class ArrayType>
  1617. void JavascriptArray::EnsureCalculationOfAllocationBuckets()
  1618. {
  1619. uint temp;
  1620. for (uint8 i = 0;i < ArrayType::AllocationBucketsCount;i++)
  1621. {
  1622. ArrayType::allocationBuckets[i][AllocationSizeIndex] = (uint)DetermineAllocationSize<ArrayType, 0>(ArrayType::allocationBuckets[i][AllocationBucketIndex], nullptr, &temp);
  1623. ArrayType::allocationBuckets[i][MissingElementsCountIndex] = temp;
  1624. }
  1625. }
  1626. template<class ArrayType, uint InlinePropertySlots>
  1627. inline size_t JavascriptArray::DetermineAllocationSizeForArrayObjects(
  1628. const uint inlineElementSlots,
  1629. size_t *const allocationPlusSizeRef,
  1630. uint *const alignedInlineElementSlotsRef)
  1631. {
  1632. uint8 bucketsCount = ArrayType::AllocationBucketsCount;
  1633. EnsureCalculationOfAllocationBuckets<ArrayType>();
  1634. if (inlineElementSlots >= 0 && inlineElementSlots <= ArrayType::allocationBuckets[bucketsCount - 1][AllocationBucketIndex])
  1635. {
  1636. for (uint8 i = 0;i < bucketsCount;i++)
  1637. {
  1638. uint elementsCountToInitialize = ArrayType::allocationBuckets[i][MissingElementsCountIndex];
  1639. uint allocationSize = ArrayType::allocationBuckets[i][AllocationSizeIndex];
  1640. // Ensure we already have allocation size calculated and within range
  1641. Assert(elementsCountToInitialize > 0 && elementsCountToInitialize <= ArrayType::allocationBuckets[bucketsCount - 1][MissingElementsCountIndex]);
  1642. Assert(allocationSize > 0 && allocationSize <= ArrayType::allocationBuckets[bucketsCount - 1][AllocationSizeIndex]);
  1643. if (inlineElementSlots <= ArrayType::allocationBuckets[i][AllocationBucketIndex])
  1644. {
  1645. if (alignedInlineElementSlotsRef)
  1646. {
  1647. *alignedInlineElementSlotsRef = elementsCountToInitialize;
  1648. }
  1649. if (allocationPlusSizeRef)
  1650. {
  1651. *allocationPlusSizeRef = allocationSize - sizeof(ArrayType);
  1652. }
  1653. return allocationSize;
  1654. }
  1655. }
  1656. }
  1657. return DetermineAllocationSize<ArrayType, InlinePropertySlots>(inlineElementSlots, allocationPlusSizeRef, alignedInlineElementSlotsRef);
  1658. }
  1659. template<class T, uint InlinePropertySlots>
  1660. inline uint JavascriptArray::DetermineAvailableInlineElementSlots(
  1661. const size_t allocationSize,
  1662. bool *const isSufficientSpaceForInlinePropertySlotsRef)
  1663. {
  1664. CompileAssert(static_cast<PropertyIndex>(InlinePropertySlots) == InlinePropertySlots);
  1665. Assert(
  1666. DynamicTypeHandler::RoundUpInlineSlotCapacity(static_cast<PropertyIndex>(InlinePropertySlots)) ==
  1667. InlinePropertySlots);
  1668. Assert(isSufficientSpaceForInlinePropertySlotsRef);
  1669. CompileAssert(
  1670. InlinePropertySlots <=
  1671. (UINT_MAX - (sizeof(T) + sizeof(SparseArraySegment<typename T::TElement>))) / sizeof(Var));
  1672. *isSufficientSpaceForInlinePropertySlotsRef =
  1673. sizeof(T) + InlinePropertySlots * sizeof(Var) + sizeof(SparseArraySegment<typename T::TElement>) <= allocationSize;
  1674. const size_t availableInlineElementSlots =
  1675. (
  1676. allocationSize -
  1677. (sizeof(T) + InlinePropertySlots * sizeof(Var) + sizeof(SparseArraySegment<typename T::TElement>))
  1678. ) / sizeof(typename T::TElement);
  1679. const uint availableInlineElementSlotsUint = static_cast<uint>(availableInlineElementSlots);
  1680. Assert(availableInlineElementSlotsUint == availableInlineElementSlots); // ensure no truncation above
  1681. return availableInlineElementSlotsUint;
  1682. }
  1683. template<class T, uint ConstInlinePropertySlots, bool UseDynamicInlinePropertySlots>
  1684. inline SparseArraySegment<typename T::TElement> *JavascriptArray::DetermineInlineHeadSegmentPointer(T *const array)
  1685. {
  1686. Assert(array);
  1687. Assert(VirtualTableInfo<T>::HasVirtualTable(array) || VirtualTableInfo<CrossSiteObject<T>>::HasVirtualTable(array));
  1688. Assert(!UseDynamicInlinePropertySlots || ConstInlinePropertySlots == 0);
  1689. Assert(
  1690. UseDynamicInlinePropertySlots ||
  1691. ConstInlinePropertySlots == array->GetTypeHandler()->GetInlineSlotCapacity());
  1692. const uint inlinePropertySlots =
  1693. UseDynamicInlinePropertySlots ? array->GetTypeHandler()->GetInlineSlotCapacity() : ConstInlinePropertySlots;
  1694. Assert(inlinePropertySlots == 0 || array->GetTypeHandler()->GetOffsetOfInlineSlots() == sizeof(T));
  1695. return
  1696. reinterpret_cast<SparseArraySegment<typename T::TElement> *>(
  1697. reinterpret_cast<Var *>(array + 1) + inlinePropertySlots);
  1698. }
  1699. //
  1700. // ItemTrace<T> specializations
  1701. //
  1702. template<>
  1703. inline uint32 JavascriptArray::ItemTrace<JavascriptArray>::GetLength(JavascriptArray* obj, ScriptContext* scriptContext)
  1704. {
  1705. return obj->GetLength();
  1706. }
  1707. template<>
  1708. inline BOOL JavascriptArray::ItemTrace<JavascriptArray>::GetItem(JavascriptArray* obj, uint32 index, Var* outVal, ScriptContext* scriptContext)
  1709. {
  1710. Assert(JavascriptArray::IsDirectAccessArray(obj));
  1711. return obj->DirectGetItemAtFull(index, outVal); // Note this does prototype lookup
  1712. }
  1713. template<>
  1714. inline uint32 JavascriptArray::ItemTrace<RecyclableObject>::GetLength(RecyclableObject* obj, ScriptContext* scriptContext)
  1715. {
  1716. return JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
  1717. }
  1718. template<>
  1719. inline BOOL JavascriptArray::ItemTrace<RecyclableObject>::GetItem(RecyclableObject* obj, uint32 index, Var* outVal, ScriptContext* scriptContext)
  1720. {
  1721. return JavascriptOperators::GetItem(obj, index, outVal, scriptContext);
  1722. }
  1723. } // namespace Js