2
0

ArrayInstance.cs 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377
  1. using System.Collections;
  2. using System.Diagnostics.CodeAnalysis;
  3. using System.Runtime.CompilerServices;
  4. using Jint.Native.Object;
  5. using Jint.Native.Symbol;
  6. using Jint.Runtime;
  7. using Jint.Runtime.Descriptors;
  8. namespace Jint.Native.Array
  9. {
  10. public class ArrayInstance : ObjectInstance, IEnumerable<JsValue>
  11. {
  12. internal PropertyDescriptor? _length;
  13. private const int MaxDenseArrayLength = 10_000_000;
  14. // we have dense and sparse, we usually can start with dense and fall back to sparse when necessary
  15. // when we have plain JsValues, _denseValues is used - if any operation occurs which requires setting more property flags
  16. // we convert to _sparse and _denseValues is set to null - it will be a slow array
  17. internal JsValue?[]? _dense;
  18. private Dictionary<uint, PropertyDescriptor?>? _sparse;
  19. private ObjectChangeFlags _objectChangeFlags;
  20. private ArrayConstructor? _constructor;
  21. private protected ArrayInstance(Engine engine, InternalTypes type) : base(engine, type: type)
  22. {
  23. _dense = System.Array.Empty<JsValue?>();
  24. }
  25. private protected ArrayInstance(Engine engine, uint capacity = 0, uint length = 0) : base(engine, type: InternalTypes.Object | InternalTypes.Array)
  26. {
  27. InitializePrototypeAndValidateCapacity(engine, capacity);
  28. if (capacity < MaxDenseArrayLength)
  29. {
  30. _dense = capacity > 0 ? new JsValue?[capacity] : System.Array.Empty<JsValue?>();
  31. }
  32. else
  33. {
  34. _sparse = new Dictionary<uint, PropertyDescriptor?>(1024);
  35. }
  36. _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
  37. }
  38. private protected ArrayInstance(Engine engine, JsValue[] items) : base(engine, type: InternalTypes.Object | InternalTypes.Array)
  39. {
  40. InitializePrototypeAndValidateCapacity(engine, capacity: 0);
  41. _dense = items;
  42. _length = new PropertyDescriptor(items.Length, PropertyFlag.OnlyWritable);
  43. }
  44. private void InitializePrototypeAndValidateCapacity(Engine engine, uint capacity)
  45. {
  46. _constructor = engine.Realm.Intrinsics.Array;
  47. _prototype = _constructor.PrototypeObject;
  48. if (capacity > 0 && capacity > engine.Options.Constraints.MaxArraySize)
  49. {
  50. ThrowMaximumArraySizeReachedException(engine, capacity);
  51. }
  52. }
  53. internal sealed override bool IsArrayLike => true;
  54. internal sealed override bool IsArray() => true;
  55. internal sealed override bool HasOriginalIterator
  56. => ReferenceEquals(Get(GlobalSymbolRegistry.Iterator), _constructor?.PrototypeObject._originalIteratorFunction);
  57. /// <summary>
  58. /// Checks whether there have been changes to object prototype chain which could render fast access patterns impossible.
  59. /// </summary>
  60. internal bool CanUseFastAccess
  61. {
  62. get
  63. {
  64. if ((_objectChangeFlags & ObjectChangeFlags.NonDefaultDataDescriptorUsage) != ObjectChangeFlags.None)
  65. {
  66. // could be a mutating property for example, length might change, not safe anymore
  67. return false;
  68. }
  69. if (_prototype is not ArrayPrototype arrayPrototype
  70. || !ReferenceEquals(_prototype, _constructor?.PrototypeObject))
  71. {
  72. // somebody has switched prototype
  73. return false;
  74. }
  75. if ((arrayPrototype._objectChangeFlags & ObjectChangeFlags.ArrayIndex) != ObjectChangeFlags.None)
  76. {
  77. // maybe somebody moved integer property to prototype? not safe anymore
  78. return false;
  79. }
  80. if (arrayPrototype.Prototype is not ObjectPrototype arrayPrototypePrototype
  81. || !ReferenceEquals(arrayPrototypePrototype, _constructor.PrototypeObject.Prototype))
  82. {
  83. return false;
  84. }
  85. return (arrayPrototypePrototype._objectChangeFlags & ObjectChangeFlags.ArrayIndex) == ObjectChangeFlags.None;
  86. }
  87. }
  88. public sealed override bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
  89. {
  90. if (CommonProperties.Length.Equals(property))
  91. {
  92. return DefineLength(desc);
  93. }
  94. var isArrayIndex = IsArrayIndex(property, out var index);
  95. TrackChanges(property, desc, isArrayIndex);
  96. if (isArrayIndex)
  97. {
  98. ConvertToSparse();
  99. return DefineOwnProperty(index, desc);
  100. }
  101. return base.DefineOwnProperty(property, desc);
  102. }
  103. private bool DefineLength(PropertyDescriptor desc)
  104. {
  105. var value = desc.Value;
  106. if (value is null)
  107. {
  108. return base.DefineOwnProperty(CommonProperties.Length, desc);
  109. }
  110. var newLenDesc = new PropertyDescriptor(desc);
  111. uint newLen = TypeConverter.ToUint32(value);
  112. if (newLen != TypeConverter.ToNumber(value))
  113. {
  114. ExceptionHelper.ThrowRangeError(_engine.Realm);
  115. }
  116. var oldLenDesc = _length;
  117. var oldLen = (uint) TypeConverter.ToNumber(oldLenDesc!.Value);
  118. newLenDesc.Value = newLen;
  119. if (newLen >= oldLen)
  120. {
  121. return base.DefineOwnProperty(CommonProperties.Length, newLenDesc);
  122. }
  123. if (!oldLenDesc.Writable)
  124. {
  125. return false;
  126. }
  127. bool newWritable;
  128. if (!newLenDesc.WritableSet || newLenDesc.Writable)
  129. {
  130. newWritable = true;
  131. }
  132. else
  133. {
  134. newWritable = false;
  135. newLenDesc.Writable = true;
  136. }
  137. var succeeded = base.DefineOwnProperty(CommonProperties.Length, newLenDesc);
  138. if (!succeeded)
  139. {
  140. return false;
  141. }
  142. var count = _dense?.Length ?? _sparse!.Count;
  143. if (count < oldLen - newLen)
  144. {
  145. if (_dense != null)
  146. {
  147. for (uint keyIndex = 0; keyIndex < _dense.Length; ++keyIndex)
  148. {
  149. if (_dense[keyIndex] is null)
  150. {
  151. continue;
  152. }
  153. // is it the index of the array
  154. if (keyIndex >= newLen && keyIndex < oldLen)
  155. {
  156. var deleteSucceeded = Delete(keyIndex);
  157. if (!deleteSucceeded)
  158. {
  159. newLenDesc.Value = keyIndex + 1;
  160. if (!newWritable)
  161. {
  162. newLenDesc.Writable = false;
  163. }
  164. base.DefineOwnProperty(CommonProperties.Length, newLenDesc);
  165. return false;
  166. }
  167. }
  168. }
  169. }
  170. else
  171. {
  172. // in the case of sparse arrays, treat each concrete element instead of
  173. // iterating over all indexes
  174. var keys = new List<uint>(_sparse!.Keys);
  175. var keysCount = keys.Count;
  176. for (var i = 0; i < keysCount; i++)
  177. {
  178. var keyIndex = keys[i];
  179. // is it the index of the array
  180. if (keyIndex >= newLen && keyIndex < oldLen)
  181. {
  182. var deleteSucceeded = Delete(TypeConverter.ToString(keyIndex));
  183. if (!deleteSucceeded)
  184. {
  185. newLenDesc.Value = JsNumber.Create(keyIndex + 1);
  186. if (!newWritable)
  187. {
  188. newLenDesc.Writable = false;
  189. }
  190. base.DefineOwnProperty(CommonProperties.Length, newLenDesc);
  191. return false;
  192. }
  193. }
  194. }
  195. }
  196. }
  197. else
  198. {
  199. while (newLen < oldLen)
  200. {
  201. // algorithm as per the spec
  202. oldLen--;
  203. var deleteSucceeded = Delete(oldLen);
  204. if (!deleteSucceeded)
  205. {
  206. newLenDesc.Value = oldLen + 1;
  207. if (!newWritable)
  208. {
  209. newLenDesc.Writable = false;
  210. }
  211. base.DefineOwnProperty(CommonProperties.Length, newLenDesc);
  212. return false;
  213. }
  214. }
  215. }
  216. if (!newWritable)
  217. {
  218. base.DefineOwnProperty(CommonProperties.Length, new PropertyDescriptor(value: null, PropertyFlag.WritableSet));
  219. }
  220. return true;
  221. }
  222. private bool DefineOwnProperty(uint index, PropertyDescriptor desc)
  223. {
  224. var oldLenDesc = _length;
  225. var oldLen = (uint) TypeConverter.ToNumber(oldLenDesc!.Value);
  226. if (index >= oldLen && !oldLenDesc.Writable)
  227. {
  228. return false;
  229. }
  230. var succeeded = base.DefineOwnProperty(index, desc);
  231. if (!succeeded)
  232. {
  233. return false;
  234. }
  235. if (index >= oldLen)
  236. {
  237. oldLenDesc.Value = index + 1;
  238. base.DefineOwnProperty(CommonProperties.Length, oldLenDesc);
  239. }
  240. return true;
  241. }
  242. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  243. internal override uint GetLength() => (uint) GetJsNumberLength()._value;
  244. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  245. private JsNumber GetJsNumberLength() => _length is null ? JsNumber.PositiveZero : (JsNumber) _length._value!;
  246. protected sealed override bool TryGetProperty(JsValue property, [NotNullWhen(true)] out PropertyDescriptor? descriptor)
  247. {
  248. if (CommonProperties.Length.Equals(property))
  249. {
  250. descriptor = _length;
  251. return _length != null;
  252. }
  253. return base.TryGetProperty(property, out descriptor);
  254. }
  255. public sealed override List<JsValue> GetOwnPropertyKeys(Types types = Types.Empty | Types.String | Types.Symbol)
  256. {
  257. if ((types & Types.String) == Types.Empty)
  258. {
  259. return base.GetOwnPropertyKeys(types);
  260. }
  261. var temp = _dense;
  262. var properties = new List<JsValue>(temp?.Length ?? 0 + 1);
  263. if (temp != null)
  264. {
  265. var length = System.Math.Min(temp.Length, GetLength());
  266. for (var i = 0; i < length; i++)
  267. {
  268. if (temp[i] is not null)
  269. {
  270. properties.Add(JsString.Create(i));
  271. }
  272. }
  273. }
  274. else
  275. {
  276. foreach (var entry in _sparse!)
  277. {
  278. properties.Add(JsString.Create(entry.Key));
  279. }
  280. }
  281. if (_length != null)
  282. {
  283. properties.Add(CommonProperties.Length);
  284. }
  285. properties.AddRange(base.GetOwnPropertyKeys(types));
  286. return properties;
  287. }
  288. /// <summary>
  289. /// Returns key and value pairs for actual array entries, excludes parent and optionally length.
  290. /// </summary>
  291. /// <param name="includeLength">Whether to return length and it's value.</param>
  292. public IEnumerable<KeyValuePair<string, JsValue>> GetEntries(bool includeLength = false)
  293. {
  294. foreach (var (index, value) in this.Enumerate())
  295. {
  296. yield return new KeyValuePair<string, JsValue>(TypeConverter.ToString(index), value);
  297. }
  298. if (includeLength && _length != null)
  299. {
  300. yield return new KeyValuePair<string, JsValue>(CommonProperties.Length._value, _length.Value);
  301. }
  302. }
  303. public sealed override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
  304. {
  305. var temp = _dense;
  306. if (temp != null)
  307. {
  308. var length = System.Math.Min(temp.Length, GetLength());
  309. for (uint i = 0; i < length; i++)
  310. {
  311. var value = temp[i];
  312. if (value is not null)
  313. {
  314. if (_sparse is null || !_sparse.TryGetValue(i, out var descriptor) || descriptor is null)
  315. {
  316. _sparse ??= new Dictionary<uint, PropertyDescriptor?>();
  317. _sparse[i] = descriptor = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
  318. }
  319. yield return new KeyValuePair<JsValue, PropertyDescriptor>(TypeConverter.ToString(i), descriptor);
  320. }
  321. }
  322. }
  323. else if (_sparse != null)
  324. {
  325. foreach (var entry in _sparse)
  326. {
  327. var value = entry.Value;
  328. if (value is not null)
  329. {
  330. yield return new KeyValuePair<JsValue, PropertyDescriptor>(TypeConverter.ToString(entry.Key), value);
  331. }
  332. }
  333. }
  334. if (_length != null)
  335. {
  336. yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Length, _length);
  337. }
  338. foreach (var entry in base.GetOwnProperties())
  339. {
  340. yield return entry;
  341. }
  342. }
  343. public sealed override PropertyDescriptor GetOwnProperty(JsValue property)
  344. {
  345. if (CommonProperties.Length.Equals(property))
  346. {
  347. return _length ?? PropertyDescriptor.Undefined;
  348. }
  349. if (IsArrayIndex(property, out var index))
  350. {
  351. if (TryGetDescriptor(index, createIfMissing: true, out var result))
  352. {
  353. return result;
  354. }
  355. return PropertyDescriptor.Undefined;
  356. }
  357. return base.GetOwnProperty(property);
  358. }
  359. internal JsValue Get(uint index)
  360. {
  361. if (!TryGetValue(index, out var value))
  362. {
  363. value = Prototype?.Get(JsString.Create(index)) ?? Undefined;
  364. }
  365. return value;
  366. }
  367. public sealed override JsValue Get(JsValue property, JsValue receiver)
  368. {
  369. if (IsSafeSelfTarget(receiver) && IsArrayIndex(property, out var index) && TryGetValue(index, out var value))
  370. {
  371. return value;
  372. }
  373. if (CommonProperties.Length.Equals(property))
  374. {
  375. var length = _length?._value;
  376. if (length is not null)
  377. {
  378. return length;
  379. }
  380. }
  381. return base.Get(property, receiver);
  382. }
  383. public sealed override bool Set(JsValue property, JsValue value, JsValue receiver)
  384. {
  385. var isSafeSelfTarget = IsSafeSelfTarget(receiver);
  386. if (isSafeSelfTarget && CanUseFastAccess)
  387. {
  388. if (!ReferenceEquals(property, CommonProperties.Length) && IsArrayIndex(property, out var index))
  389. {
  390. SetIndexValue(index, value, updateLength: true);
  391. return true;
  392. }
  393. if (CommonProperties.Length.Equals(property)
  394. && _length is { Writable: true }
  395. && value is JsNumber jsNumber
  396. && jsNumber.IsInteger()
  397. && jsNumber._value <= MaxDenseArrayLength
  398. && jsNumber._value >= GetLength())
  399. {
  400. // we don't need explicit resize
  401. _length.Value = jsNumber;
  402. return true;
  403. }
  404. }
  405. // slow path
  406. return base.Set(property, value, receiver);
  407. }
  408. private bool IsSafeSelfTarget(JsValue receiver) => ReferenceEquals(receiver, this) && Extensible;
  409. public sealed override bool HasProperty(JsValue property)
  410. {
  411. if (IsArrayIndex(property, out var index) && GetValue(index, unwrapFromNonDataDescriptor: false) is not null)
  412. {
  413. return true;
  414. }
  415. return base.HasProperty(property);
  416. }
  417. internal bool HasProperty(ulong index)
  418. {
  419. if (index < uint.MaxValue)
  420. {
  421. var temp = _dense;
  422. if (temp != null)
  423. {
  424. if (index < (uint) temp.Length && temp[index] is not null)
  425. {
  426. return true;
  427. }
  428. }
  429. else if (_sparse!.ContainsKey((uint) index))
  430. {
  431. return true;
  432. }
  433. }
  434. return base.HasProperty(index);
  435. }
  436. protected internal sealed override void SetOwnProperty(JsValue property, PropertyDescriptor desc)
  437. {
  438. var isArrayIndex = IsArrayIndex(property, out var index);
  439. TrackChanges(property, desc, isArrayIndex);
  440. if (isArrayIndex)
  441. {
  442. WriteArrayValue(index, desc);
  443. }
  444. else if (CommonProperties.Length.Equals(property))
  445. {
  446. _length = desc;
  447. }
  448. else
  449. {
  450. base.SetOwnProperty(property, desc);
  451. }
  452. }
  453. private void TrackChanges(JsValue property, PropertyDescriptor desc, bool isArrayIndex)
  454. {
  455. EnsureInitialized();
  456. if (isArrayIndex)
  457. {
  458. if (!desc.IsDefaultArrayValueDescriptor() && desc.Flags != PropertyFlag.None)
  459. {
  460. _objectChangeFlags |= ObjectChangeFlags.NonDefaultDataDescriptorUsage;
  461. }
  462. if (GetType() != typeof(JsArray))
  463. {
  464. _objectChangeFlags |= ObjectChangeFlags.ArrayIndex;
  465. }
  466. }
  467. else
  468. {
  469. _objectChangeFlags |= property.IsSymbol() ? ObjectChangeFlags.Symbol : ObjectChangeFlags.Property;
  470. }
  471. }
  472. public sealed override void RemoveOwnProperty(JsValue property)
  473. {
  474. if (IsArrayIndex(property, out var index))
  475. {
  476. Delete(index);
  477. }
  478. if (CommonProperties.Length.Equals(property))
  479. {
  480. _length = null;
  481. }
  482. base.RemoveOwnProperty(property);
  483. }
  484. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  485. internal static bool IsArrayIndex(JsValue p, out uint index)
  486. {
  487. if (p.IsNumber())
  488. {
  489. var value = ((JsNumber) p)._value;
  490. var intValue = (uint) value;
  491. index = intValue;
  492. return value == intValue && intValue != uint.MaxValue;
  493. }
  494. index = !p.IsSymbol() ? ParseArrayIndex(p.ToString()) : uint.MaxValue;
  495. return index != uint.MaxValue;
  496. // 15.4 - Use an optimized version of the specification
  497. // return TypeConverter.ToString(index) == TypeConverter.ToString(p) && index != uint.MaxValue;
  498. }
  499. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  500. internal static uint ParseArrayIndex(string p)
  501. {
  502. if (p.Length == 0 || p.Length > 1 && !IsInRange(p[0], '1', '9') || !uint.TryParse(p, out var d))
  503. {
  504. return uint.MaxValue;
  505. }
  506. return d;
  507. }
  508. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  509. private static bool IsInRange(char c, char min, char max) => c - (uint) min <= max - (uint) min;
  510. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  511. internal void SetIndexValue(uint index, JsValue? value, bool updateLength)
  512. {
  513. if (updateLength)
  514. {
  515. EnsureCorrectLength(index);
  516. }
  517. WriteArrayValue(index, value);
  518. }
  519. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  520. private void EnsureCorrectLength(uint index)
  521. {
  522. var length = GetLength();
  523. if (index >= length)
  524. {
  525. SetLength(index + 1);
  526. }
  527. }
  528. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  529. internal void SetLength(ulong length) => SetLength(JsNumber.Create(length));
  530. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  531. internal void SetLength(JsNumber length)
  532. {
  533. if (Extensible && _length!._flags == PropertyFlag.OnlyWritable)
  534. {
  535. _length!.Value = length;
  536. }
  537. else
  538. {
  539. // slow path
  540. Set(CommonProperties.Length, length, true);
  541. }
  542. }
  543. internal uint GetSmallestIndex()
  544. {
  545. if (_dense != null)
  546. {
  547. return 0;
  548. }
  549. uint smallest = 0;
  550. // only try to help if collection reasonable small
  551. if (_sparse!.Count > 0 && _sparse.Count < 100 && !_sparse.ContainsKey(0))
  552. {
  553. smallest = uint.MaxValue;
  554. foreach (var key in _sparse.Keys)
  555. {
  556. smallest = System.Math.Min(key, smallest);
  557. }
  558. }
  559. return smallest;
  560. }
  561. internal bool DeletePropertyOrThrow(uint index)
  562. {
  563. if (!Delete(index))
  564. {
  565. ExceptionHelper.ThrowTypeError(_engine.Realm);
  566. }
  567. return true;
  568. }
  569. private bool Delete(uint index) => Delete(index, unwrapFromNonDataDescriptor: false, out _);
  570. private bool Delete(uint index, bool unwrapFromNonDataDescriptor, out JsValue? deletedValue)
  571. {
  572. TryGetDescriptor(index, createIfMissing: false, out var desc);
  573. // check fast path
  574. var temp = _dense;
  575. if (temp != null)
  576. {
  577. if (index < (uint) temp.Length)
  578. {
  579. if (desc is null || desc.Configurable)
  580. {
  581. deletedValue = temp[index];
  582. temp[index] = null;
  583. return true;
  584. }
  585. }
  586. }
  587. if (desc is null)
  588. {
  589. deletedValue = null;
  590. return true;
  591. }
  592. if (desc.Configurable)
  593. {
  594. _sparse!.Remove(index);
  595. deletedValue = desc.IsDataDescriptor() || unwrapFromNonDataDescriptor
  596. ? UnwrapJsValue(desc)
  597. : null;
  598. return true;
  599. }
  600. deletedValue = null;
  601. return false;
  602. }
  603. internal bool DeleteAt(uint index)
  604. {
  605. var temp = _dense;
  606. if (temp != null)
  607. {
  608. if (index < (uint) temp.Length)
  609. {
  610. temp[index] = null;
  611. return true;
  612. }
  613. }
  614. else
  615. {
  616. return _sparse!.Remove(index);
  617. }
  618. return false;
  619. }
  620. private bool TryGetDescriptor(uint index, bool createIfMissing, [NotNullWhen(true)] out PropertyDescriptor? descriptor)
  621. {
  622. if (!createIfMissing && _sparse is null)
  623. {
  624. descriptor = null;
  625. return false;
  626. }
  627. descriptor = null;
  628. var temp = _dense;
  629. if (temp != null)
  630. {
  631. if (index < (uint) temp.Length)
  632. {
  633. var value = temp[index];
  634. if (value is not null)
  635. {
  636. if (_sparse is null || !_sparse.TryGetValue(index, out descriptor) || descriptor is null)
  637. {
  638. _sparse ??= new Dictionary<uint, PropertyDescriptor?>();
  639. _sparse[index] = descriptor = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
  640. }
  641. descriptor.Value = value;
  642. return true;
  643. }
  644. }
  645. return false;
  646. }
  647. _sparse?.TryGetValue(index, out descriptor);
  648. return descriptor is not null;
  649. }
  650. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  651. internal bool TryGetValue(uint index, out JsValue value)
  652. {
  653. value = GetValue(index, unwrapFromNonDataDescriptor: true)!;
  654. if (value is not null)
  655. {
  656. return true;
  657. }
  658. return TryGetValueUnlikely(index, out value);
  659. }
  660. [MethodImpl(MethodImplOptions.NoInlining)]
  661. private bool TryGetValueUnlikely(uint index, out JsValue value)
  662. {
  663. if (!CanUseFastAccess)
  664. {
  665. // slow path must be checked for prototype
  666. var prototype = Prototype;
  667. JsValue key = index;
  668. while (prototype is not null)
  669. {
  670. var desc = prototype.GetOwnProperty(key);
  671. if (desc != PropertyDescriptor.Undefined)
  672. {
  673. value = UnwrapJsValue(desc);
  674. return true;
  675. }
  676. prototype = prototype.Prototype;
  677. }
  678. }
  679. value = Undefined;
  680. return false;
  681. }
  682. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  683. private JsValue? GetValue(uint index, bool unwrapFromNonDataDescriptor)
  684. {
  685. var temp = _dense;
  686. if (temp != null)
  687. {
  688. if (index < (uint) temp.Length)
  689. {
  690. return temp[index];
  691. }
  692. return null;
  693. }
  694. return GetValueUnlikely(index, unwrapFromNonDataDescriptor);
  695. }
  696. [MethodImpl(MethodImplOptions.NoInlining)]
  697. private JsValue? GetValueUnlikely(uint index, bool unwrapFromNonDataDescriptor)
  698. {
  699. JsValue? value = null;
  700. if (_sparse!.TryGetValue(index, out var descriptor) && descriptor != null)
  701. {
  702. value = descriptor.IsDataDescriptor() || unwrapFromNonDataDescriptor
  703. ? UnwrapJsValue(descriptor)
  704. : null;
  705. }
  706. return value;
  707. }
  708. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  709. private void WriteArrayValue(uint index, PropertyDescriptor descriptor)
  710. {
  711. var temp = _dense;
  712. if (temp != null && descriptor.IsDefaultArrayValueDescriptor())
  713. {
  714. if (index < (uint) temp.Length)
  715. {
  716. temp[index] = descriptor.Value;
  717. }
  718. else
  719. {
  720. WriteArrayValueUnlikely(index, descriptor.Value);
  721. }
  722. }
  723. else
  724. {
  725. WriteArrayValueUnlikely(index, descriptor);
  726. }
  727. }
  728. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  729. private void WriteArrayValue(uint index, JsValue? value)
  730. {
  731. var temp = _dense;
  732. if (temp != null)
  733. {
  734. if (index < (uint) temp.Length)
  735. {
  736. temp[index] = value;
  737. return;
  738. }
  739. }
  740. WriteArrayValueUnlikely(index, value);
  741. }
  742. [MethodImpl(MethodImplOptions.NoInlining)]
  743. private void WriteArrayValueUnlikely(uint index, JsValue? value)
  744. {
  745. // calculate eagerly so we know if we outgrow
  746. var dense = _dense;
  747. var newSize = dense != null && index >= (uint) dense.Length
  748. ? System.Math.Max(index, System.Math.Max(dense.Length, 2)) * 2
  749. : 0;
  750. var canUseDense = dense != null
  751. && index < MaxDenseArrayLength
  752. && newSize < MaxDenseArrayLength
  753. && index < dense.Length + 50; // looks sparse
  754. if (canUseDense)
  755. {
  756. EnsureCapacity((uint) newSize);
  757. _dense![index] = value;
  758. }
  759. else
  760. {
  761. ConvertToSparse();
  762. _sparse![index] = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
  763. }
  764. }
  765. private void WriteArrayValueUnlikely(uint index, PropertyDescriptor? value)
  766. {
  767. if (_sparse == null)
  768. {
  769. ConvertToSparse();
  770. }
  771. _sparse![index] = value;
  772. }
  773. private void ConvertToSparse()
  774. {
  775. // need to move data
  776. var temp = _dense;
  777. if (temp is null)
  778. {
  779. return;
  780. }
  781. _sparse ??= new Dictionary<uint, PropertyDescriptor?>();
  782. for (uint i = 0; i < (uint) temp.Length; ++i)
  783. {
  784. var value = temp[i];
  785. if (value is not null)
  786. {
  787. _sparse.TryGetValue(i, out var descriptor);
  788. descriptor ??= new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
  789. descriptor.Value = value;
  790. _sparse[i] = descriptor;
  791. }
  792. else
  793. {
  794. _sparse.Remove(i);
  795. }
  796. }
  797. _dense = null;
  798. }
  799. internal void EnsureCapacity(uint capacity, bool force = false)
  800. {
  801. var dense = _dense;
  802. if (dense is null)
  803. {
  804. return;
  805. }
  806. if (!force && (capacity > MaxDenseArrayLength || capacity <= (uint) dense.Length))
  807. {
  808. return;
  809. }
  810. if (capacity > _engine.Options.Constraints.MaxArraySize)
  811. {
  812. ThrowMaximumArraySizeReachedException(_engine, capacity);
  813. }
  814. // need to grow
  815. var newArray = new JsValue[capacity];
  816. System.Array.Copy(dense, newArray, dense.Length);
  817. _dense = newArray;
  818. }
  819. public JsValue[] ToArray()
  820. {
  821. var length = GetLength();
  822. var array = new JsValue[length];
  823. for (uint i = 0; i < length; i++)
  824. {
  825. TryGetValue(i, out var outValue);
  826. array[i] = outValue;
  827. }
  828. return array;
  829. }
  830. IEnumerator IEnumerable.GetEnumerator()
  831. {
  832. return GetEnumerator();
  833. }
  834. public IEnumerator<JsValue> GetEnumerator()
  835. {
  836. foreach (var (_, value) in this.Enumerate())
  837. {
  838. yield return value;
  839. }
  840. }
  841. private readonly record struct IndexedEntry(int Index, JsValue Value);
  842. private IEnumerable<IndexedEntry> Enumerate()
  843. {
  844. if (!CanUseFastAccess)
  845. {
  846. // slow path where prototype is also checked
  847. var length = GetLength();
  848. for (uint i = 0; i < length; i++)
  849. {
  850. TryGetValue(i, out var outValue);
  851. yield return new IndexedEntry((int) i, outValue);
  852. }
  853. yield break;
  854. }
  855. var temp = _dense;
  856. if (temp != null)
  857. {
  858. var length = System.Math.Min(temp.Length, GetLength());
  859. for (var i = 0; i < length; i++)
  860. {
  861. var value = temp[i];
  862. if (value is not null)
  863. {
  864. yield return new IndexedEntry(i, value);
  865. }
  866. }
  867. }
  868. else
  869. {
  870. foreach (var entry in _sparse!)
  871. {
  872. var descriptor = entry.Value;
  873. if (descriptor is not null)
  874. {
  875. yield return new IndexedEntry((int) entry.Key, descriptor.Value);
  876. }
  877. }
  878. }
  879. }
  880. /// <summary>
  881. /// Pushes the value to the end of the array instance.
  882. /// </summary>
  883. public void Push(JsValue value)
  884. {
  885. var initialLength = GetLength();
  886. var newLength = initialLength + 1;
  887. var temp = _dense;
  888. var canUseDirectIndexSet = temp != null && newLength <= temp.Length;
  889. double n = initialLength;
  890. if (canUseDirectIndexSet)
  891. {
  892. temp![(uint) n] = value;
  893. }
  894. else
  895. {
  896. WriteValueSlow(n, value);
  897. }
  898. // check if we can set length fast without breaking ECMA specification
  899. if (n < uint.MaxValue && CanSetLength())
  900. {
  901. _length!.Value = newLength;
  902. }
  903. else
  904. {
  905. if (!Set(CommonProperties.Length, newLength))
  906. {
  907. ExceptionHelper.ThrowTypeError(_engine.Realm);
  908. }
  909. }
  910. }
  911. /// <summary>
  912. /// Pushes the given values to the end of the array.
  913. /// </summary>
  914. public uint Push(JsValue[] values)
  915. {
  916. var initialLength = GetLength();
  917. var newLength = initialLength + values.Length;
  918. // if we see that we are bringing more than normal growth algorithm handles, ensure capacity eagerly
  919. if (_dense != null
  920. && initialLength != 0
  921. && values.Length > initialLength * 2
  922. && newLength <= MaxDenseArrayLength)
  923. {
  924. EnsureCapacity((uint) newLength);
  925. }
  926. var temp = _dense;
  927. ulong n = initialLength;
  928. foreach (var argument in values)
  929. {
  930. if (n < ArrayOperations.MaxArrayLength)
  931. {
  932. WriteArrayValue((uint) n, argument);
  933. }
  934. else
  935. {
  936. DefineOwnProperty(n, new PropertyDescriptor(argument, PropertyFlag.ConfigurableEnumerableWritable));
  937. }
  938. n++;
  939. }
  940. // check if we can set length fast without breaking ECMA specification
  941. if (n < ArrayOperations.MaxArrayLength && CanSetLength())
  942. {
  943. _length!.Value = n;
  944. }
  945. else
  946. {
  947. if (!Set(CommonProperties.Length, newLength))
  948. {
  949. ExceptionHelper.ThrowTypeError(_engine.Realm);
  950. }
  951. }
  952. return (uint) n;
  953. }
  954. public JsValue Pop()
  955. {
  956. var len = GetJsNumberLength();
  957. if (JsNumber.PositiveZero.Equals(len))
  958. {
  959. SetLength(len);
  960. return Undefined;
  961. }
  962. var newLength = (uint) len._value - 1;
  963. if (!Delete(newLength, unwrapFromNonDataDescriptor: true, out var element))
  964. {
  965. ExceptionHelper.ThrowTypeError(_engine.Realm);
  966. }
  967. SetLength(newLength);
  968. return element ?? Undefined;
  969. }
  970. private bool CanSetLength()
  971. {
  972. if (!_length!.IsAccessorDescriptor())
  973. {
  974. return _length.Writable;
  975. }
  976. var set = _length.Set;
  977. return set is not null && !set.IsUndefined();
  978. }
  979. [MethodImpl(MethodImplOptions.NoInlining)]
  980. private void WriteValueSlow(double n, JsValue value)
  981. {
  982. if (n < ArrayOperations.MaxArrayLength)
  983. {
  984. WriteArrayValue((uint) n, value);
  985. }
  986. else
  987. {
  988. DefinePropertyOrThrow((uint) n, new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable));
  989. }
  990. }
  991. internal JsArray Map(JsValue[] arguments)
  992. {
  993. var callbackfn = arguments.At(0);
  994. var thisArg = arguments.At(1);
  995. var len = GetLength();
  996. var callable = GetCallable(callbackfn);
  997. var a = _engine.Realm.Intrinsics.Array.ArrayCreate(len);
  998. var args = _engine._jsValueArrayPool.RentArray(3);
  999. args[2] = this;
  1000. for (uint k = 0; k < len; k++)
  1001. {
  1002. if (TryGetValue(k, out var kvalue))
  1003. {
  1004. args[0] = kvalue;
  1005. args[1] = k;
  1006. var mappedValue = callable.Call(thisArg, args);
  1007. if (a._dense != null && k < (uint) a._dense.Length)
  1008. {
  1009. a._dense[k] = mappedValue;
  1010. }
  1011. else
  1012. {
  1013. a.WriteArrayValue(k, mappedValue);
  1014. }
  1015. }
  1016. }
  1017. _engine._jsValueArrayPool.ReturnArray(args);
  1018. return a;
  1019. }
  1020. /// <inheritdoc />
  1021. internal sealed override bool FindWithCallback(
  1022. JsValue[] arguments,
  1023. out ulong index,
  1024. out JsValue value,
  1025. bool visitUnassigned,
  1026. bool fromEnd = false)
  1027. {
  1028. var thisArg = arguments.At(1);
  1029. var callbackfn = arguments.At(0);
  1030. var callable = GetCallable(callbackfn);
  1031. var len = GetLength();
  1032. if (len == 0)
  1033. {
  1034. index = 0;
  1035. value = Undefined;
  1036. return false;
  1037. }
  1038. var args = _engine._jsValueArrayPool.RentArray(3);
  1039. args[2] = this;
  1040. if (!fromEnd)
  1041. {
  1042. for (uint k = 0; k < len; k++)
  1043. {
  1044. if (TryGetValue(k, out var kvalue) || visitUnassigned)
  1045. {
  1046. kvalue ??= Undefined;
  1047. args[0] = kvalue;
  1048. args[1] = k;
  1049. var testResult = callable.Call(thisArg, args);
  1050. if (TypeConverter.ToBoolean(testResult))
  1051. {
  1052. index = k;
  1053. value = kvalue;
  1054. return true;
  1055. }
  1056. }
  1057. }
  1058. }
  1059. else
  1060. {
  1061. for (long k = len - 1; k >= 0; k--)
  1062. {
  1063. var idx = (uint) k;
  1064. if (TryGetValue(idx, out var kvalue) || visitUnassigned)
  1065. {
  1066. kvalue ??= Undefined;
  1067. args[0] = kvalue;
  1068. args[1] = idx;
  1069. var testResult = callable.Call(thisArg, args);
  1070. if (TypeConverter.ToBoolean(testResult))
  1071. {
  1072. index = idx;
  1073. value = kvalue;
  1074. return true;
  1075. }
  1076. }
  1077. }
  1078. }
  1079. _engine._jsValueArrayPool.ReturnArray(args);
  1080. index = 0;
  1081. value = Undefined;
  1082. return false;
  1083. }
  1084. internal sealed override bool IsIntegerIndexedArray => true;
  1085. public JsValue this[uint index]
  1086. {
  1087. get
  1088. {
  1089. TryGetValue(index, out var kValue);
  1090. return kValue;
  1091. }
  1092. set
  1093. {
  1094. SetIndexValue(index, value, updateLength: true);
  1095. }
  1096. }
  1097. public JsValue this[int index]
  1098. {
  1099. get
  1100. {
  1101. JsValue? kValue;
  1102. if (index >= 0)
  1103. {
  1104. TryGetValue((uint) index, out kValue);
  1105. }
  1106. else
  1107. {
  1108. // slow path
  1109. TryGetValue(JsNumber.Create(index), out kValue);
  1110. }
  1111. return kValue;
  1112. }
  1113. set
  1114. {
  1115. if (index >= 0)
  1116. {
  1117. SetIndexValue((uint) index, value, updateLength: true);
  1118. }
  1119. else
  1120. {
  1121. Set(index, value);
  1122. }
  1123. }
  1124. }
  1125. /// <summary>
  1126. /// Fast path for concatenating sane-sized arrays, we assume size has been calculated.
  1127. /// </summary>
  1128. internal void CopyValues(JsArray source, uint sourceStartIndex, uint targetStartIndex, uint length)
  1129. {
  1130. if (length == 0)
  1131. {
  1132. return;
  1133. }
  1134. var sourceDense = source._dense;
  1135. if (sourceDense is not null)
  1136. {
  1137. EnsureCapacity((uint) (targetStartIndex + sourceDense.LongLength));
  1138. }
  1139. var dense = _dense;
  1140. if (dense != null
  1141. && sourceDense != null
  1142. && (uint) dense.Length >= targetStartIndex + length
  1143. && dense[targetStartIndex] is null)
  1144. {
  1145. uint j = 0;
  1146. for (var i = sourceStartIndex; i < sourceStartIndex + length; ++i, j++)
  1147. {
  1148. JsValue? sourceValue;
  1149. if (i < (uint) sourceDense.Length)
  1150. {
  1151. sourceValue = sourceDense[i];
  1152. }
  1153. else
  1154. {
  1155. source.TryGetValue(i, out sourceValue);
  1156. }
  1157. dense[targetStartIndex + j] = sourceValue;
  1158. }
  1159. }
  1160. else
  1161. {
  1162. // slower version
  1163. for (uint k = sourceStartIndex; k < length; k++)
  1164. {
  1165. if (source.TryGetValue(k, out var subElement))
  1166. {
  1167. SetIndexValue(targetStartIndex, subElement, updateLength: false);
  1168. }
  1169. targetStartIndex++;
  1170. }
  1171. }
  1172. }
  1173. public sealed override string ToString()
  1174. {
  1175. // debugger can make things hard when evaluates computed values
  1176. return "(" + (_length?._value!.AsNumber() ?? 0) + ")[]";
  1177. }
  1178. private static void ThrowMaximumArraySizeReachedException(Engine engine, uint capacity)
  1179. {
  1180. ExceptionHelper.ThrowMemoryLimitExceededException(
  1181. $"The array size {capacity} is larger than maximum allowed ({engine.Options.Constraints.MaxArraySize})"
  1182. );
  1183. }
  1184. }
  1185. internal static class ArrayPropertyDescriptorExtensions
  1186. {
  1187. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  1188. internal static bool IsDefaultArrayValueDescriptor(this PropertyDescriptor propertyDescriptor)
  1189. => propertyDescriptor.Flags == PropertyFlag.ConfigurableEnumerableWritable && propertyDescriptor.IsDataDescriptor();
  1190. }
  1191. }