ArrayInstance.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813
  1. using System.Collections.Generic;
  2. using System.Runtime.CompilerServices;
  3. using Jint.Native.Object;
  4. using Jint.Runtime;
  5. using Jint.Runtime.Descriptors;
  6. using PropertyDescriptor = Jint.Runtime.Descriptors.PropertyDescriptor;
  7. using TypeConverter = Jint.Runtime.TypeConverter;
  8. namespace Jint.Native.Array
  9. {
  10. public class ArrayInstance : ObjectInstance
  11. {
  12. private const string PropertyNameLength = "length";
  13. private const int PropertyNameLengthLength = 6;
  14. internal PropertyDescriptor _length;
  15. private const int MaxDenseArrayLength = 1024 * 10;
  16. // we have dense and sparse, we usually can start with dense and fall back to sparse when necessary
  17. private PropertyDescriptor[] _dense;
  18. private Dictionary<uint, PropertyDescriptor> _sparse;
  19. public ArrayInstance(Engine engine, uint capacity = 0) : base(engine, objectClass: "Array")
  20. {
  21. if (capacity < MaxDenseArrayLength)
  22. {
  23. _dense = capacity > 0 ? new PropertyDescriptor[capacity] : System.ArrayExt.Empty<PropertyDescriptor>();
  24. }
  25. else
  26. {
  27. _sparse = new Dictionary<uint, PropertyDescriptor>((int) (capacity <= 1024 ? capacity : 1024));
  28. }
  29. }
  30. public ArrayInstance(Engine engine, PropertyDescriptor[] items) : base(engine, objectClass: "Array")
  31. {
  32. int length = 0;
  33. if (items == null || items.Length == 0)
  34. {
  35. _dense = System.ArrayExt.Empty<PropertyDescriptor>();
  36. length = 0;
  37. }
  38. else
  39. {
  40. _dense = items;
  41. length = items.Length;
  42. }
  43. _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
  44. }
  45. public ArrayInstance(Engine engine, Dictionary<uint, PropertyDescriptor> items) : base(engine, objectClass: "Array")
  46. {
  47. _sparse = items;
  48. var length = items?.Count ?? 0;
  49. _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
  50. }
  51. internal override bool IsConcatSpreadable => !TryGetIsConcatSpreadable(out var isConcatSpreadable) || isConcatSpreadable;
  52. internal override bool IsArrayLike => true;
  53. /// Implementation from ObjectInstance official specs as the one
  54. /// in ObjectInstance is optimized for the general case and wouldn't work
  55. /// for arrays
  56. public override void Put(string propertyName, JsValue value, bool throwOnError)
  57. {
  58. if (!CanPut(propertyName))
  59. {
  60. if (throwOnError)
  61. {
  62. ExceptionHelper.ThrowTypeError(Engine);
  63. }
  64. return;
  65. }
  66. var ownDesc = GetOwnProperty(propertyName);
  67. if (ownDesc.IsDataDescriptor())
  68. {
  69. var valueDesc = new PropertyDescriptor(value, PropertyFlag.None);
  70. DefineOwnProperty(propertyName, valueDesc, throwOnError);
  71. return;
  72. }
  73. // property is an accessor or inherited
  74. var desc = GetProperty(propertyName);
  75. if (desc.IsAccessorDescriptor())
  76. {
  77. var setter = desc.Set.TryCast<ICallable>();
  78. setter.Call(this, new[] {value});
  79. }
  80. else
  81. {
  82. var newDesc = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
  83. DefineOwnProperty(propertyName, newDesc, throwOnError);
  84. }
  85. }
  86. public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
  87. {
  88. var oldLenDesc = _length;
  89. var oldLen = (uint) TypeConverter.ToNumber(oldLenDesc.Value);
  90. if (propertyName.Length == 6 && propertyName == "length")
  91. {
  92. var value = desc.Value;
  93. if (ReferenceEquals(value, null))
  94. {
  95. return base.DefineOwnProperty("length", desc, throwOnError);
  96. }
  97. var newLenDesc = new PropertyDescriptor(desc);
  98. uint newLen = TypeConverter.ToUint32(value);
  99. if (newLen != TypeConverter.ToNumber(value))
  100. {
  101. ExceptionHelper.ThrowRangeError(_engine);
  102. }
  103. newLenDesc.Value = newLen;
  104. if (newLen >= oldLen)
  105. {
  106. return base.DefineOwnProperty("length", newLenDesc, throwOnError);
  107. }
  108. if (!oldLenDesc.Writable)
  109. {
  110. if (throwOnError)
  111. {
  112. ExceptionHelper.ThrowTypeError(_engine);
  113. }
  114. return false;
  115. }
  116. bool newWritable;
  117. if (!newLenDesc.WritableSet || newLenDesc.Writable)
  118. {
  119. newWritable = true;
  120. }
  121. else
  122. {
  123. newWritable = false;
  124. newLenDesc.Writable = true;
  125. }
  126. var succeeded = base.DefineOwnProperty("length", newLenDesc, throwOnError);
  127. if (!succeeded)
  128. {
  129. return false;
  130. }
  131. var count = _dense?.Length ?? _sparse.Count;
  132. if (count < oldLen - newLen)
  133. {
  134. if (_dense != null)
  135. {
  136. for (uint keyIndex = 0; keyIndex < _dense.Length; ++keyIndex)
  137. {
  138. if (_dense[keyIndex] == null)
  139. {
  140. continue;
  141. }
  142. // is it the index of the array
  143. if (keyIndex >= newLen && keyIndex < oldLen)
  144. {
  145. var deleteSucceeded = DeleteAt(keyIndex);
  146. if (!deleteSucceeded)
  147. {
  148. newLenDesc.Value = keyIndex + 1;
  149. if (!newWritable)
  150. {
  151. newLenDesc.Writable = false;
  152. }
  153. base.DefineOwnProperty("length", newLenDesc, false);
  154. if (throwOnError)
  155. {
  156. ExceptionHelper.ThrowTypeError(_engine);
  157. }
  158. return false;
  159. }
  160. }
  161. }
  162. }
  163. else
  164. {
  165. // in the case of sparse arrays, treat each concrete element instead of
  166. // iterating over all indexes
  167. var keys = new List<uint>(_sparse.Keys);
  168. var keysCount = keys.Count;
  169. for (var i = 0; i < keysCount; i++)
  170. {
  171. var keyIndex = keys[i];
  172. // is it the index of the array
  173. if (keyIndex >= newLen && keyIndex < oldLen)
  174. {
  175. var deleteSucceeded = Delete(TypeConverter.ToString(keyIndex), false);
  176. if (!deleteSucceeded)
  177. {
  178. newLenDesc.Value = JsNumber.Create(keyIndex + 1);
  179. if (!newWritable)
  180. {
  181. newLenDesc.Writable = false;
  182. }
  183. base.DefineOwnProperty("length", newLenDesc, false);
  184. if (throwOnError)
  185. {
  186. ExceptionHelper.ThrowTypeError(_engine);
  187. }
  188. return false;
  189. }
  190. }
  191. }
  192. }
  193. }
  194. else
  195. {
  196. while (newLen < oldLen)
  197. {
  198. // algorithm as per the spec
  199. oldLen--;
  200. var deleteSucceeded = Delete(TypeConverter.ToString(oldLen), false);
  201. if (!deleteSucceeded)
  202. {
  203. newLenDesc.Value = oldLen + 1;
  204. if (!newWritable)
  205. {
  206. newLenDesc.Writable = false;
  207. }
  208. base.DefineOwnProperty("length", newLenDesc, false);
  209. if (throwOnError)
  210. {
  211. ExceptionHelper.ThrowTypeError(_engine);
  212. }
  213. return false;
  214. }
  215. }
  216. }
  217. if (!newWritable)
  218. {
  219. DefineOwnProperty("length", new PropertyDescriptor(value: null, PropertyFlag.WritableSet), false);
  220. }
  221. return true;
  222. }
  223. else if (IsArrayIndex(propertyName, out var index))
  224. {
  225. if (index >= oldLen && !oldLenDesc.Writable)
  226. {
  227. if (throwOnError)
  228. {
  229. ExceptionHelper.ThrowTypeError(_engine);
  230. }
  231. return false;
  232. }
  233. var succeeded = base.DefineOwnProperty(propertyName, desc, false);
  234. if (!succeeded)
  235. {
  236. if (throwOnError)
  237. {
  238. ExceptionHelper.ThrowTypeError(_engine);
  239. }
  240. return false;
  241. }
  242. if (index >= oldLen)
  243. {
  244. oldLenDesc.Value = index + 1;
  245. base.DefineOwnProperty("length", oldLenDesc, false);
  246. }
  247. return true;
  248. }
  249. return base.DefineOwnProperty(propertyName, desc, throwOnError);
  250. }
  251. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  252. public uint GetLength()
  253. {
  254. return (uint) ((JsNumber) _length._value)._value;
  255. }
  256. protected override void AddProperty(string propertyName, PropertyDescriptor descriptor)
  257. {
  258. if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
  259. {
  260. _length = descriptor;
  261. return;
  262. }
  263. base.AddProperty(propertyName, descriptor);
  264. }
  265. protected override bool TryGetProperty(string propertyName, out PropertyDescriptor descriptor)
  266. {
  267. if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
  268. {
  269. descriptor = _length;
  270. return _length != null;
  271. }
  272. return base.TryGetProperty(propertyName, out descriptor);
  273. }
  274. public override IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
  275. {
  276. if (_length != null)
  277. {
  278. yield return new KeyValuePair<string, PropertyDescriptor>(PropertyNameLength, _length);
  279. }
  280. if (_dense != null)
  281. {
  282. var length = System.Math.Min(_dense.Length, GetLength());
  283. for (var i = 0; i < length; i++)
  284. {
  285. if (_dense[i] != null)
  286. {
  287. yield return new KeyValuePair<string, PropertyDescriptor>(TypeConverter.ToString(i), _dense[i]);
  288. }
  289. }
  290. }
  291. else
  292. {
  293. foreach (var entry in _sparse)
  294. {
  295. yield return new KeyValuePair<string, PropertyDescriptor>(TypeConverter.ToString(entry.Key), entry.Value);
  296. }
  297. }
  298. foreach (var entry in base.GetOwnProperties())
  299. {
  300. yield return entry;
  301. }
  302. }
  303. public override PropertyDescriptor GetOwnProperty(string propertyName)
  304. {
  305. if (IsArrayIndex(propertyName, out var index))
  306. {
  307. if (TryGetDescriptor(index, out var result))
  308. {
  309. return result;
  310. }
  311. return PropertyDescriptor.Undefined;
  312. }
  313. if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
  314. {
  315. return _length ?? PropertyDescriptor.Undefined;
  316. }
  317. return base.GetOwnProperty(propertyName);
  318. }
  319. protected internal override void SetOwnProperty(string propertyName, PropertyDescriptor desc)
  320. {
  321. if (IsArrayIndex(propertyName, out var index))
  322. {
  323. WriteArrayValue(index, desc);
  324. }
  325. else if (propertyName.Length == PropertyNameLengthLength && propertyName == PropertyNameLength)
  326. {
  327. _length = desc;
  328. }
  329. else
  330. {
  331. base.SetOwnProperty(propertyName, desc);
  332. }
  333. }
  334. public override bool HasOwnProperty(string p)
  335. {
  336. if (IsArrayIndex(p, out var index))
  337. {
  338. return index < GetLength()
  339. && (_sparse == null || _sparse.ContainsKey(index))
  340. && (_dense == null || (index < (uint) _dense.Length && _dense[index] != null));
  341. }
  342. if (p == PropertyNameLength)
  343. {
  344. return _length != null;
  345. }
  346. return base.HasOwnProperty(p);
  347. }
  348. public override void RemoveOwnProperty(string p)
  349. {
  350. if (IsArrayIndex(p, out var index))
  351. {
  352. DeleteAt(index);
  353. }
  354. if (p == PropertyNameLength)
  355. {
  356. _length = null;
  357. }
  358. base.RemoveOwnProperty(p);
  359. }
  360. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  361. private static bool IsArrayIndex(string p, out uint index)
  362. {
  363. index = ParseArrayIndex(p);
  364. return index != uint.MaxValue;
  365. // 15.4 - Use an optimized version of the specification
  366. // return TypeConverter.ToString(index) == TypeConverter.ToString(p) && index != uint.MaxValue;
  367. }
  368. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  369. private static uint ParseArrayIndex(string p)
  370. {
  371. int d = p[0] - '0';
  372. if (d < 0 || d > 9)
  373. {
  374. return uint.MaxValue;
  375. }
  376. if (d == 0 && p.Length > 1)
  377. {
  378. // If p is a number that start with '0' and is not '0' then
  379. // its ToString representation can't be the same a p. This is
  380. // not a valid array index. '01' !== ToString(ToUInt32('01'))
  381. // http://www.ecma-international.org/ecma-262/5.1/#sec-15.4
  382. return uint.MaxValue;
  383. }
  384. if (p.Length > 1)
  385. {
  386. return StringAsIndex(d, p);
  387. }
  388. return (uint) d;
  389. }
  390. private static uint StringAsIndex(int d, string p)
  391. {
  392. ulong result = (uint) d;
  393. for (int i = 1; i < p.Length; i++)
  394. {
  395. d = p[i] - '0';
  396. if (d < 0 || d > 9)
  397. {
  398. return uint.MaxValue;
  399. }
  400. result = result * 10 + (uint) d;
  401. if (result >= uint.MaxValue)
  402. {
  403. return uint.MaxValue;
  404. }
  405. }
  406. return (uint) result;
  407. }
  408. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  409. internal void SetIndexValue(uint index, JsValue value, bool updateLength)
  410. {
  411. if (updateLength)
  412. {
  413. var length = GetLength();
  414. if (index >= length)
  415. {
  416. SetLength(index + 1);
  417. }
  418. }
  419. WriteArrayValue(index, new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable));
  420. }
  421. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  422. internal void SetLength(uint length)
  423. {
  424. _length.Value = length;
  425. }
  426. internal uint GetSmallestIndex()
  427. {
  428. if (_dense != null)
  429. {
  430. return 0;
  431. }
  432. uint smallest = 0;
  433. // only try to help if collection reasonable small
  434. if (_sparse.Count > 0 && _sparse.Count < 100 && !_sparse.ContainsKey(0))
  435. {
  436. smallest = uint.MaxValue;
  437. foreach (var key in _sparse.Keys)
  438. {
  439. smallest = System.Math.Min(key, smallest);
  440. }
  441. }
  442. return smallest;
  443. }
  444. internal uint GetLargestIndex()
  445. {
  446. if (_dense != null)
  447. {
  448. return (uint) (_dense.Length - 1);
  449. }
  450. uint largest = uint.MaxValue;
  451. // only try to help if collection reasonable small
  452. if (_sparse.Count > 0 && _sparse.Count < 100)
  453. {
  454. largest = 0;
  455. foreach (var key in _sparse.Keys)
  456. {
  457. largest = System.Math.Max(key, largest);
  458. }
  459. }
  460. return largest;
  461. }
  462. public bool TryGetValue(uint index, out JsValue value)
  463. {
  464. value = Undefined;
  465. TryGetDescriptor(index, out var desc);
  466. desc = desc ?? GetProperty(TypeConverter.ToString(index)) ?? PropertyDescriptor.Undefined;
  467. return desc.TryGetValue(this, out value);
  468. }
  469. internal bool DeleteAt(uint index)
  470. {
  471. if (_dense != null)
  472. {
  473. if (index < (uint) _dense.Length)
  474. {
  475. _dense[index] = null;
  476. return true;
  477. }
  478. }
  479. else
  480. {
  481. return _sparse.Remove(index);
  482. }
  483. return false;
  484. }
  485. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  486. private bool TryGetDescriptor(uint index, out PropertyDescriptor descriptor)
  487. {
  488. if (_dense != null)
  489. {
  490. descriptor = null;
  491. if (index < (uint) _dense.Length)
  492. {
  493. descriptor = _dense[index];
  494. }
  495. return descriptor != null;
  496. }
  497. return _sparse.TryGetValue(index, out descriptor);
  498. }
  499. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  500. internal void WriteArrayValue(uint index, PropertyDescriptor desc)
  501. {
  502. // calculate eagerly so we know if we outgrow
  503. var newSize = _dense != null && index >= (uint) _dense.Length
  504. ? System.Math.Max(index, System.Math.Max(_dense.Length, 2)) * 2
  505. : 0;
  506. bool canUseDense = _dense != null
  507. && index < MaxDenseArrayLength
  508. && newSize < MaxDenseArrayLength
  509. && index < _dense.Length + 50; // looks sparse
  510. if (canUseDense)
  511. {
  512. var temp = _dense;
  513. if (index >= (uint) temp.Length)
  514. {
  515. EnsureCapacity((uint) newSize);
  516. }
  517. _dense[index] = desc;
  518. }
  519. else
  520. {
  521. if (_dense != null)
  522. {
  523. ConvertToSparse();
  524. }
  525. _sparse[index] = desc;
  526. }
  527. }
  528. private void ConvertToSparse()
  529. {
  530. _sparse = new Dictionary<uint, PropertyDescriptor>(_dense.Length <= 1024 ? _dense.Length : 0);
  531. // need to move data
  532. for (uint i = 0; i < (uint) _dense.Length; ++i)
  533. {
  534. if (_dense[i] != null)
  535. {
  536. _sparse[i] = _dense[i];
  537. }
  538. }
  539. _dense = null;
  540. }
  541. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  542. internal void EnsureCapacity(uint capacity)
  543. {
  544. if (capacity <= MaxDenseArrayLength && capacity > (uint) _dense.Length)
  545. {
  546. // need to grow
  547. var newArray = new PropertyDescriptor[capacity];
  548. System.Array.Copy(_dense, newArray, _dense.Length);
  549. _dense = newArray;
  550. }
  551. }
  552. public IEnumerator<JsValue> GetEnumerator()
  553. {
  554. var length = GetLength();
  555. for (uint i = 0; i < length; i++)
  556. {
  557. if (TryGetValue(i, out JsValue outValue))
  558. {
  559. yield return outValue;
  560. }
  561. };
  562. }
  563. internal uint Push(JsValue[] arguments)
  564. {
  565. var initialLength = GetLength();
  566. var newLength = initialLength + arguments.Length;
  567. // if we see that we are bringing more than normal growth algorithm handles, ensure capacity eagerly
  568. if (_dense != null
  569. && initialLength != 0
  570. && arguments.Length > initialLength * 2
  571. && newLength <= MaxDenseArrayLength)
  572. {
  573. EnsureCapacity((uint) newLength);
  574. }
  575. double n = initialLength;
  576. for (var i = 0; i < arguments.Length; i++)
  577. {
  578. var desc = new PropertyDescriptor(arguments[i], PropertyFlag.ConfigurableEnumerableWritable);
  579. if (_dense != null && n < _dense.Length)
  580. {
  581. _dense[(uint) n] = desc;
  582. }
  583. else if (n < uint.MaxValue)
  584. {
  585. WriteArrayValue((uint) n, desc);
  586. }
  587. else
  588. {
  589. DefineOwnProperty(TypeConverter.ToString((uint) n), desc, true);
  590. }
  591. n++;
  592. }
  593. // check if we can set length fast without breaking ECMA specification
  594. if (n < uint.MaxValue && CanPut(PropertyNameLength))
  595. {
  596. _length.Value = (uint) n;
  597. }
  598. else
  599. {
  600. Put(PropertyNameLength, newLength, true);
  601. }
  602. return (uint) n;
  603. }
  604. internal ArrayInstance Map(JsValue[] arguments)
  605. {
  606. var callbackfn = arguments.At(0);
  607. var thisArg = arguments.At(1);
  608. var len = GetLength();
  609. var callable = GetCallable(callbackfn);
  610. var a = Engine.Array.ConstructFast(len);
  611. var args = _engine._jsValueArrayPool.RentArray(3);
  612. args[2] = this;
  613. for (uint k = 0; k < len; k++)
  614. {
  615. if (TryGetValue(k, out var kvalue))
  616. {
  617. args[0] = kvalue;
  618. args[1] = k;
  619. var mappedValue = callable.Call(thisArg, args);
  620. var desc = new PropertyDescriptor(mappedValue, PropertyFlag.ConfigurableEnumerableWritable);
  621. if (a._dense != null && k < (uint) a._dense.Length)
  622. {
  623. a._dense[k] = desc;
  624. }
  625. else
  626. {
  627. a.WriteArrayValue(k, desc);
  628. }
  629. }
  630. }
  631. _engine._jsValueArrayPool.ReturnArray(args);
  632. return a;
  633. }
  634. /// <inheritdoc />
  635. internal override bool FindWithCallback(
  636. JsValue[] arguments,
  637. out uint index,
  638. out JsValue value,
  639. bool visitUnassigned)
  640. {
  641. var thisArg = arguments.At(1);
  642. var callbackfn = arguments.At(0);
  643. var callable = GetCallable(callbackfn);
  644. var len = GetLength();
  645. if (len == 0)
  646. {
  647. index = 0;
  648. value = Undefined;
  649. return false;
  650. }
  651. var args = _engine._jsValueArrayPool.RentArray(3);
  652. args[2] = this;
  653. for (uint k = 0; k < len; k++)
  654. {
  655. if (TryGetValue(k, out var kvalue) || visitUnassigned)
  656. {
  657. args[0] = kvalue;
  658. args[1] = k;
  659. var testResult = callable.Call(thisArg, args);
  660. if (TypeConverter.ToBoolean(testResult))
  661. {
  662. index = k;
  663. value = kvalue;
  664. return true;
  665. }
  666. }
  667. }
  668. _engine._jsValueArrayPool.ReturnArray(args);
  669. index = 0;
  670. value = Undefined;
  671. return false;
  672. }
  673. public uint Length => GetLength();
  674. public JsValue this[uint index]
  675. {
  676. get
  677. {
  678. TryGetValue(index, out var kValue);
  679. return kValue;
  680. }
  681. }
  682. internal ArrayInstance ToArray(Engine engine)
  683. {
  684. var length = GetLength();
  685. var array = _engine.Array.ConstructFast(length);
  686. for (uint i = 0; i < length; i++)
  687. {
  688. if (TryGetValue(i, out var kValue))
  689. {
  690. array.SetIndexValue(i, kValue, updateLength: false);
  691. }
  692. }
  693. return array;
  694. }
  695. }
  696. }