JavascriptArray.inl 76 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940
  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. this->DirectSetItemAt(index, newValue);
  422. }
  423. template<typename T>
  424. inline void JavascriptArray::DirectSetItemInLastUsedSegmentAt(const uint32 offset, const T newValue)
  425. {
  426. Assert(!SparseArraySegment<T>::IsMissingItem(&newValue));
  427. SparseArraySegment<T> *const seg = (SparseArraySegment<T>*)GetLastUsedSegment();
  428. Assert(seg);
  429. Assert(offset < seg->size);
  430. Assert(!(HasNoMissingValues() &&
  431. offset < seg->length &&
  432. SparseArraySegment<T>::IsMissingItem(&seg->elements[offset]) &&
  433. seg == head));
  434. const bool scanForMissingValues = NeedScanForMissingValuesUponSetItem(seg, offset);
  435. DebugOnly(VerifyNotNeedMarshal(newValue));
  436. seg->elements[offset] = newValue;
  437. if (offset >= seg->length)
  438. {
  439. if(offset > seg->length && seg == head)
  440. {
  441. SetHasNoMissingValues(false);
  442. }
  443. seg->length = offset + 1;
  444. seg->CheckLengthvsSize();
  445. const uint32 itemIndex = seg->left + offset;
  446. if (this->length <= itemIndex)
  447. {
  448. this->length = itemIndex + 1;
  449. }
  450. }
  451. else if(scanForMissingValues)
  452. {
  453. ScanForMissingValues<T>();
  454. }
  455. }
  456. #if ENABLE_PROFILE_INFO
  457. template<typename T>
  458. inline void JavascriptArray::DirectProfiledSetItemInHeadSegmentAt(
  459. const uint32 offset,
  460. const T newValue,
  461. StElemInfo *const stElemInfo)
  462. {
  463. Assert(!SparseArraySegment<T>::IsMissingItem(&newValue));
  464. SparseArraySegment<T> *const seg = SparseArraySegment<T>::From(head);
  465. Assert(seg);
  466. Assert(offset < seg->size);
  467. Assert(!(HasNoMissingValues() &&
  468. offset < seg->length &&
  469. SparseArraySegment<T>::IsMissingItem(&seg->elements[offset])));
  470. Assert(stElemInfo);
  471. stElemInfo->filledMissingValue = offset < seg->length && SparseArraySegment<T>::IsMissingItem(&seg->elements[offset]);
  472. const bool scanForMissingValues = NeedScanForMissingValuesUponSetItem(seg, offset);
  473. DebugOnly(VerifyNotNeedMarshal(newValue));
  474. seg->elements[offset] = newValue;
  475. if (offset >= seg->length)
  476. {
  477. if(offset > seg->length)
  478. {
  479. SetHasNoMissingValues(false);
  480. }
  481. seg->length = offset + 1;
  482. seg->CheckLengthvsSize();
  483. const uint32 itemIndex = seg->left + offset;
  484. if (this->length <= itemIndex)
  485. {
  486. this->length = itemIndex + 1;
  487. }
  488. }
  489. else if(scanForMissingValues)
  490. {
  491. ScanForMissingValues<T>();
  492. }
  493. }
  494. #endif
  495. template<typename T>
  496. inline BOOL JavascriptArray::DirectGetItemAt(uint32 index, T* outVal)
  497. {
  498. #ifdef VALIDATE_ARRAY
  499. ValidateArray();
  500. #endif
  501. if (index >= length)
  502. {
  503. return false;
  504. }
  505. #ifdef VALIDATE_ARRAY
  506. T v_btree = NULL;
  507. SparseArraySegmentBase* seg_btree = nullptr;
  508. bool first_pass = true;
  509. #endif
  510. SparseArraySegmentBase* nextSeg;
  511. SegmentBTreeRoot * segmentMap = GetSegmentMap();
  512. if (segmentMap)
  513. {
  514. SparseArraySegmentBase* matchOrNextSeg;
  515. segmentMap->Find(index, nextSeg, matchOrNextSeg);
  516. if (!nextSeg)
  517. {
  518. nextSeg = matchOrNextSeg;
  519. }
  520. }
  521. else
  522. {
  523. #ifdef VALIDATE_ARRAY
  524. SECOND_PASS:
  525. #endif
  526. nextSeg = this->GetBeginLookupSegment(index, false);
  527. }
  528. uint probeCost = 0;
  529. while (nextSeg != nullptr && nextSeg->left <= index)
  530. {
  531. uint32 limit = nextSeg->left + nextSeg->length;
  532. if (index < limit)
  533. {
  534. const T * v = AddressOf(((SparseArraySegment<T>*)nextSeg)->elements[index - nextSeg->left]);
  535. this->SetLastUsedSegment(nextSeg);
  536. #ifdef VALIDATE_ARRAY
  537. Assert(segmentMap == GetSegmentMap());
  538. if (segmentMap && first_pass)
  539. {
  540. v_btree = *v;
  541. seg_btree= nextSeg;
  542. first_pass = false;
  543. goto SECOND_PASS;
  544. }
  545. else if (segmentMap && !first_pass)
  546. {
  547. Assert(seg_btree == nextSeg);
  548. }
  549. #endif
  550. if (SparseArraySegment<T>::IsMissingItem(v))
  551. {
  552. Assert(!(HasNoMissingValues() && nextSeg == head));
  553. return false;
  554. }
  555. *outVal = *v;
  556. return true;
  557. }
  558. nextSeg = nextSeg->next;
  559. Assert(segmentMap == GetSegmentMap());
  560. if (!segmentMap)
  561. {
  562. probeCost++;
  563. if (probeCost > SegmentBTree::GetLazyCrossOverLimit() && this->head != EmptySegment)
  564. {
  565. // Build a SegmentMap
  566. segmentMap = BuildSegmentMap();
  567. // Find the right segment
  568. SparseArraySegmentBase* matchOrNextSeg;
  569. segmentMap->Find(index, nextSeg, matchOrNextSeg);
  570. if (!nextSeg)
  571. {
  572. nextSeg = matchOrNextSeg;
  573. }
  574. }
  575. }
  576. }
  577. #ifdef VALIDATE_ARRAY
  578. Assert(segmentMap == GetSegmentMap());
  579. if (segmentMap && first_pass)
  580. {
  581. v_btree = NULL;
  582. seg_btree= nullptr;
  583. first_pass = false;
  584. goto SECOND_PASS;
  585. }
  586. else if (segmentMap && !first_pass)
  587. {
  588. Assert(v_btree == NULL && seg_btree == nullptr);
  589. }
  590. #endif
  591. return false;
  592. }
  593. template<typename T>
  594. void JavascriptArray::EnsureHead()
  595. {
  596. if (this->head == EmptySegment)
  597. {
  598. this->AllocateHead<T>();
  599. }
  600. }
  601. template<typename T>
  602. void JavascriptArray::AllocateHead()
  603. {
  604. Recycler* recycler = GetRecycler();
  605. uint32 allocLength;
  606. Assert(this->head == EmptySegment);
  607. if (this->length)
  608. {
  609. allocLength = this->length <= MaxInitialDenseLength ? this->length : SparseArraySegmentBase::HEAD_CHUNK_SIZE;
  610. this->head = SparseArraySegment<T>::AllocateSegment(recycler, 0, 0, allocLength, nullptr);
  611. }
  612. else
  613. {
  614. allocLength = SparseArraySegmentBase::HEAD_CHUNK_SIZE;
  615. this->head = SparseArraySegment<T>::AllocateSegment(recycler, 0, 0, allocLength, nullptr);
  616. }
  617. this->SetLastUsedSegment(this->head);
  618. SetHasNoMissingValues();
  619. }
  620. template<typename T>
  621. SparseArraySegment<T>* JavascriptArray::PrepareSegmentForMemOp(uint32 startIndex, uint32 length)
  622. {
  623. uint32 endIndex;
  624. if (UInt32Math::Add(startIndex, length - 1, &endIndex))
  625. {
  626. return nullptr;
  627. }
  628. if (endIndex >= this->length)
  629. {
  630. if (endIndex < JavascriptArray::InvalidIndex)
  631. {
  632. this->length = endIndex + 1;
  633. }
  634. else
  635. {
  636. return nullptr;
  637. }
  638. }
  639. // We are messing with the segments and the length of the array.
  640. // We must be certain we reach the end of this function without
  641. // any interruption to guaranty coherence of the array
  642. AutoDisableInterrupt autoDisableInterrupt(this->GetScriptContext()->GetThreadContext());
  643. this->EnsureHead<T>();
  644. Recycler* recycler = GetRecycler();
  645. //Find the segment where itemIndex is present or is at the boundary
  646. SparseArraySegment<T>* current = (SparseArraySegment<T>*)this->GetLastUsedSegment();
  647. SparseArraySegmentBase* prev = nullptr;
  648. SparseArraySegmentBase* startSeg = nullptr;
  649. SparseArraySegmentBase* endSeg = nullptr;
  650. SparseArraySegmentBase* startPrev = nullptr;
  651. uint32 growby, startOffset, endOffset;
  652. bool isAllocationSolelyInLastUsedSegment = false;
  653. // FindStartAndEndSegment
  654. {
  655. if (current->left > startIndex || endIndex >= current->left + current->size)
  656. {
  657. // The allocation may touch other segments, just start looking from head
  658. current = SparseArraySegment<T>::From(head);
  659. }
  660. else
  661. {
  662. // We are allocating solely in the last used segment
  663. startSeg = endSeg = current;
  664. current = nullptr;
  665. isAllocationSolelyInLastUsedSegment = true;
  666. }
  667. while (current != nullptr)
  668. {
  669. startOffset = startIndex - current->left;
  670. endOffset = endIndex - current->left;
  671. if (!startSeg)
  672. {
  673. if (startIndex <= current->left)
  674. {
  675. startPrev = prev;
  676. startSeg = current;
  677. }
  678. else if (startOffset <= current->size)
  679. {
  680. if ((nullptr == current->next) || (startIndex < current->next->left))
  681. {
  682. startPrev = prev;
  683. startSeg = current;
  684. }
  685. }
  686. }
  687. if (!endSeg)
  688. {
  689. if (endIndex <= current->left)
  690. {
  691. endSeg = current;
  692. break;
  693. }
  694. else if (endOffset <= current->size)
  695. {
  696. if ((nullptr == current->next) || (endIndex < current->next->left))
  697. {
  698. endSeg = current;
  699. break;
  700. }
  701. }
  702. }
  703. prev = current;
  704. current = SparseArraySegment<T>::From(current->next);
  705. }
  706. if (!startSeg && !endSeg)
  707. {
  708. startPrev = prev;
  709. }
  710. }
  711. if (startSeg == nullptr)
  712. {
  713. // if start index is greater than array length then we can add a new segment (or extend the last segment based on some heuristics)
  714. // ResizeArrayIfStartIsOutsideArrayLength;
  715. Assert(endSeg == nullptr);
  716. Assert(startIndex >= head->size);
  717. // Reallocate head if it meets a heuristics
  718. if (startPrev == head // prev segment is the head segment
  719. && !head->next // There is only one head segment in the array
  720. && startIndex - head->size <= MergeSegmentsLengthHeuristics // Distance to next index is relatively small
  721. )
  722. {
  723. SparseArraySegmentBase *oldHead = head;
  724. bool isInlineSegment = JavascriptArray::IsInlineSegment(oldHead, this);
  725. current = SparseArraySegment<T>::From(head)->GrowByMin(recycler, startIndex + length - head->size);
  726. current->length = endIndex + 1;
  727. current->CheckLengthvsSize();
  728. head = current;
  729. if (isInlineSegment)
  730. {
  731. this->ClearElements(oldHead, 0);
  732. }
  733. SetHasNoMissingValues(false);
  734. }
  735. else
  736. {
  737. //itemIndex is greater than the (left + size) of last segment in the linked list
  738. current = SparseArraySegment<T>::AllocateSegment(recycler, startIndex, length, (SparseArraySegment<T> *)nullptr);
  739. LinkSegments((Js::SparseArraySegment<T>*)startPrev, current);
  740. current->length = length;
  741. current->CheckLengthvsSize();
  742. if (current == head)
  743. {
  744. Assert(startIndex == 0);
  745. Assert(current->length == length);
  746. SetHasNoMissingValues();
  747. }
  748. }
  749. }
  750. else
  751. {
  752. // once we found the start segment we extend the start segment until startIndex+length . We don't care about what was there
  753. // as they will be overwritten by the memset/ memcopy. Then we need to append items from the (startIndex+length) to array.length
  754. // from the end segment to the new array
  755. // ExtendStartSegmentForMemOp
  756. SparseArraySegmentBase *oldStartSeg = startSeg;
  757. bool isInlineSegment = false;
  758. startOffset = startIndex - startSeg->left;
  759. if ((startIndex >= startSeg->left) && (startOffset < startSeg->size))
  760. {
  761. // startIndex is within startSeg
  762. if ((startOffset + length) > startSeg->size)
  763. {
  764. isInlineSegment = JavascriptArray::IsInlineSegment(startSeg, this);
  765. // if we don't have enough space in startSeg
  766. growby = length - (startSeg->size - startOffset);
  767. current = ((Js::SparseArraySegment<T>*)startSeg)->GrowByMin(recycler, growby);
  768. LinkSegments((Js::SparseArraySegment<T>*)startPrev, current);
  769. if (current == head)
  770. {
  771. if (startIndex > current->length)
  772. {
  773. // if it's the head segment and memset starts after the segment length, missing value is introduced
  774. SetHasNoMissingValues(false);
  775. }
  776. else if (!HasNoMissingValues())
  777. {
  778. // Have we overwritten all the missing values?
  779. if (!ScanForMissingValues<T>(0, startOffset))
  780. {
  781. SetHasNoMissingValues();
  782. }
  783. }
  784. }
  785. current->length = startOffset + length;
  786. current->CheckLengthvsSize();
  787. }
  788. else
  789. {
  790. // if we have enough space in the startseg
  791. current = (Js::SparseArraySegment<T>*)startSeg;
  792. if (current == head)
  793. {
  794. if (startIndex > current->length)
  795. {
  796. // if it's the head segment and memset starts after the segment length, missing value is introduced
  797. SetHasNoMissingValues(false);
  798. }
  799. else if (!HasNoMissingValues())
  800. {
  801. // Have we overwritten all the missing values?
  802. if (!ScanForMissingValues<T>(0, startOffset) && !ScanForMissingValues<T>(startOffset + length, current->length))
  803. {
  804. SetHasNoMissingValues();
  805. }
  806. }
  807. }
  808. current->length = current->length > (startOffset + length) ? current->length : (startOffset + length);
  809. current->CheckLengthvsSize();
  810. Assert(current == oldStartSeg);
  811. }
  812. }
  813. else if ((startIndex + 1) <= startSeg->left)
  814. {
  815. isInlineSegment = JavascriptArray::IsInlineSegment(startSeg, this);
  816. // startIndex is in between prev and startIndex
  817. current = SparseArraySegment<T>::template AllocateSegmentImpl<false>(recycler, startIndex, length, nullptr);
  818. LinkSegments((Js::SparseArraySegment<T>*)startPrev, current);
  819. if (current == head)
  820. {
  821. SetHasNoMissingValues();
  822. }
  823. }
  824. else
  825. {
  826. isInlineSegment = JavascriptArray::IsInlineSegment(startSeg, this);
  827. Assert(startIndex == startSeg->left + startSeg->size);
  828. current = ((Js::SparseArraySegment<T>*)startSeg)->GrowByMin(recycler, length);
  829. LinkSegments((Js::SparseArraySegment<T>*)startPrev, current);
  830. if (current == head)
  831. {
  832. if (startIndex > current->length)
  833. {
  834. // if it's the head segment and memset starts after the segment length, missing value is introduced
  835. SetHasNoMissingValues(false);
  836. }
  837. }
  838. current->length = startOffset + length;
  839. current->CheckLengthvsSize();
  840. }
  841. startSeg = current;
  842. Assert(startSeg != oldStartSeg || !isInlineSegment); // ensure isInlineSegment implies startSeg != oldStartSeg
  843. if (isInlineSegment)
  844. {
  845. this->ClearElements(oldStartSeg, 0);
  846. }
  847. // AppendLeftOverItemsFromEndSegment
  848. SparseArraySegmentBase *oldCurrent = current;
  849. isInlineSegment = false;
  850. if (!endSeg)
  851. {
  852. // end is beyond the length of the array
  853. Assert(endIndex == (current->left + current->length - 1));
  854. current->next = nullptr;
  855. Assert(oldCurrent == current);
  856. }
  857. else
  858. {
  859. endOffset = endIndex - endSeg->left;
  860. startOffset = startIndex - current->left;
  861. if ((endIndex >= endSeg->left) && (endOffset < endSeg->size))
  862. {
  863. // endIndex is within endSeg
  864. if (endSeg->length - 1 > endOffset)
  865. {
  866. if (startSeg != endSeg)
  867. {
  868. isInlineSegment = JavascriptArray::IsInlineSegment(current, this);
  869. // we have some leftover items on endseg
  870. growby = (endSeg->length - endOffset - 1);
  871. current = current->GrowByMin(recycler, growby);
  872. CopyArray(current->elements + startOffset + length, growby,
  873. ((Js::SparseArraySegment<T>*)endSeg)->elements + endOffset + 1, growby);
  874. LinkSegments((Js::SparseArraySegment<T>*)startPrev, current);
  875. current->length = startOffset + length + growby;
  876. current->CheckLengthvsSize();
  877. if (current == head && HasNoMissingValues())
  878. {
  879. if (ScanForMissingValues<T>(startOffset + length, current->length))
  880. {
  881. SetHasNoMissingValues(false);
  882. }
  883. }
  884. }
  885. }
  886. current->next = endSeg->next;
  887. }
  888. else if ((endIndex + 1) <= endSeg->left)
  889. {
  890. // endIndex is between endSeg and the segment before
  891. if (endIndex + 1 == endSeg->left && current == head)
  892. {
  893. isInlineSegment = JavascriptArray::IsInlineSegment(current, this);
  894. // extend current to hold endSeg
  895. growby = endSeg->length;
  896. current = current->GrowByMin(recycler, growby);
  897. CopyArray(current->elements + endIndex + 1, endSeg->length,
  898. ((Js::SparseArraySegment<T>*)endSeg)->elements, endSeg->length);
  899. LinkSegments((Js::SparseArraySegment<T>*)startPrev, current);
  900. if (current == head && HasNoMissingValues())
  901. {
  902. if (ScanForMissingValues<T>(endIndex + 1, endIndex + growby))
  903. {
  904. SetHasNoMissingValues(false);
  905. }
  906. }
  907. current->length = endIndex + growby + 1;
  908. current->CheckLengthvsSize();
  909. current->next = endSeg->next;
  910. }
  911. else
  912. {
  913. current->next = endSeg;
  914. Assert(oldCurrent == current);
  915. }
  916. }
  917. else
  918. {
  919. //endIndex is at the boundary of endSeg segment at the left + size
  920. Assert(endIndex == endSeg->left + endSeg->size);
  921. current->next = endSeg->next;
  922. Assert(oldCurrent == current);
  923. }
  924. }
  925. Assert(oldCurrent != current || !isInlineSegment); // ensure isInlineSegment implies oldCurrent != current
  926. if (isInlineSegment)
  927. {
  928. this->ClearElements(oldCurrent, 0);
  929. }
  930. }
  931. Assert(current);
  932. Assert(current->left <= startIndex);
  933. Assert((startIndex - current->left) < current->size);
  934. // If we are solely using the last used segment, make sure there were no allocation done
  935. Assert(!isAllocationSolelyInLastUsedSegment || (current == startSeg && current == endSeg));
  936. autoDisableInterrupt.Completed();
  937. return current;
  938. }
  939. template<typename T>
  940. bool JavascriptArray::DirectSetItemAtRangeFromArray(uint32 toStartIndex, uint32 length, JavascriptArray *fromArray, uint32 fromStartIndex)
  941. {
  942. if (length == 0)
  943. {
  944. return true;
  945. }
  946. // Do not do a memcopy from an array that has missing values
  947. if (fromArray == nullptr || fromArray == this || !fromArray->HasNoMissingValues())
  948. {
  949. return false;
  950. }
  951. bool isBtree = false;
  952. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  953. isBtree = Js::Configuration::Global.flags.ForceArrayBTree;
  954. #endif
  955. if (GetSegmentMap() || fromArray->GetSegmentMap() || isBtree)
  956. {
  957. for (uint i = 0; i < length; i++)
  958. {
  959. T val;
  960. if (!fromArray->DirectGetItemAt(fromStartIndex + i, &val))
  961. {
  962. return false;
  963. }
  964. DirectSetItem_Full(toStartIndex + i, val);
  965. }
  966. return true;
  967. }
  968. const auto isSegmentValid = [length](Js::SparseArraySegment<T>* segment, uint32 startIndex) {
  969. uint32 end, segmentEnd;
  970. // Check the segment is big enough
  971. return (
  972. segment &&
  973. !UInt32Math::Add(startIndex, length, &end) &&
  974. !UInt32Math::Add(segment->left, segment->length, &segmentEnd) &&
  975. startIndex >= segment->left &&
  976. startIndex < segmentEnd &&
  977. segmentEnd >= end
  978. );
  979. };
  980. // Check if the head segment of the fromArray has everything we need to copy
  981. Js::SparseArraySegment<T>* fromSegment = (Js::SparseArraySegment<T>*)fromArray->GetHead();
  982. if (!isSegmentValid(fromSegment, fromStartIndex))
  983. {
  984. return false;
  985. }
  986. // Check the from segment first so we don't prepare the toSegment in case it is invalid
  987. SparseArraySegment<T> *toSegment = PrepareSegmentForMemOp<T>(toStartIndex, length);
  988. if (!isSegmentValid(toSegment, toStartIndex))
  989. {
  990. return false;
  991. }
  992. Assert(GetSegmentMap() == nullptr && fromArray->GetSegmentMap() == nullptr);
  993. int memcopySize = length;
  994. int toStartOffset = toStartIndex - toSegment->left;
  995. int fromStartOffset = fromStartIndex - fromSegment->left;
  996. Assert((fromStartOffset + length) <= fromSegment->length);
  997. CopyArray(
  998. toSegment->elements + toStartOffset,
  999. toSegment->size - toStartOffset,
  1000. fromSegment->elements + fromStartOffset,
  1001. memcopySize
  1002. );
  1003. fromArray->SetLastUsedSegment(fromSegment);
  1004. this->SetLastUsedSegment(toSegment);
  1005. #if DBG
  1006. if (Js::Configuration::Global.flags.MemOpMissingValueValidate)
  1007. {
  1008. if (toSegment == head)
  1009. {
  1010. Assert(ScanForMissingValues<T>(0, this->length) != HasNoMissingValues());
  1011. }
  1012. }
  1013. #endif
  1014. return true;
  1015. }
  1016. template<typename T>
  1017. bool JavascriptArray::DirectSetItemAtRange(uint32 startIndex, uint32 length, T newValue)
  1018. {
  1019. bool isBtree = false;
  1020. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  1021. isBtree = Js::Configuration::Global.flags.ForceArrayBTree;
  1022. #endif
  1023. if (GetSegmentMap() || isBtree)
  1024. {
  1025. for (uint i = startIndex; i < startIndex + length; i++)
  1026. {
  1027. DirectSetItem_Full<T>(i, newValue);
  1028. }
  1029. return true;
  1030. }
  1031. if (startIndex == 0 && head != EmptySegment && length < head->size)
  1032. {
  1033. CopyValueToSegmentBuferNoCheck(SparseArraySegment<T>::From(head)->elements, length, newValue);
  1034. if (length > this->length)
  1035. {
  1036. this->length = length;
  1037. }
  1038. if (length > head->length)
  1039. {
  1040. head->length = length;
  1041. head->CheckLengthvsSize();
  1042. }
  1043. if (!HasNoMissingValues())
  1044. {
  1045. if (ScanForMissingValues<T>(length, head->length) == false)
  1046. {
  1047. SetHasNoMissingValues(true);
  1048. }
  1049. }
  1050. this->SetLastUsedSegment(head);
  1051. }
  1052. else if (startIndex == 0 && length > this->length && (head == EmptySegment || length > head->size))
  1053. {
  1054. Recycler *recycler = GetRecycler();
  1055. SparseArraySegmentBase* current = SparseArraySegment<T>::AllocateSegment(recycler, 0, length, (SparseArraySegment<T> *)nullptr);
  1056. this->SetHeadAndLastUsedSegment(current);
  1057. this->length = length;
  1058. Assert(!HasSegmentMap());
  1059. SetHasNoMissingValues(true);
  1060. CopyValueToSegmentBuferNoCheck(((Js::SparseArraySegment<T>*)current)->elements, length, newValue);
  1061. }
  1062. else
  1063. {
  1064. return DirectSetItemAtRangeFull<T>(startIndex, length, newValue);
  1065. }
  1066. return true;
  1067. }
  1068. template<typename T>
  1069. bool JavascriptArray::DirectSetItemAtRangeFull(uint32 startIndex, uint32 length, T newValue)
  1070. {
  1071. if (length == 0)
  1072. {
  1073. return true;
  1074. }
  1075. Assert(!GetSegmentMap());
  1076. SparseArraySegment<T> *current = PrepareSegmentForMemOp<T>(startIndex, length);
  1077. if (current == nullptr)
  1078. {
  1079. return false;
  1080. }
  1081. Assert(current->left + current->length >= startIndex + length);
  1082. Field(T)* segmentCopyStart = current->elements + (startIndex - current->left);
  1083. CopyValueToSegmentBuferNoCheck(segmentCopyStart, length, newValue);
  1084. this->SetLastUsedSegment(current);
  1085. #if DBG
  1086. if (Js::Configuration::Global.flags.MemOpMissingValueValidate)
  1087. {
  1088. if (current == head)
  1089. {
  1090. Assert(ScanForMissingValues<T>(0, this->length) != HasNoMissingValues());
  1091. }
  1092. }
  1093. #endif
  1094. return true;
  1095. }
  1096. template<typename T>
  1097. void JavascriptArray::DirectSetItem_Full(uint32 itemIndex, T newValue)
  1098. {
  1099. Assert(!SparseArraySegment<T>::IsMissingItem(&newValue));
  1100. DebugOnly(VerifyNotNeedMarshal(newValue));
  1101. this->EnsureHead<T>();
  1102. AnalysisAssert(head);
  1103. #ifdef VALIDATE_ARRAY
  1104. ValidateArray();
  1105. #endif
  1106. if (itemIndex >= this->length)
  1107. {
  1108. if (itemIndex != JavascriptArray::InvalidIndex)
  1109. {
  1110. this->length = itemIndex + 1;
  1111. }
  1112. else
  1113. {
  1114. JavascriptError::ThrowRangeError(this->GetScriptContext(), JSERR_ArrayLengthAssignIncorrect);
  1115. }
  1116. }
  1117. Recycler* recycler = GetRecycler();
  1118. //Find the segment where itemIndex is present or is at the boundary
  1119. SparseArraySegment<T>* current = (SparseArraySegment<T>*)this->GetBeginLookupSegment(itemIndex, false);
  1120. // If it doesn't fit in current chunk (watch for overflow), start from beginning as we'll
  1121. // need the prev
  1122. if (current->left + current->size > current->left || itemIndex >= current->left + current->size)
  1123. {
  1124. current = SparseArraySegment<T>::From(head);
  1125. }
  1126. SparseArraySegmentBase* prev = nullptr;
  1127. #ifdef VALIDATE_ARRAY
  1128. SparseArraySegmentBase* current_btree = nullptr;
  1129. SparseArraySegmentBase* prev_btree = nullptr;
  1130. bool first_pass = true;
  1131. #endif
  1132. SegmentBTreeRoot * segmentMap = GetSegmentMap();
  1133. if (segmentMap)
  1134. {
  1135. SparseArraySegmentBase* prevSeg = nullptr;
  1136. SparseArraySegmentBase* currentBase = current;
  1137. segmentMap->Find(itemIndex, prevSeg, currentBase);
  1138. current = (SparseArraySegment<T>*)currentBase;
  1139. Assert(!prevSeg || prevSeg->next == current);
  1140. if (prevSeg)
  1141. {
  1142. bool noExactMatch = !current || itemIndex < current->left;
  1143. Assert(prevSeg->left + prevSeg->size >= prevSeg->left);
  1144. bool extendPrevSeg = itemIndex <= prevSeg->left + prevSeg->size;
  1145. if (noExactMatch && extendPrevSeg)
  1146. {
  1147. current = SparseArraySegment<T>::From(head);
  1148. prev = nullptr;
  1149. if (prevSeg != head)
  1150. {
  1151. // Since we are going to extend prevSeg we need the
  1152. // address of it's left neighbor's next pointer
  1153. currentBase = current;
  1154. segmentMap->Find(prevSeg->left, prevSeg, currentBase);
  1155. current = (SparseArraySegment<T>*)currentBase;
  1156. Assert(prevSeg && prevSeg->next == current);
  1157. prev = prevSeg;
  1158. }
  1159. }
  1160. else
  1161. {
  1162. prev = prevSeg;
  1163. }
  1164. }
  1165. else
  1166. {
  1167. Assert(current == head);
  1168. }
  1169. }
  1170. #ifdef VALIDATE_ARRAY
  1171. SECOND_PASS:
  1172. if (!first_pass)
  1173. {
  1174. current = (SparseArraySegment<T>*)this->GetBeginLookupSegment(itemIndex, false);
  1175. // If it doesn't fit in current chunk (watch for overflow), start from beginning as we'll
  1176. // need the prev
  1177. if (current->left + current->size > current->left || itemIndex >= current->left + current->size)
  1178. {
  1179. current = SparseArraySegment<T>::From(head);
  1180. }
  1181. prev = nullptr;
  1182. }
  1183. #endif
  1184. uint probeCost = 0;
  1185. while(current != nullptr)
  1186. {
  1187. uint32 offset = itemIndex - current->left;
  1188. if (itemIndex < current->left)
  1189. {
  1190. break;
  1191. }
  1192. else if (offset <= current->size)
  1193. {
  1194. if ((nullptr == current->next) || (itemIndex < current->next->left))
  1195. {
  1196. break;
  1197. }
  1198. }
  1199. prev = current;
  1200. current = SparseArraySegment<T>::From(current->next);
  1201. Assert(segmentMap == GetSegmentMap());
  1202. if (!segmentMap)
  1203. {
  1204. probeCost++;
  1205. if (probeCost > SegmentBTree::GetLazyCrossOverLimit())
  1206. {
  1207. // Build a SegmentMap
  1208. segmentMap = BuildSegmentMap();
  1209. SparseArraySegmentBase* prevSeg = nullptr;
  1210. SparseArraySegmentBase* currentBase = current;
  1211. segmentMap->Find(itemIndex, prevSeg, currentBase);
  1212. current = (SparseArraySegment<T>*)currentBase;
  1213. Assert(prevSeg->next == current);
  1214. if (prevSeg)
  1215. {
  1216. bool noExactMatch = !current || itemIndex < current->left;
  1217. Assert(prevSeg->left + prevSeg->size >= prevSeg->left);
  1218. bool extendPrevSeg = itemIndex <= prevSeg->left + prevSeg->size;
  1219. if (noExactMatch && extendPrevSeg)
  1220. {
  1221. current = SparseArraySegment<T>::From(head);
  1222. prev = nullptr;
  1223. if (prevSeg != head)
  1224. {
  1225. // Since we are going to extend prevSeg we need the
  1226. // address of its left neighbor's next pointer
  1227. currentBase = current;
  1228. segmentMap->Find(prevSeg->left, prevSeg, currentBase);
  1229. current = (SparseArraySegment<T>*)currentBase;
  1230. Assert(prevSeg->next == current);
  1231. prev = prevSeg;
  1232. }
  1233. }
  1234. else
  1235. {
  1236. prev = prevSeg;
  1237. }
  1238. }
  1239. else
  1240. {
  1241. Assert(current == head);
  1242. }
  1243. }
  1244. }
  1245. }
  1246. #ifdef VALIDATE_ARRAY
  1247. Assert(segmentMap == GetSegmentMap());
  1248. if (segmentMap && first_pass)
  1249. {
  1250. current_btree = current;
  1251. prev_btree = prev;
  1252. first_pass = false;
  1253. goto SECOND_PASS;
  1254. }
  1255. else if (segmentMap)
  1256. {
  1257. Assert(current_btree == current && prev_btree == prev);
  1258. }
  1259. #endif
  1260. if (current != nullptr)
  1261. {
  1262. uint32 offset = itemIndex - current->left;
  1263. if ((itemIndex >= current->left) && (offset < current->size))
  1264. {
  1265. //itemIndex lies in the segment
  1266. Assert(!(HasNoMissingValues() &&
  1267. offset < current->length &&
  1268. SparseArraySegment<T>::IsMissingItem(&current->elements[offset]) &&
  1269. current == head));
  1270. if(offset > current->length && current == head)
  1271. {
  1272. SetHasNoMissingValues(false);
  1273. }
  1274. const bool scanForMissingValues = NeedScanForMissingValuesUponSetItem(current, offset);
  1275. ((SparseArraySegment<T>*)current)->SetElement(recycler, itemIndex, newValue);
  1276. if(scanForMissingValues)
  1277. {
  1278. ScanForMissingValues<T>();
  1279. }
  1280. }
  1281. else if ((itemIndex + 1) < current->left)
  1282. {
  1283. //itemIndex lies in between current and previous segment
  1284. SparseArraySegment<T>* newSeg = SparseArraySegment<T>::AllocateSegment(recycler, prev, itemIndex);
  1285. newSeg->SetElement(recycler, itemIndex, newValue);
  1286. newSeg->next = current;
  1287. LinkSegments((SparseArraySegment<T>*)prev, newSeg);
  1288. current = newSeg;
  1289. TryAddToSegmentMap(recycler, newSeg);
  1290. Assert(current != head);
  1291. }
  1292. else
  1293. {
  1294. //itemIndex is at boundary of current segment either at the left + size or at left - 1;
  1295. Assert((itemIndex == current->left + current->size) || (itemIndex + 1 == current->left));
  1296. SparseArraySegment<T>* next = SparseArraySegment<T>::From(current->next);
  1297. Assert(segmentMap == GetSegmentMap());
  1298. if (!segmentMap && next != nullptr && (itemIndex + 1) == next->left)
  1299. {
  1300. // Don't merge segments if we are using a segmentMap
  1301. //Special case where we need to merge two segments. itemIndex is on the size boundary
  1302. //of the current segment & left boundary of the next
  1303. const bool currentWasFull = current->length == current->size;
  1304. Assert(itemIndex == current->left + current->size);
  1305. SparseArraySegmentBase* oldSegment = current;
  1306. bool isInlineSegment = JavascriptArray::IsInlineSegment(oldSegment, this);
  1307. current = SparseArraySegment<T>::CopySegment(recycler, (SparseArraySegment<T>*)current, next->left, next, next->left, next->length);
  1308. current->next = next->next;
  1309. current->SetElement(recycler, itemIndex, newValue);
  1310. LinkSegments((SparseArraySegment<T>*)prev, current);
  1311. if(HasNoMissingValues() && current == head)
  1312. {
  1313. // We just merged the head segment and its next segment and filled the only missing value in-between the
  1314. // two segments. We already know that the previous head segment does not have any missing values. If the
  1315. // previous head segment was full, scan the new head segment starting from the merge point for missing
  1316. // values. If the previous head segment was not full, then merging the segments would have created
  1317. // missing values.
  1318. SetHasNoMissingValues(false);
  1319. if(currentWasFull)
  1320. {
  1321. ScanForMissingValues<T>(offset + 1);
  1322. }
  1323. }
  1324. if (isInlineSegment && current != oldSegment)
  1325. {
  1326. this->ClearElements(oldSegment, 0);
  1327. }
  1328. }
  1329. else
  1330. {
  1331. if(offset > current->length && current == head)
  1332. {
  1333. SetHasNoMissingValues(false);
  1334. }
  1335. const bool currentWasHead = current == head;
  1336. SparseArraySegmentBase* oldSegment = current;
  1337. bool isInlineSegment = JavascriptArray::IsInlineSegment(oldSegment, this);
  1338. uint originalKey = oldSegment->left;
  1339. current = current->SetElementGrow(recycler, prev, itemIndex, newValue);
  1340. Assert(segmentMap == GetSegmentMap());
  1341. if (segmentMap)
  1342. {
  1343. segmentMap->SwapSegment(originalKey, oldSegment, current);
  1344. }
  1345. LinkSegments((SparseArraySegment<T>*)prev, current);
  1346. // Scan for missing values when the current segment was grown at the beginning and made the head segment
  1347. if(!currentWasHead && current == head)
  1348. {
  1349. ScanForMissingValues<T>();
  1350. }
  1351. if (isInlineSegment)
  1352. {
  1353. this->ClearElements(oldSegment, 0);
  1354. }
  1355. }
  1356. }
  1357. }
  1358. else
  1359. {
  1360. // Reallocate head if need it meets a heuristics
  1361. Assert(itemIndex >= head->size);
  1362. if (prev == head // prev segment is the head segment
  1363. && !head->next // There is only one head segment in the array
  1364. && !segmentMap // There is no segmentMap which makes sure that array is not highly fragmented.
  1365. && itemIndex - head->size <= MergeSegmentsLengthHeuristics // Distance to next index is relatively small
  1366. )
  1367. {
  1368. current = SparseArraySegment<T>::From(head)->GrowByMin(recycler, itemIndex + 1 - head->size);
  1369. current->elements[itemIndex] = newValue;
  1370. current->length = itemIndex + 1;
  1371. current->CheckLengthvsSize();
  1372. if (JavascriptArray::IsInlineSegment(head, this))
  1373. {
  1374. this->ClearElements(head, 0);
  1375. }
  1376. head = current;
  1377. SetHasNoMissingValues(false);
  1378. }
  1379. else
  1380. {
  1381. //itemIndex is greater than the (left + size) of last segment in the linked list
  1382. current = SparseArraySegment<T>::AllocateSegment(recycler, itemIndex, 1, (SparseArraySegment<T> *)nullptr);
  1383. current->SetElement(recycler, itemIndex, newValue);
  1384. LinkSegments((SparseArraySegment<T>*)prev, current);
  1385. TryAddToSegmentMap(recycler, current);
  1386. if(current == head)
  1387. {
  1388. Assert(itemIndex == 0);
  1389. Assert(current->length == 1);
  1390. SetHasNoMissingValues();
  1391. }
  1392. }
  1393. }
  1394. this->SetLastUsedSegment(current);
  1395. #ifdef VALIDATE_ARRAY
  1396. ValidateArray();
  1397. #endif
  1398. }
  1399. template<typename T>
  1400. bool JavascriptArray::NeedScanForMissingValuesUponSetItem(SparseArraySegment<T> *const segment, const uint32 offset) const
  1401. {
  1402. Assert(segment);
  1403. // Scan for missing values upon SetItem when a missing value is being filled and the surrounding values are not missing,
  1404. // as this could be the last missing value that is being filled
  1405. return
  1406. offset < segment->length &&
  1407. SparseArraySegment<T>::IsMissingItem(&segment->elements[offset]) &&
  1408. (offset == 0 || !SparseArraySegment<T>::IsMissingItem(&segment->elements[offset - 1])) &&
  1409. (offset == segment->length - 1 || !SparseArraySegment<T>::IsMissingItem(&segment->elements[offset + 1])) &&
  1410. segment == head;
  1411. }
  1412. template<typename T>
  1413. void JavascriptArray::ScanForMissingValues(const uint startIndex)
  1414. {
  1415. Assert(head);
  1416. Assert(!HasNoMissingValues());
  1417. SparseArraySegment<T> *const segment = SparseArraySegment<T>::From(head);
  1418. const uint segmentLength = segment->length;
  1419. const Field(T) * const segmentElements = segment->elements;
  1420. for(uint i = startIndex; i < segmentLength; ++i)
  1421. {
  1422. if(SparseArraySegment<T>::IsMissingItem(&segmentElements[i]))
  1423. {
  1424. return;
  1425. }
  1426. }
  1427. SetHasNoMissingValues();
  1428. }
  1429. template<typename T>
  1430. bool JavascriptArray::ScanForMissingValues(const uint startIndex, const uint endIndex)
  1431. {
  1432. Assert(head);
  1433. //Assert(!HasNoMissingValues());
  1434. SparseArraySegment<T> *const segment = SparseArraySegment<T>::From(head);
  1435. const Field(T) *const segmentElements = segment->elements;
  1436. for (uint i = startIndex; i < endIndex; ++i)
  1437. {
  1438. if (SparseArraySegment<T>::IsMissingItem(&segmentElements[i]))
  1439. {
  1440. return true;
  1441. }
  1442. }
  1443. return false;
  1444. }
  1445. inline void JavascriptArray::DirectSetItemIfNotExist(uint32 index, Var newValue)
  1446. {
  1447. Assert(VirtualTableInfo<JavascriptArray>::HasVirtualTable(this));
  1448. Var oldValue;
  1449. if (!DirectGetItemAt(index, &oldValue))
  1450. {
  1451. DirectSetItemAt(index, newValue);
  1452. }
  1453. }
  1454. //Grow the array head and try to set at the boundary
  1455. template<typename unitType, typename classname>
  1456. inline BOOL JavascriptArray::TryGrowHeadSegmentAndSetItem(uint32 indexInt, unitType iValue)
  1457. {
  1458. SparseArraySegment<unitType> *current = SparseArraySegment<unitType>::From(head);
  1459. if (indexInt == current->length // index is at the boundary of size & length
  1460. && current->size // Make sure its not empty segment.
  1461. && !current->next // There is only head segment.
  1462. && current->length == current->size // Why did we miss the fastpath?
  1463. && !SparseArraySegment<unitType>::IsMissingItem(&iValue)) // value to set is not a missing value.
  1464. {
  1465. SparseArraySegmentBase *oldCurrent = current;
  1466. bool isInlineSegment = JavascriptArray::IsInlineSegment(oldCurrent, this);
  1467. current= current->GrowByMin(this->GetRecycler(), indexInt + 1);
  1468. DebugOnly(VerifyNotNeedMarshal(iValue));
  1469. current->elements[indexInt] = iValue;
  1470. current->length = indexInt + 1;
  1471. current->CheckLengthvsSize();
  1472. // There is only a head segment in this condition A segment map is not necessary
  1473. // and most likely invalid at this point. Also we are setting the head and lastUsedSegment
  1474. // to the same segment. Precedent in the rest of the code base dictates the use of
  1475. // SetHeadAndLastUsedSegment which asserts if a segment map exists.
  1476. ClearSegmentMap();
  1477. SetHeadAndLastUsedSegment(current);
  1478. if (isInlineSegment)
  1479. {
  1480. this->ClearElements(oldCurrent, 0);
  1481. }
  1482. if (this->length <= indexInt)
  1483. {
  1484. this->length = indexInt + 1;
  1485. }
  1486. #ifdef VALIDATE_ARRAY
  1487. ValidateArray();
  1488. #endif
  1489. return true;
  1490. }
  1491. return false;
  1492. }
  1493. //
  1494. // JavascriptArray::IndexTrace specialized on uint32 (small index)
  1495. //
  1496. template<>
  1497. inline Var JavascriptArray::IndexTrace<uint32>::ToNumber(const uint32& index, ScriptContext* scriptContext)
  1498. {
  1499. return JavascriptNumber::ToVar(index, scriptContext);
  1500. }
  1501. template<>
  1502. inline BOOL JavascriptArray::IndexTrace<uint32>::GetItem(JavascriptArray* arr, const uint32& index, Var* outVal)
  1503. {
  1504. return arr->DirectGetItemAt(index, outVal);
  1505. }
  1506. template<>
  1507. inline BOOL JavascriptArray::IndexTrace<uint32>::SetItem(JavascriptArray* arr, const uint32& index, Var newValue)
  1508. {
  1509. return arr->SetItem(index, newValue, PropertyOperation_None);
  1510. }
  1511. template<>
  1512. inline void JavascriptArray::IndexTrace<uint32>::SetItemIfNotExist(JavascriptArray* arr, const uint32& index, Var newValue)
  1513. {
  1514. arr->DirectSetItemIfNotExist(index, newValue);
  1515. }
  1516. template<>
  1517. inline BOOL JavascriptArray::IndexTrace<uint32>::DeleteItem(JavascriptArray* arr, const uint32& index)
  1518. {
  1519. switch (arr->GetTypeId())
  1520. {
  1521. case TypeIds_Array:
  1522. return arr->DirectDeleteItemAt<Var>(index);
  1523. case TypeIds_NativeIntArray:
  1524. return arr->DirectDeleteItemAt<int32>(index);
  1525. case TypeIds_NativeFloatArray:
  1526. return arr->DirectDeleteItemAt<double>(index);
  1527. default:
  1528. Assert(FALSE);
  1529. return FALSE;
  1530. }
  1531. }
  1532. template<>
  1533. inline BOOL JavascriptArray::IndexTrace<uint32>::SetItem(RecyclableObject* obj, const uint32& index, Var newValue, PropertyOperationFlags flags)
  1534. {
  1535. ScriptContext* requestContext = obj->GetScriptContext();
  1536. return JavascriptOperators::SetItem(obj, obj, index, newValue, requestContext, flags);
  1537. }
  1538. template<>
  1539. inline BOOL JavascriptArray::IndexTrace<uint32>::DeleteItem(RecyclableObject* obj, const uint32& index, PropertyOperationFlags flags)
  1540. {
  1541. return JavascriptOperators::DeleteItem(obj, index, flags);
  1542. }
  1543. //
  1544. // JavascriptArray::IndexTrace specialized on BigIndex
  1545. //
  1546. template<>
  1547. inline Var JavascriptArray::IndexTrace<JavascriptArray::BigIndex>::ToNumber(const JavascriptArray::BigIndex& index, ScriptContext* scriptContext)
  1548. {
  1549. return index.ToNumber(scriptContext);
  1550. }
  1551. template<>
  1552. inline BOOL JavascriptArray::IndexTrace<JavascriptArray::BigIndex>::GetItem(JavascriptArray* arr, const JavascriptArray::BigIndex& index, Var* outVal)
  1553. {
  1554. return index.GetItem(arr, outVal);
  1555. }
  1556. template<>
  1557. inline BOOL JavascriptArray::IndexTrace<JavascriptArray::BigIndex>::SetItem(JavascriptArray* arr, const JavascriptArray::BigIndex& index, Var newValue)
  1558. {
  1559. return index.SetItem(arr, newValue);
  1560. }
  1561. template<>
  1562. inline void JavascriptArray::IndexTrace<JavascriptArray::BigIndex>::SetItemIfNotExist(JavascriptArray* arr, const JavascriptArray::BigIndex& index, Var newValue)
  1563. {
  1564. index.SetItemIfNotExist(arr, newValue);
  1565. }
  1566. template<>
  1567. inline BOOL JavascriptArray::IndexTrace<JavascriptArray::BigIndex>::DeleteItem(JavascriptArray* arr, const JavascriptArray::BigIndex& index)
  1568. {
  1569. return index.DeleteItem(arr);
  1570. }
  1571. template<>
  1572. inline BOOL JavascriptArray::IndexTrace<JavascriptArray::BigIndex>::SetItem(RecyclableObject* obj, const JavascriptArray::BigIndex& index, Var newValue, PropertyOperationFlags flags)
  1573. {
  1574. return index.SetItem(obj, newValue, flags);
  1575. }
  1576. template<>
  1577. inline BOOL JavascriptArray::IndexTrace<JavascriptArray::BigIndex>::DeleteItem(RecyclableObject* obj, const JavascriptArray::BigIndex& index, PropertyOperationFlags flags)
  1578. {
  1579. return index.DeleteItem(obj, flags);
  1580. }
  1581. template<class T, uint InlinePropertySlots>
  1582. inline size_t JavascriptArray::DetermineAllocationSize(
  1583. const uint inlineElementSlots,
  1584. size_t *const allocationPlusSizeRef,
  1585. uint *const alignedInlineElementSlotsRef)
  1586. {
  1587. CompileAssert(static_cast<PropertyIndex>(InlinePropertySlots) == InlinePropertySlots);
  1588. Assert(
  1589. DynamicTypeHandler::RoundUpInlineSlotCapacity(static_cast<PropertyIndex>(InlinePropertySlots)) ==
  1590. InlinePropertySlots);
  1591. CompileAssert(
  1592. InlinePropertySlots <=
  1593. (UINT_MAX - (sizeof(T) + sizeof(SparseArraySegment<typename T::TElement>))) / sizeof(Var));
  1594. const uint objectSize =
  1595. sizeof(T) + sizeof(SparseArraySegment<typename T::TElement>) + InlinePropertySlots * sizeof(Var);
  1596. size_t totalSize = UInt32Math::MulAdd<sizeof(typename T::TElement), objectSize>(inlineElementSlots);
  1597. #if defined(TARGET_64)
  1598. // On x64, the total size won't be anywhere near AllocSizeMath::MaxMemory on x64, so no need to check
  1599. totalSize = HeapInfo::GetAlignedSizeNoCheck(totalSize);
  1600. #else
  1601. totalSize = HeapInfo::GetAlignedSize(totalSize);
  1602. #endif
  1603. if(allocationPlusSizeRef)
  1604. {
  1605. *allocationPlusSizeRef = totalSize - sizeof(T);
  1606. }
  1607. if(alignedInlineElementSlotsRef)
  1608. {
  1609. const size_t alignedInlineElementSlots = (totalSize - objectSize) / sizeof(typename T::TElement);
  1610. *alignedInlineElementSlotsRef = static_cast<uint>(alignedInlineElementSlots);
  1611. Assert(*alignedInlineElementSlotsRef == alignedInlineElementSlots); // ensure no truncation above
  1612. }
  1613. return totalSize;
  1614. }
  1615. template<class ArrayType>
  1616. void JavascriptArray::EnsureCalculationOfAllocationBuckets()
  1617. {
  1618. uint temp;
  1619. for (uint8 i = 0;i < ArrayType::AllocationBucketsCount;i++)
  1620. {
  1621. ArrayType::allocationBuckets[i][AllocationSizeIndex] = (uint)DetermineAllocationSize<ArrayType, 0>(ArrayType::allocationBuckets[i][AllocationBucketIndex], nullptr, &temp);
  1622. ArrayType::allocationBuckets[i][MissingElementsCountIndex] = temp;
  1623. }
  1624. }
  1625. template<class ArrayType, uint InlinePropertySlots>
  1626. inline size_t JavascriptArray::DetermineAllocationSizeForArrayObjects(
  1627. const uint inlineElementSlots,
  1628. size_t *const allocationPlusSizeRef,
  1629. uint *const alignedInlineElementSlotsRef)
  1630. {
  1631. uint8 bucketsCount = ArrayType::AllocationBucketsCount;
  1632. EnsureCalculationOfAllocationBuckets<ArrayType>();
  1633. if (inlineElementSlots >= 0 && inlineElementSlots <= ArrayType::allocationBuckets[bucketsCount - 1][AllocationBucketIndex])
  1634. {
  1635. for (uint8 i = 0;i < bucketsCount;i++)
  1636. {
  1637. uint elementsCountToInitialize = ArrayType::allocationBuckets[i][MissingElementsCountIndex];
  1638. uint allocationSize = ArrayType::allocationBuckets[i][AllocationSizeIndex];
  1639. // Ensure we already have allocation size calculated and within range
  1640. Assert(elementsCountToInitialize > 0 && elementsCountToInitialize <= ArrayType::allocationBuckets[bucketsCount - 1][MissingElementsCountIndex]);
  1641. Assert(allocationSize > 0 && allocationSize <= ArrayType::allocationBuckets[bucketsCount - 1][AllocationSizeIndex]);
  1642. if (inlineElementSlots <= ArrayType::allocationBuckets[i][AllocationBucketIndex])
  1643. {
  1644. if (alignedInlineElementSlotsRef)
  1645. {
  1646. *alignedInlineElementSlotsRef = elementsCountToInitialize;
  1647. }
  1648. if (allocationPlusSizeRef)
  1649. {
  1650. *allocationPlusSizeRef = allocationSize - sizeof(ArrayType);
  1651. }
  1652. return allocationSize;
  1653. }
  1654. }
  1655. }
  1656. return DetermineAllocationSize<ArrayType, InlinePropertySlots>(inlineElementSlots, allocationPlusSizeRef, alignedInlineElementSlotsRef);
  1657. }
  1658. template<class T, uint InlinePropertySlots>
  1659. inline uint JavascriptArray::DetermineAvailableInlineElementSlots(
  1660. const size_t allocationSize,
  1661. bool *const isSufficientSpaceForInlinePropertySlotsRef)
  1662. {
  1663. CompileAssert(static_cast<PropertyIndex>(InlinePropertySlots) == InlinePropertySlots);
  1664. Assert(
  1665. DynamicTypeHandler::RoundUpInlineSlotCapacity(static_cast<PropertyIndex>(InlinePropertySlots)) ==
  1666. InlinePropertySlots);
  1667. Assert(isSufficientSpaceForInlinePropertySlotsRef);
  1668. CompileAssert(
  1669. InlinePropertySlots <=
  1670. (UINT_MAX - (sizeof(T) + sizeof(SparseArraySegment<typename T::TElement>))) / sizeof(Var));
  1671. *isSufficientSpaceForInlinePropertySlotsRef =
  1672. sizeof(T) + InlinePropertySlots * sizeof(Var) + sizeof(SparseArraySegment<typename T::TElement>) <= allocationSize;
  1673. const size_t availableInlineElementSlots =
  1674. (
  1675. allocationSize -
  1676. (sizeof(T) + InlinePropertySlots * sizeof(Var) + sizeof(SparseArraySegment<typename T::TElement>))
  1677. ) / sizeof(typename T::TElement);
  1678. const uint availableInlineElementSlotsUint = static_cast<uint>(availableInlineElementSlots);
  1679. Assert(availableInlineElementSlotsUint == availableInlineElementSlots); // ensure no truncation above
  1680. return availableInlineElementSlotsUint;
  1681. }
  1682. template<class T, uint ConstInlinePropertySlots, bool UseDynamicInlinePropertySlots>
  1683. inline SparseArraySegment<typename T::TElement> *JavascriptArray::DetermineInlineHeadSegmentPointer(T *const array)
  1684. {
  1685. Assert(array);
  1686. Assert(VirtualTableInfo<T>::HasVirtualTable(array) || VirtualTableInfo<CrossSiteObject<T>>::HasVirtualTable(array));
  1687. Assert(!UseDynamicInlinePropertySlots || ConstInlinePropertySlots == 0);
  1688. Assert(
  1689. UseDynamicInlinePropertySlots ||
  1690. ConstInlinePropertySlots == array->GetTypeHandler()->GetInlineSlotCapacity());
  1691. const uint inlinePropertySlots =
  1692. UseDynamicInlinePropertySlots ? array->GetTypeHandler()->GetInlineSlotCapacity() : ConstInlinePropertySlots;
  1693. Assert(inlinePropertySlots == 0 || array->GetTypeHandler()->GetOffsetOfInlineSlots() == sizeof(T));
  1694. return
  1695. reinterpret_cast<SparseArraySegment<typename T::TElement> *>(
  1696. reinterpret_cast<Var *>(array + 1) + inlinePropertySlots);
  1697. }
  1698. //
  1699. // ItemTrace<T> specializations
  1700. //
  1701. template<>
  1702. inline uint32 JavascriptArray::ItemTrace<JavascriptArray>::GetLength(JavascriptArray* obj, ScriptContext* scriptContext)
  1703. {
  1704. return obj->GetLength();
  1705. }
  1706. template<>
  1707. inline BOOL JavascriptArray::ItemTrace<JavascriptArray>::GetItem(JavascriptArray* obj, uint32 index, Var* outVal, ScriptContext* scriptContext)
  1708. {
  1709. Assert(JavascriptArray::IsDirectAccessArray(obj));
  1710. return obj->DirectGetItemAtFull(index, outVal); // Note this does prototype lookup
  1711. }
  1712. template<>
  1713. inline uint32 JavascriptArray::ItemTrace<RecyclableObject>::GetLength(RecyclableObject* obj, ScriptContext* scriptContext)
  1714. {
  1715. return JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext);
  1716. }
  1717. template<>
  1718. inline BOOL JavascriptArray::ItemTrace<RecyclableObject>::GetItem(RecyclableObject* obj, uint32 index, Var* outVal, ScriptContext* scriptContext)
  1719. {
  1720. return JavascriptOperators::GetItem(obj, index, outVal, scriptContext);
  1721. }
  1722. } // namespace Js