ArrayInstance.cs 42 KB

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