ArrayInstance.cs 43 KB

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