ArrayInstance.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. using System.Collections.Generic;
  2. using System.Runtime.CompilerServices;
  3. using Jint.Native.Object;
  4. using Jint.Runtime;
  5. using Jint.Runtime.Descriptors;
  6. using Jint.Runtime.Descriptors.Specialized;
  7. using PropertyDescriptor = Jint.Runtime.Descriptors.PropertyDescriptor;
  8. using TypeConverter = Jint.Runtime.TypeConverter;
  9. namespace Jint.Native.Array
  10. {
  11. public class ArrayInstance : ObjectInstance
  12. {
  13. private readonly Engine _engine;
  14. private const int MaxDenseArrayLength = 1024 * 10;
  15. // we have dense and sparse, we usually can start with dense and fall back to sparse when necessary
  16. private IPropertyDescriptor[] _dense;
  17. private Dictionary<uint, IPropertyDescriptor> _sparse;
  18. public ArrayInstance(Engine engine, uint capacity = 0) : base(engine)
  19. {
  20. _engine = engine;
  21. if (capacity < MaxDenseArrayLength)
  22. {
  23. _dense = capacity > 0 ? new IPropertyDescriptor[capacity] : System.Array.Empty<IPropertyDescriptor>();
  24. }
  25. else
  26. {
  27. _sparse = new Dictionary<uint, IPropertyDescriptor>((int) (capacity <= 1024 ? capacity : 1024));
  28. }
  29. }
  30. public override string Class => "Array";
  31. /// Implementation from ObjectInstance official specs as the one
  32. /// in ObjectInstance is optimized for the general case and wouldn't work
  33. /// for arrays
  34. public override void Put(string propertyName, JsValue value, bool throwOnError)
  35. {
  36. if (!CanPut(propertyName))
  37. {
  38. if (throwOnError)
  39. {
  40. throw new JavaScriptException(Engine.TypeError);
  41. }
  42. return;
  43. }
  44. var ownDesc = GetOwnProperty(propertyName);
  45. if (ownDesc.IsDataDescriptor())
  46. {
  47. var valueDesc = new NullConfigurationPropertyDescriptor(value);
  48. DefineOwnProperty(propertyName, valueDesc, throwOnError);
  49. return;
  50. }
  51. // property is an accessor or inherited
  52. var desc = GetProperty(propertyName);
  53. if (desc.IsAccessorDescriptor())
  54. {
  55. var setter = desc.Set.TryCast<ICallable>();
  56. setter.Call(JsValue, new[] {value});
  57. }
  58. else
  59. {
  60. var newDesc = new ConfigurableEnumerableWritablePropertyDescriptor(value);
  61. DefineOwnProperty(propertyName, newDesc, throwOnError);
  62. }
  63. }
  64. public override bool DefineOwnProperty(string propertyName, IPropertyDescriptor desc, bool throwOnError)
  65. {
  66. var oldLenDesc = GetOwnProperty("length");
  67. var oldLen = (uint) TypeConverter.ToNumber(oldLenDesc.Value);
  68. if (propertyName == "length")
  69. {
  70. if (desc.Value == null)
  71. {
  72. return base.DefineOwnProperty("length", desc, throwOnError);
  73. }
  74. var newLenDesc = new PropertyDescriptor(desc);
  75. uint newLen = TypeConverter.ToUint32(desc.Value);
  76. if (newLen != TypeConverter.ToNumber(desc.Value))
  77. {
  78. throw new JavaScriptException(_engine.RangeError);
  79. }
  80. newLenDesc.Value = newLen;
  81. if (newLen >= oldLen)
  82. {
  83. return base.DefineOwnProperty("length", newLenDesc, throwOnError);
  84. }
  85. if (!oldLenDesc.Writable.Value)
  86. {
  87. if (throwOnError)
  88. {
  89. throw new JavaScriptException(_engine.TypeError);
  90. }
  91. return false;
  92. }
  93. bool newWritable;
  94. if (!newLenDesc.Writable.HasValue || newLenDesc.Writable.Value)
  95. {
  96. newWritable = true;
  97. }
  98. else
  99. {
  100. newWritable = false;
  101. newLenDesc.Writable = true;
  102. }
  103. var succeeded = base.DefineOwnProperty("length", newLenDesc, throwOnError);
  104. if (!succeeded)
  105. {
  106. return false;
  107. }
  108. int count = 0;
  109. if (_dense != null)
  110. {
  111. for (int i = 0; i < _dense.Length; ++i)
  112. {
  113. if (_dense[i] != null)
  114. {
  115. count++;
  116. }
  117. }
  118. }
  119. else
  120. {
  121. count = _sparse.Count;
  122. }
  123. if (count < oldLen - newLen)
  124. {
  125. if (_dense != null)
  126. {
  127. for (uint keyIndex = 0; keyIndex < _dense.Length; ++keyIndex)
  128. {
  129. if (_dense[keyIndex] == null)
  130. {
  131. continue;
  132. }
  133. // is it the index of the array
  134. if (keyIndex >= newLen && keyIndex < oldLen)
  135. {
  136. var deleteSucceeded = Delete(TypeConverter.ToString(keyIndex), false);
  137. if (!deleteSucceeded)
  138. {
  139. newLenDesc.Value = keyIndex + 1;
  140. if (!newWritable)
  141. {
  142. newLenDesc.Writable = false;
  143. }
  144. base.DefineOwnProperty("length", newLenDesc, false);
  145. if (throwOnError)
  146. {
  147. throw new JavaScriptException(_engine.TypeError);
  148. }
  149. return false;
  150. }
  151. }
  152. }
  153. }
  154. else
  155. {
  156. // in the case of sparse arrays, treat each concrete element instead of
  157. // iterating over all indexes
  158. var keys = ArrayExecutionContext.Current.KeyCache;
  159. keys.Clear();
  160. keys.AddRange(_sparse.Keys);
  161. foreach (var keyIndex in keys)
  162. {
  163. // is it the index of the array
  164. if (keyIndex >= newLen && keyIndex < oldLen)
  165. {
  166. var deleteSucceeded = Delete(TypeConverter.ToString(keyIndex), false);
  167. if (!deleteSucceeded)
  168. {
  169. newLenDesc.Value = JsValue.FromInt(keyIndex + 1);
  170. if (!newWritable)
  171. {
  172. newLenDesc.Writable = false;
  173. }
  174. base.DefineOwnProperty("length", newLenDesc, false);
  175. if (throwOnError)
  176. {
  177. throw new JavaScriptException(_engine.TypeError);
  178. }
  179. return false;
  180. }
  181. }
  182. }
  183. }
  184. }
  185. else
  186. {
  187. while (newLen < oldLen)
  188. {
  189. // algorithm as per the spec
  190. oldLen--;
  191. var deleteSucceeded = Delete(TypeConverter.ToString(oldLen), false);
  192. if (!deleteSucceeded)
  193. {
  194. newLenDesc.Value = oldLen + 1;
  195. if (!newWritable)
  196. {
  197. newLenDesc.Writable = false;
  198. }
  199. base.DefineOwnProperty("length", newLenDesc, false);
  200. if (throwOnError)
  201. {
  202. throw new JavaScriptException(_engine.TypeError);
  203. }
  204. return false;
  205. }
  206. }
  207. }
  208. if (!newWritable)
  209. {
  210. DefineOwnProperty("length", new PropertyDescriptor(value: null, writable: false, enumerable: null, configurable: null), false);
  211. }
  212. return true;
  213. }
  214. else if (IsArrayIndex(propertyName, out var index))
  215. {
  216. if (index >= oldLen && !oldLenDesc.Writable.Value)
  217. {
  218. if (throwOnError)
  219. {
  220. throw new JavaScriptException(_engine.TypeError);
  221. }
  222. return false;
  223. }
  224. var succeeded = base.DefineOwnProperty(propertyName, desc, false);
  225. if (!succeeded)
  226. {
  227. if (throwOnError)
  228. {
  229. throw new JavaScriptException(_engine.TypeError);
  230. }
  231. return false;
  232. }
  233. if (index >= oldLen)
  234. {
  235. oldLenDesc.Value = index + 1;
  236. base.DefineOwnProperty("length", oldLenDesc, false);
  237. }
  238. return true;
  239. }
  240. return base.DefineOwnProperty(propertyName, desc, throwOnError);
  241. }
  242. public uint GetLength()
  243. {
  244. return GetLengthValue();
  245. }
  246. public override IEnumerable<KeyValuePair<string, IPropertyDescriptor>> GetOwnProperties()
  247. {
  248. if (_dense != null)
  249. {
  250. for (var i = 0; i < _dense.Length; i++)
  251. {
  252. if (_dense[i] != null)
  253. {
  254. yield return new KeyValuePair<string, IPropertyDescriptor>(TypeConverter.ToString(i), _dense[i]);
  255. }
  256. }
  257. }
  258. else
  259. {
  260. foreach (var entry in _sparse)
  261. {
  262. yield return new KeyValuePair<string, IPropertyDescriptor>(TypeConverter.ToString(entry.Key), entry.Value);
  263. }
  264. }
  265. foreach (var entry in base.GetOwnProperties())
  266. {
  267. yield return entry;
  268. }
  269. }
  270. public override IPropertyDescriptor GetOwnProperty(string propertyName)
  271. {
  272. if (IsArrayIndex(propertyName, out var index))
  273. {
  274. if (TryGetDescriptor(index, out var result))
  275. {
  276. return result;
  277. }
  278. return PropertyDescriptor.Undefined;
  279. }
  280. return base.GetOwnProperty(propertyName);
  281. }
  282. protected internal override void SetOwnProperty(string propertyName, IPropertyDescriptor desc)
  283. {
  284. if (IsArrayIndex(propertyName, out var index))
  285. {
  286. WriteArrayValue(index, desc);
  287. }
  288. else
  289. {
  290. base.SetOwnProperty(propertyName, desc);
  291. }
  292. }
  293. public override bool HasOwnProperty(string p)
  294. {
  295. if (IsArrayIndex(p, out var index))
  296. {
  297. return index < GetLengthValue()
  298. && (_sparse == null || _sparse.ContainsKey(index))
  299. && (_dense == null || _dense[index] != null);
  300. }
  301. return base.HasOwnProperty(p);
  302. }
  303. public override void RemoveOwnProperty(string p)
  304. {
  305. uint index;
  306. if (IsArrayIndex(p, out index))
  307. {
  308. DeleteAt(index);
  309. }
  310. base.RemoveOwnProperty(p);
  311. }
  312. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  313. private static bool IsArrayIndex(string p, out uint index)
  314. {
  315. index = ParseArrayIndex(p);
  316. return index != uint.MaxValue;
  317. // 15.4 - Use an optimized version of the specification
  318. // return TypeConverter.ToString(index) == TypeConverter.ToString(p) && index != uint.MaxValue;
  319. }
  320. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  321. private static uint ParseArrayIndex(string p)
  322. {
  323. int d = p[0] - '0';
  324. if (d < 0 || d > 9)
  325. {
  326. return uint.MaxValue;
  327. }
  328. if (d == 0 && p.Length > 1)
  329. {
  330. // If p is a number that start with '0' and is not '0' then
  331. // its ToString representation can't be the same a p. This is
  332. // not a valid array index. '01' !== ToString(ToUInt32('01'))
  333. // http://www.ecma-international.org/ecma-262/5.1/#sec-15.4
  334. return uint.MaxValue;
  335. }
  336. ulong result = (uint) d;
  337. for (int i = 1; i < p.Length; i++)
  338. {
  339. d = p[i] - '0';
  340. if (d < 0 || d > 9)
  341. {
  342. return uint.MaxValue;
  343. }
  344. result = result * 10 + (uint) d;
  345. if (result >= uint.MaxValue)
  346. {
  347. return uint.MaxValue;
  348. }
  349. }
  350. return (uint) result;
  351. }
  352. internal void SetIndexValue(uint index, JsValue value, bool throwOnError)
  353. {
  354. var length = GetLengthValue();
  355. if (index >= length)
  356. {
  357. var p = base.GetOwnProperty("length");
  358. p.Value = index + 1;
  359. }
  360. WriteArrayValue(index, new ConfigurableEnumerableWritablePropertyDescriptor(value));
  361. }
  362. internal uint GetSmallestIndex()
  363. {
  364. if (_dense != null)
  365. {
  366. return 0;
  367. }
  368. uint smallest = 0;
  369. // only try to help if collection reasonable small
  370. if (_sparse.Count > 0 && _sparse.Count < 100 && !_sparse.ContainsKey(0))
  371. {
  372. smallest = uint.MaxValue;
  373. foreach (var key in _sparse.Keys)
  374. {
  375. smallest = System.Math.Min(key, smallest);
  376. }
  377. }
  378. return smallest;
  379. }
  380. public bool TryGetValue(uint index, out JsValue value)
  381. {
  382. value = JsValue.Undefined;
  383. if (!TryGetDescriptor(index, out var desc)
  384. || desc == null
  385. || desc == PropertyDescriptor.Undefined
  386. || (desc.Value == null && desc.Get == null))
  387. {
  388. desc = GetProperty(TypeConverter.ToString(index));
  389. }
  390. if (desc != null && desc != PropertyDescriptor.Undefined)
  391. {
  392. bool success = desc.TryGetValue(this, out value);
  393. return success;
  394. }
  395. return false;
  396. }
  397. internal void DeleteAt(uint index)
  398. {
  399. if (_dense != null)
  400. {
  401. if (index < _dense.Length)
  402. {
  403. _dense[index] = null;
  404. }
  405. }
  406. else
  407. {
  408. _sparse.Remove(index);
  409. }
  410. }
  411. private bool TryGetDescriptor(uint index, out IPropertyDescriptor descriptor)
  412. {
  413. if (_dense != null)
  414. {
  415. if (index >= _dense.Length)
  416. {
  417. descriptor = null;
  418. return false;
  419. }
  420. descriptor = _dense[index];
  421. return descriptor != null;
  422. }
  423. return _sparse.TryGetValue(index, out descriptor);
  424. }
  425. private void WriteArrayValue(uint index, IPropertyDescriptor desc)
  426. {
  427. // calculate eagerly so we know if we outgrow
  428. var newSize = _dense != null && index >= _dense.Length
  429. ? System.Math.Max(index, System.Math.Max(_dense.Length, 2)) * 2
  430. : 0;
  431. bool canUseDense = _dense != null
  432. && index < MaxDenseArrayLength
  433. && newSize < MaxDenseArrayLength
  434. && index < _dense.Length + 50; // looks sparse
  435. if (canUseDense)
  436. {
  437. if (index >= _dense.Length)
  438. {
  439. EnsureCapacity((uint) newSize);
  440. }
  441. _dense[index] = desc;
  442. }
  443. else
  444. {
  445. if (_dense != null)
  446. {
  447. _sparse = new Dictionary<uint, IPropertyDescriptor>(_dense.Length <= 1024 ? _dense.Length : 0);
  448. // need to move data
  449. for (uint i = 0; i < _dense.Length; ++i)
  450. {
  451. if (_dense[i] != null)
  452. {
  453. _sparse[i] = _dense[i];
  454. }
  455. }
  456. _dense = null;
  457. }
  458. _sparse[index] = desc;
  459. }
  460. }
  461. internal void EnsureCapacity(uint capacity)
  462. {
  463. if (capacity > _dense.Length)
  464. {
  465. // need to grow
  466. var newArray = new IPropertyDescriptor[capacity];
  467. System.Array.Copy(_dense, newArray, _dense.Length);
  468. _dense = newArray;
  469. }
  470. }
  471. }
  472. }