2
0

ArrayInstance.cs 43 KB

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