ArrayInstance.cs 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042
  1. using System.Collections;
  2. using System.Collections.Generic;
  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. internal PropertyDescriptor[] _dense;
  16. private Dictionary<uint, PropertyDescriptor> _sparse;
  17. private ObjectChangeFlags _objectChangeFlags;
  18. public ArrayInstance(Engine engine, uint capacity = 0) : base(engine)
  19. {
  20. if (capacity > engine.Options.Constraints.MaxArraySize)
  21. {
  22. ThrowMaximumArraySizeReachedException(engine, capacity);
  23. }
  24. if (capacity < MaxDenseArrayLength)
  25. {
  26. _dense = capacity > 0 ? new PropertyDescriptor[capacity] : System.Array.Empty<PropertyDescriptor>();
  27. }
  28. else
  29. {
  30. _sparse = new Dictionary<uint, PropertyDescriptor>((int) (capacity <= 1024 ? capacity : 1024));
  31. }
  32. }
  33. /// <summary>
  34. /// Possibility to construct valid array fast, requires that supplied array does not have holes.
  35. /// </summary>
  36. public ArrayInstance(Engine engine, PropertyDescriptor[] items) : base(engine)
  37. {
  38. int length = 0;
  39. if (items == null || items.Length == 0)
  40. {
  41. _dense = System.Array.Empty<PropertyDescriptor>();
  42. length = 0;
  43. }
  44. else
  45. {
  46. _dense = items;
  47. length = items.Length;
  48. }
  49. _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
  50. }
  51. public ArrayInstance(Engine engine, Dictionary<uint, PropertyDescriptor> items) : base(engine)
  52. {
  53. _sparse = items;
  54. var length = items?.Count ?? 0;
  55. _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
  56. }
  57. public sealed override bool IsArrayLike => true;
  58. public sealed override bool IsArray() => true;
  59. internal sealed override bool HasOriginalIterator
  60. => ReferenceEquals(Get(GlobalSymbolRegistry.Iterator), _engine.Realm.Intrinsics.Array.PrototypeObject._originalIteratorFunction);
  61. /// <summary>
  62. /// Checks whether there have been changes to object prototype chain which could render fast access patterns impossible.
  63. /// </summary>
  64. internal bool CanUseFastAccess
  65. {
  66. get
  67. {
  68. if ((_objectChangeFlags & ObjectChangeFlags.NonDefaultDataDescriptorUsage) != 0)
  69. {
  70. // could be a mutating property for example, length might change, not safe anymore
  71. return false;
  72. }
  73. if (_prototype is not ArrayPrototype arrayPrototype
  74. || !ReferenceEquals(_prototype, _engine.Realm.Intrinsics.Array.PrototypeObject))
  75. {
  76. // somebody has switched prototype
  77. return false;
  78. }
  79. if ((arrayPrototype._objectChangeFlags & ObjectChangeFlags.ArrayIndex) != 0)
  80. {
  81. // maybe somebody moved integer property to prototype? not safe anymore
  82. return false;
  83. }
  84. if (arrayPrototype.Prototype is not ObjectPrototype arrayPrototypePrototype
  85. || !ReferenceEquals(arrayPrototypePrototype, _engine.Realm.Intrinsics.Array.PrototypeObject.Prototype))
  86. {
  87. return false;
  88. }
  89. return (arrayPrototypePrototype._objectChangeFlags & ObjectChangeFlags.ArrayIndex) == 0;
  90. }
  91. }
  92. public sealed override bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
  93. {
  94. var isArrayIndex = IsArrayIndex(property, out var index);
  95. TrackChanges(property, desc, isArrayIndex);
  96. if (isArrayIndex)
  97. {
  98. return DefineOwnProperty(index, desc);
  99. }
  100. if (property == CommonProperties.Length)
  101. {
  102. var value = desc.Value;
  103. if (ReferenceEquals(value, null))
  104. {
  105. return base.DefineOwnProperty(CommonProperties.Length, desc);
  106. }
  107. var newLenDesc = new PropertyDescriptor(desc);
  108. uint newLen = TypeConverter.ToUint32(value);
  109. if (newLen != TypeConverter.ToNumber(value))
  110. {
  111. ExceptionHelper.ThrowRangeError(_engine.Realm);
  112. }
  113. var oldLenDesc = _length;
  114. var oldLen = (uint) TypeConverter.ToNumber(oldLenDesc.Value);
  115. newLenDesc.Value = newLen;
  116. if (newLen >= oldLen)
  117. {
  118. return base.DefineOwnProperty(CommonProperties.Length, newLenDesc);
  119. }
  120. if (!oldLenDesc.Writable)
  121. {
  122. return false;
  123. }
  124. bool newWritable;
  125. if (!newLenDesc.WritableSet || newLenDesc.Writable)
  126. {
  127. newWritable = true;
  128. }
  129. else
  130. {
  131. newWritable = false;
  132. newLenDesc.Writable = true;
  133. }
  134. var succeeded = base.DefineOwnProperty(CommonProperties.Length, newLenDesc);
  135. if (!succeeded)
  136. {
  137. return false;
  138. }
  139. var count = _dense?.Length ?? _sparse.Count;
  140. if (count < oldLen - newLen)
  141. {
  142. if (_dense != null)
  143. {
  144. for (uint keyIndex = 0; keyIndex < _dense.Length; ++keyIndex)
  145. {
  146. if (_dense[keyIndex] == null)
  147. {
  148. continue;
  149. }
  150. // is it the index of the array
  151. if (keyIndex >= newLen && keyIndex < oldLen)
  152. {
  153. var deleteSucceeded = Delete(keyIndex);
  154. if (!deleteSucceeded)
  155. {
  156. newLenDesc.Value = keyIndex + 1;
  157. if (!newWritable)
  158. {
  159. newLenDesc.Writable = false;
  160. }
  161. base.DefineOwnProperty(CommonProperties.Length, newLenDesc);
  162. return false;
  163. }
  164. }
  165. }
  166. }
  167. else
  168. {
  169. // in the case of sparse arrays, treat each concrete element instead of
  170. // iterating over all indexes
  171. var keys = new List<uint>(_sparse.Keys);
  172. var keysCount = keys.Count;
  173. for (var i = 0; i < keysCount; i++)
  174. {
  175. var keyIndex = keys[i];
  176. // is it the index of the array
  177. if (keyIndex >= newLen && keyIndex < oldLen)
  178. {
  179. var deleteSucceeded = Delete(TypeConverter.ToString(keyIndex));
  180. if (!deleteSucceeded)
  181. {
  182. newLenDesc.Value = JsNumber.Create(keyIndex + 1);
  183. if (!newWritable)
  184. {
  185. newLenDesc.Writable = false;
  186. }
  187. base.DefineOwnProperty(CommonProperties.Length, newLenDesc);
  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(oldLen);
  201. if (!deleteSucceeded)
  202. {
  203. newLenDesc.Value = oldLen + 1;
  204. if (!newWritable)
  205. {
  206. newLenDesc.Writable = false;
  207. }
  208. base.DefineOwnProperty(CommonProperties.Length, newLenDesc);
  209. return false;
  210. }
  211. }
  212. }
  213. if (!newWritable)
  214. {
  215. base.DefineOwnProperty(CommonProperties.Length, new PropertyDescriptor(value: null, PropertyFlag.WritableSet));
  216. }
  217. return true;
  218. }
  219. return base.DefineOwnProperty(property, desc);
  220. }
  221. private bool DefineOwnProperty(uint index, PropertyDescriptor desc)
  222. {
  223. var oldLenDesc = _length;
  224. var oldLen = (uint) TypeConverter.ToNumber(oldLenDesc.Value);
  225. if (index >= oldLen && !oldLenDesc.Writable)
  226. {
  227. return false;
  228. }
  229. var succeeded = base.DefineOwnProperty(index, desc);
  230. if (!succeeded)
  231. {
  232. return false;
  233. }
  234. if (index >= oldLen)
  235. {
  236. oldLenDesc.Value = index + 1;
  237. base.DefineOwnProperty(CommonProperties.Length, oldLenDesc);
  238. }
  239. return true;
  240. }
  241. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  242. internal uint GetLength()
  243. {
  244. if (_length is null)
  245. {
  246. return 0;
  247. }
  248. return (uint) ((JsNumber) _length._value)._value;
  249. }
  250. protected sealed override void AddProperty(JsValue property, PropertyDescriptor descriptor)
  251. {
  252. if (property == CommonProperties.Length)
  253. {
  254. _length = descriptor;
  255. return;
  256. }
  257. base.AddProperty(property, descriptor);
  258. }
  259. protected sealed override bool TryGetProperty(JsValue property, out PropertyDescriptor descriptor)
  260. {
  261. if (property == CommonProperties.Length)
  262. {
  263. descriptor = _length;
  264. return _length != null;
  265. }
  266. return base.TryGetProperty(property, out descriptor);
  267. }
  268. public sealed override List<JsValue> GetOwnPropertyKeys(Types types = Types.None | Types.String | Types.Symbol)
  269. {
  270. if ((types & Types.String) == 0)
  271. {
  272. return base.GetOwnPropertyKeys(types);
  273. }
  274. var properties = new List<JsValue>(_dense?.Length ?? 0 + 1);
  275. if (_dense != null)
  276. {
  277. var length = System.Math.Min(_dense.Length, GetLength());
  278. for (var i = 0; i < length; i++)
  279. {
  280. if (_dense[i] != null)
  281. {
  282. properties.Add(JsString.Create(i));
  283. }
  284. }
  285. }
  286. else
  287. {
  288. foreach (var entry in _sparse)
  289. {
  290. properties.Add(JsString.Create(entry.Key));
  291. }
  292. }
  293. if (_length != null)
  294. {
  295. properties.Add(CommonProperties.Length);
  296. }
  297. properties.AddRange(base.GetOwnPropertyKeys(types));
  298. return properties;
  299. }
  300. public sealed override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
  301. {
  302. if (_dense != null)
  303. {
  304. var length = System.Math.Min(_dense.Length, GetLength());
  305. for (var i = 0; i < length; i++)
  306. {
  307. if (_dense[i] != null)
  308. {
  309. yield return new KeyValuePair<JsValue, PropertyDescriptor>(TypeConverter.ToString(i), _dense[i]);
  310. }
  311. }
  312. }
  313. else
  314. {
  315. foreach (var entry in _sparse)
  316. {
  317. yield return new KeyValuePair<JsValue, PropertyDescriptor>(TypeConverter.ToString(entry.Key), entry.Value);
  318. }
  319. }
  320. if (_length != null)
  321. {
  322. yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Length, _length);
  323. }
  324. foreach (var entry in base.GetOwnProperties())
  325. {
  326. yield return entry;
  327. }
  328. }
  329. public sealed override PropertyDescriptor GetOwnProperty(JsValue property)
  330. {
  331. if (property == CommonProperties.Length)
  332. {
  333. return _length ?? PropertyDescriptor.Undefined;
  334. }
  335. if (IsArrayIndex(property, out var index))
  336. {
  337. if (TryGetDescriptor(index, out var result))
  338. {
  339. return result;
  340. }
  341. return PropertyDescriptor.Undefined;
  342. }
  343. return base.GetOwnProperty(property);
  344. }
  345. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  346. private PropertyDescriptor GetOwnProperty(uint index)
  347. {
  348. return TryGetDescriptor(index, out var result)
  349. ? result
  350. : PropertyDescriptor.Undefined;
  351. }
  352. internal JsValue Get(uint index)
  353. {
  354. var prop = GetOwnProperty(index);
  355. if (prop == PropertyDescriptor.Undefined)
  356. {
  357. prop = Prototype?.GetProperty(JsString.Create(index)) ?? PropertyDescriptor.Undefined;
  358. }
  359. return UnwrapJsValue(prop);
  360. }
  361. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  362. private PropertyDescriptor GetProperty(uint index)
  363. {
  364. var prop = GetOwnProperty(index);
  365. if (prop != PropertyDescriptor.Undefined)
  366. {
  367. return prop;
  368. }
  369. return Prototype?.GetProperty(JsString.Create(index)) ?? PropertyDescriptor.Undefined;
  370. }
  371. public sealed override bool Set(JsValue property, JsValue value, JsValue receiver)
  372. {
  373. if (ReferenceEquals(receiver, this) && Extensible && IsArrayIndex(property, out var index))
  374. {
  375. if (TryGetDescriptor(index, out var descriptor))
  376. {
  377. if (descriptor.IsDefaultArrayValueDescriptor())
  378. {
  379. // fast path with direct write without allocations
  380. descriptor.Value = value;
  381. return true;
  382. }
  383. }
  384. else if (CanUseFastAccess)
  385. {
  386. // we know it's to be written to own array backing field as new value
  387. WriteArrayValue(index, new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable));
  388. EnsureCorrectLength(index);
  389. return true;
  390. }
  391. }
  392. // slow path
  393. return base.Set(property, value, receiver);
  394. }
  395. protected internal sealed override void SetOwnProperty(JsValue property, PropertyDescriptor desc)
  396. {
  397. var isArrayIndex = IsArrayIndex(property, out var index);
  398. TrackChanges(property, desc, isArrayIndex);
  399. if (isArrayIndex)
  400. {
  401. WriteArrayValue(index, desc);
  402. }
  403. else if (property == CommonProperties.Length)
  404. {
  405. _length = desc;
  406. }
  407. else
  408. {
  409. base.SetOwnProperty(property, desc);
  410. }
  411. }
  412. private void TrackChanges(JsValue property, PropertyDescriptor desc, bool isArrayIndex)
  413. {
  414. EnsureInitialized();
  415. if (!desc.IsDefaultArrayValueDescriptor())
  416. {
  417. _objectChangeFlags |= ObjectChangeFlags.NonDefaultDataDescriptorUsage;
  418. }
  419. if (isArrayIndex)
  420. {
  421. _objectChangeFlags |= ObjectChangeFlags.ArrayIndex;
  422. }
  423. else
  424. {
  425. _objectChangeFlags |= property.IsSymbol() ? ObjectChangeFlags.Symbol : ObjectChangeFlags.Property;
  426. }
  427. }
  428. public sealed override void RemoveOwnProperty(JsValue p)
  429. {
  430. if (IsArrayIndex(p, out var index))
  431. {
  432. Delete(index);
  433. }
  434. if (p == CommonProperties.Length)
  435. {
  436. _length = null;
  437. }
  438. base.RemoveOwnProperty(p);
  439. }
  440. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  441. internal static bool IsArrayIndex(JsValue p, out uint index)
  442. {
  443. if (p is JsNumber number)
  444. {
  445. var value = number._value;
  446. var intValue = (uint) value;
  447. index = intValue;
  448. return value == intValue && intValue != uint.MaxValue;
  449. }
  450. index = ParseArrayIndex(p.ToString());
  451. return index != uint.MaxValue;
  452. // 15.4 - Use an optimized version of the specification
  453. // return TypeConverter.ToString(index) == TypeConverter.ToString(p) && index != uint.MaxValue;
  454. }
  455. internal static uint ParseArrayIndex(string p)
  456. {
  457. if (p.Length == 0)
  458. {
  459. return uint.MaxValue;
  460. }
  461. int d = p[0] - '0';
  462. if (d < 0 || d > 9)
  463. {
  464. return uint.MaxValue;
  465. }
  466. if (d == 0 && p.Length > 1)
  467. {
  468. // If p is a number that start with '0' and is not '0' then
  469. // its ToString representation can't be the same a p. This is
  470. // not a valid array index. '01' !== ToString(ToUInt32('01'))
  471. // http://www.ecma-international.org/ecma-262/5.1/#sec-15.4
  472. return uint.MaxValue;
  473. }
  474. if (p.Length > 1)
  475. {
  476. return StringAsIndex(d, p);
  477. }
  478. return (uint) d;
  479. }
  480. private static uint StringAsIndex(int d, string p)
  481. {
  482. ulong result = (uint) d;
  483. for (int i = 1; i < p.Length; i++)
  484. {
  485. d = p[i] - '0';
  486. if (d < 0 || d > 9)
  487. {
  488. return uint.MaxValue;
  489. }
  490. result = result * 10 + (uint) d;
  491. if (result >= uint.MaxValue)
  492. {
  493. return uint.MaxValue;
  494. }
  495. }
  496. return (uint) result;
  497. }
  498. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  499. internal void SetIndexValue(uint index, JsValue value, bool updateLength)
  500. {
  501. if (updateLength)
  502. {
  503. EnsureCorrectLength(index);
  504. }
  505. WriteArrayValue(index, new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable));
  506. }
  507. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  508. private void EnsureCorrectLength(uint index)
  509. {
  510. var length = GetLength();
  511. if (index >= length)
  512. {
  513. SetLength(index + 1);
  514. }
  515. }
  516. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  517. internal void SetLength(uint length)
  518. {
  519. _length.Value = length;
  520. }
  521. internal uint GetSmallestIndex()
  522. {
  523. if (_dense != null)
  524. {
  525. return 0;
  526. }
  527. uint smallest = 0;
  528. // only try to help if collection reasonable small
  529. if (_sparse.Count > 0 && _sparse.Count < 100 && !_sparse.ContainsKey(0))
  530. {
  531. smallest = uint.MaxValue;
  532. foreach (var key in _sparse.Keys)
  533. {
  534. smallest = System.Math.Min(key, smallest);
  535. }
  536. }
  537. return smallest;
  538. }
  539. public bool TryGetValue(uint index, out JsValue value)
  540. {
  541. value = Undefined;
  542. if (!TryGetDescriptor(index, out var desc))
  543. {
  544. desc = GetProperty(JsString.Create(index));
  545. }
  546. return desc.TryGetValue(this, out value);
  547. }
  548. internal bool DeletePropertyOrThrow(uint index)
  549. {
  550. if (!Delete(index))
  551. {
  552. ExceptionHelper.ThrowTypeError(_engine.Realm);
  553. }
  554. return true;
  555. }
  556. internal bool Delete(uint index)
  557. {
  558. var desc = GetOwnProperty(index);
  559. if (desc == PropertyDescriptor.Undefined)
  560. {
  561. return true;
  562. }
  563. if (desc.Configurable)
  564. {
  565. DeleteAt(index);
  566. return true;
  567. }
  568. return false;
  569. }
  570. internal bool DeleteAt(uint index)
  571. {
  572. var temp = _dense;
  573. if (temp != null)
  574. {
  575. if (index < (uint) temp.Length)
  576. {
  577. temp[index] = null;
  578. return true;
  579. }
  580. }
  581. else
  582. {
  583. return _sparse.Remove(index);
  584. }
  585. return false;
  586. }
  587. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  588. private bool TryGetDescriptor(uint index, out PropertyDescriptor descriptor)
  589. {
  590. var temp = _dense;
  591. if (temp != null)
  592. {
  593. descriptor = null;
  594. if (index < (uint) temp.Length)
  595. {
  596. descriptor = temp[index];
  597. }
  598. return descriptor != null;
  599. }
  600. return _sparse.TryGetValue(index, out descriptor);
  601. }
  602. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  603. internal void WriteArrayValue(uint index, PropertyDescriptor desc)
  604. {
  605. // calculate eagerly so we know if we outgrow
  606. var newSize = _dense != null && index >= (uint) _dense.Length
  607. ? System.Math.Max(index, System.Math.Max(_dense.Length, 2)) * 2
  608. : 0;
  609. bool canUseDense = _dense != null
  610. && index < MaxDenseArrayLength
  611. && newSize < MaxDenseArrayLength
  612. && index < _dense.Length + 50; // looks sparse
  613. if (canUseDense)
  614. {
  615. if (index >= (uint) _dense.Length)
  616. {
  617. EnsureCapacity((uint) newSize);
  618. }
  619. _dense[index] = desc;
  620. }
  621. else
  622. {
  623. if (_dense != null)
  624. {
  625. ConvertToSparse();
  626. }
  627. _sparse[index] = desc;
  628. }
  629. }
  630. private void ConvertToSparse()
  631. {
  632. _sparse = new Dictionary<uint, PropertyDescriptor>(_dense.Length <= 1024 ? _dense.Length : 0);
  633. // need to move data
  634. for (uint i = 0; i < (uint) _dense.Length; ++i)
  635. {
  636. if (_dense[i] != null)
  637. {
  638. _sparse[i] = _dense[i];
  639. }
  640. }
  641. _dense = null;
  642. }
  643. internal void EnsureCapacity(uint capacity)
  644. {
  645. if (capacity > MaxDenseArrayLength || _dense is null || capacity <= (uint) _dense.Length)
  646. {
  647. return;
  648. }
  649. if (capacity > _engine.Options.Constraints.MaxArraySize)
  650. {
  651. ThrowMaximumArraySizeReachedException(_engine, capacity);
  652. }
  653. // need to grow
  654. var newArray = new PropertyDescriptor[capacity];
  655. System.Array.Copy(_dense, newArray, _dense.Length);
  656. _dense = newArray;
  657. }
  658. public IEnumerator<JsValue> GetEnumerator()
  659. {
  660. var length = GetLength();
  661. for (uint i = 0; i < length; i++)
  662. {
  663. TryGetValue(i, out var outValue);
  664. yield return outValue;
  665. }
  666. }
  667. IEnumerator IEnumerable.GetEnumerator()
  668. {
  669. return GetEnumerator();
  670. }
  671. internal uint Push(JsValue[] arguments)
  672. {
  673. var initialLength = GetLength();
  674. var newLength = initialLength + arguments.Length;
  675. // if we see that we are bringing more than normal growth algorithm handles, ensure capacity eagerly
  676. if (_dense != null
  677. && initialLength != 0
  678. && arguments.Length > initialLength * 2
  679. && newLength <= MaxDenseArrayLength)
  680. {
  681. EnsureCapacity((uint) newLength);
  682. }
  683. var canUseDirectIndexSet = _dense != null && newLength <= _dense.Length;
  684. double n = initialLength;
  685. foreach (var argument in arguments)
  686. {
  687. var desc = new PropertyDescriptor(argument, PropertyFlag.ConfigurableEnumerableWritable);
  688. if (canUseDirectIndexSet)
  689. {
  690. _dense[(uint) n] = desc;
  691. }
  692. else
  693. {
  694. WriteValueSlow(n, desc);
  695. }
  696. n++;
  697. }
  698. // check if we can set length fast without breaking ECMA specification
  699. if (n < uint.MaxValue && CanSetLength())
  700. {
  701. _length.Value = (uint) n;
  702. }
  703. else
  704. {
  705. if (!Set(CommonProperties.Length, newLength))
  706. {
  707. ExceptionHelper.ThrowTypeError(_engine.Realm);
  708. }
  709. }
  710. return (uint) n;
  711. }
  712. private bool CanSetLength()
  713. {
  714. if (!_length.IsAccessorDescriptor())
  715. {
  716. return _length.Writable;
  717. }
  718. var set = _length.Set;
  719. return set is not null && !set.IsUndefined();
  720. }
  721. [MethodImpl(MethodImplOptions.NoInlining)]
  722. private void WriteValueSlow(double n, PropertyDescriptor desc)
  723. {
  724. if (n < uint.MaxValue)
  725. {
  726. WriteArrayValue((uint) n, desc);
  727. }
  728. else
  729. {
  730. DefinePropertyOrThrow((uint) n, desc);
  731. }
  732. }
  733. internal ArrayInstance Map(JsValue[] arguments)
  734. {
  735. var callbackfn = arguments.At(0);
  736. var thisArg = arguments.At(1);
  737. var len = GetLength();
  738. var callable = GetCallable(callbackfn);
  739. var a = Engine.Realm.Intrinsics.Array.ArrayCreate(len);
  740. var args = _engine._jsValueArrayPool.RentArray(3);
  741. args[2] = this;
  742. for (uint k = 0; k < len; k++)
  743. {
  744. if (TryGetValue(k, out var kvalue))
  745. {
  746. args[0] = kvalue;
  747. args[1] = k;
  748. var mappedValue = callable.Call(thisArg, args);
  749. var desc = new PropertyDescriptor(mappedValue, PropertyFlag.ConfigurableEnumerableWritable);
  750. if (a._dense != null && k < (uint) a._dense.Length)
  751. {
  752. a._dense[k] = desc;
  753. }
  754. else
  755. {
  756. a.WriteArrayValue(k, desc);
  757. }
  758. }
  759. }
  760. _engine._jsValueArrayPool.ReturnArray(args);
  761. return a;
  762. }
  763. /// <inheritdoc />
  764. internal sealed override bool FindWithCallback(
  765. JsValue[] arguments,
  766. out uint index,
  767. out JsValue value,
  768. bool visitUnassigned,
  769. bool fromEnd = false)
  770. {
  771. var thisArg = arguments.At(1);
  772. var callbackfn = arguments.At(0);
  773. var callable = GetCallable(callbackfn);
  774. var len = GetLength();
  775. if (len == 0)
  776. {
  777. index = 0;
  778. value = Undefined;
  779. return false;
  780. }
  781. var args = _engine._jsValueArrayPool.RentArray(3);
  782. args[2] = this;
  783. if (!fromEnd)
  784. {
  785. for (uint k = 0; k < len; k++)
  786. {
  787. if (TryGetValue(k, out var kvalue) || visitUnassigned)
  788. {
  789. args[0] = kvalue;
  790. args[1] = k;
  791. var testResult = callable.Call(thisArg, args);
  792. if (TypeConverter.ToBoolean(testResult))
  793. {
  794. index = k;
  795. value = kvalue;
  796. return true;
  797. }
  798. }
  799. }
  800. }
  801. else
  802. {
  803. for (long k = len - 1; k >= 0; k--)
  804. {
  805. var idx = (uint) k;
  806. if (TryGetValue(idx, out var kvalue) || visitUnassigned)
  807. {
  808. args[0] = kvalue;
  809. args[1] = idx;
  810. var testResult = callable.Call(thisArg, args);
  811. if (TypeConverter.ToBoolean(testResult))
  812. {
  813. index = idx;
  814. value = kvalue;
  815. return true;
  816. }
  817. }
  818. }
  819. }
  820. _engine._jsValueArrayPool.ReturnArray(args);
  821. index = 0;
  822. value = Undefined;
  823. return false;
  824. }
  825. public sealed override uint Length => GetLength();
  826. internal sealed override bool IsIntegerIndexedArray => true;
  827. public JsValue this[uint index]
  828. {
  829. get
  830. {
  831. TryGetValue(index, out var kValue);
  832. return kValue;
  833. }
  834. }
  835. /// <summary>
  836. /// Fast path for concatenating sane-sized arrays, we assume size has been calculated.
  837. /// </summary>
  838. internal void CopyValues(ArrayInstance source, uint sourceStartIndex, uint targetStartIndex, uint length)
  839. {
  840. if (length == 0)
  841. {
  842. return;
  843. }
  844. var sourceDense = source._dense;
  845. if (sourceDense is not null)
  846. {
  847. EnsureCapacity((uint) sourceDense.LongLength);
  848. }
  849. var dense = _dense;
  850. if (dense != null && sourceDense != null
  851. && (uint) dense.Length >= targetStartIndex + length
  852. && dense[targetStartIndex] is null)
  853. {
  854. uint j = 0;
  855. for (uint i = sourceStartIndex; i < sourceStartIndex + length; ++i, j++)
  856. {
  857. var sourcePropertyDescriptor = i < (uint) sourceDense.Length && sourceDense[i] != null
  858. ? sourceDense[i]
  859. : source.GetProperty(i);
  860. dense[targetStartIndex + j] = sourcePropertyDescriptor?._value is not null
  861. ? new PropertyDescriptor(sourcePropertyDescriptor._value, PropertyFlag.ConfigurableEnumerableWritable)
  862. : null;
  863. }
  864. }
  865. else
  866. {
  867. // slower version
  868. for (uint k = sourceStartIndex; k < length; k++)
  869. {
  870. if (source.TryGetValue(k, out var subElement))
  871. {
  872. SetIndexValue(targetStartIndex, subElement, updateLength: false);
  873. }
  874. targetStartIndex++;
  875. }
  876. }
  877. }
  878. public sealed override string ToString()
  879. {
  880. // debugger can make things hard when evaluates computed values
  881. return "(" + (_length?._value.AsNumber() ?? 0) + ")[]";
  882. }
  883. private static void ThrowMaximumArraySizeReachedException(Engine engine, uint capacity)
  884. {
  885. ExceptionHelper.ThrowMemoryLimitExceededException(
  886. $"The array size {capacity} is larger than maximum allowed ({engine.Options.Constraints.MaxArraySize})"
  887. );
  888. }
  889. }
  890. internal static class ArrayPropertyDescriptorExtensions
  891. {
  892. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  893. internal static bool IsDefaultArrayValueDescriptor(this PropertyDescriptor propertyDescriptor)
  894. => propertyDescriptor.Flags == PropertyFlag.ConfigurableEnumerableWritable && propertyDescriptor.IsDataDescriptor();
  895. }
  896. }