ArrayInstance.cs 38 KB

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