ArrayPrototype.cs 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262
  1. using System;
  2. using System.Collections.Generic;
  3. using Jint.Collections;
  4. using Jint.Native.Object;
  5. using Jint.Native.Symbol;
  6. using Jint.Pooling;
  7. using Jint.Runtime;
  8. using Jint.Runtime.Descriptors;
  9. using Jint.Runtime.Interop;
  10. using Jint.Runtime.Interpreter.Expressions;
  11. using static System.String;
  12. namespace Jint.Native.Array
  13. {
  14. /// <summary>
  15. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4
  16. /// </summary>
  17. public sealed class ArrayPrototype : ArrayInstance
  18. {
  19. private ArrayConstructor _arrayConstructor;
  20. private ArrayPrototype(Engine engine) : base(engine)
  21. {
  22. }
  23. public static ArrayPrototype CreatePrototypeObject(Engine engine, ArrayConstructor arrayConstructor)
  24. {
  25. var obj = new ArrayPrototype(engine)
  26. {
  27. _prototype = engine.Object.PrototypeObject,
  28. _length = new PropertyDescriptor(JsNumber.PositiveZero, PropertyFlag.Writable),
  29. _arrayConstructor = arrayConstructor,
  30. };
  31. return obj;
  32. }
  33. protected override void Initialize()
  34. {
  35. _properties = new StringDictionarySlim<PropertyDescriptor>(35)
  36. {
  37. ["constructor"] = new PropertyDescriptor(_arrayConstructor, PropertyFlag.NonEnumerable),
  38. ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToString, 0, PropertyFlag.Configurable), true, false, true),
  39. ["toLocaleString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toLocaleString", ToLocaleString, 0, PropertyFlag.Configurable), true, false, true),
  40. ["concat"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "concat", Concat, 1, PropertyFlag.Configurable), true, false, true),
  41. ["copyWithin"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "copyWithin", CopyWithin, 2, PropertyFlag.Configurable), true, false, true),
  42. ["entries"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "entries", Iterator, 0, PropertyFlag.Configurable), true, false, true),
  43. ["fill"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "fill", Fill, 1, PropertyFlag.Configurable), true, false, true),
  44. ["join"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "join", Join, 1, PropertyFlag.Configurable), true, false, true),
  45. ["pop"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "pop", Pop, 0, PropertyFlag.Configurable), true, false, true),
  46. ["push"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "push", Push, 1, PropertyFlag.Configurable), true, false, true),
  47. ["reverse"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "reverse", Reverse, 0, PropertyFlag.Configurable), true, false, true),
  48. ["shift"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "shift", Shift, 0, PropertyFlag.Configurable), true, false, true),
  49. ["slice"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "slice", Slice, 2, PropertyFlag.Configurable), true, false, true),
  50. ["sort"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "sort", Sort, 1, PropertyFlag.Configurable), true, false, true),
  51. ["splice"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "splice", Splice, 2, PropertyFlag.Configurable), true, false, true),
  52. ["unshift"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "unshift", Unshift, 1, PropertyFlag.Configurable), true, false, true),
  53. ["includes"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "includes", Includes, 1, PropertyFlag.Configurable), true, false, true),
  54. ["indexOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "indexOf", IndexOf, 1, PropertyFlag.Configurable), true, false, true),
  55. ["lastIndexOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "lastIndexOf", LastIndexOf, 1, PropertyFlag.Configurable), true, false, true),
  56. ["every"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "every", Every, 1, PropertyFlag.Configurable), true, false, true),
  57. ["some"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "some", Some, 1, PropertyFlag.Configurable), true, false, true),
  58. ["forEach"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "forEach", ForEach, 1, PropertyFlag.Configurable), true, false, true),
  59. ["map"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "map", Map, 1, PropertyFlag.Configurable), true, false, true),
  60. ["filter"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "filter", Filter, 1, PropertyFlag.Configurable), true, false, true),
  61. ["reduce"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "reduce", Reduce, 1, PropertyFlag.Configurable), true, false, true),
  62. ["reduceRight"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "reduceRight", ReduceRight, 1, PropertyFlag.Configurable), true, false, true),
  63. ["find"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "find", Find, 1, PropertyFlag.Configurable), true, false, true),
  64. ["findIndex"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "findIndex", FindIndex, 1, PropertyFlag.Configurable), true, false, true),
  65. ["keys"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "keys", Keys, 0, PropertyFlag.Configurable), true, false, true),
  66. ["values"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "values", Values, 0, PropertyFlag.Configurable), true, false, true),
  67. [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "iterator", Values, 1), true, false, true)
  68. };
  69. }
  70. private ObjectInstance Keys(JsValue thisObj, JsValue[] arguments)
  71. {
  72. if (thisObj is ObjectInstance oi && oi.IsArrayLike)
  73. {
  74. return _engine.Iterator.ConstructArrayLikeKeyIterator(oi);
  75. }
  76. return ExceptionHelper.ThrowTypeError<ObjectInstance>(_engine, "cannot construct iterator");
  77. }
  78. private ObjectInstance Values(JsValue thisObj, JsValue[] arguments)
  79. {
  80. if (thisObj is ObjectInstance oi && oi.IsArrayLike)
  81. {
  82. return _engine.Iterator.ConstructArrayLikeValueIterator(oi);
  83. }
  84. return ExceptionHelper.ThrowTypeError<ObjectInstance>(_engine, "cannot construct iterator");
  85. }
  86. private ObjectInstance Iterator(JsValue thisObj, JsValue[] arguments)
  87. {
  88. if (thisObj is ObjectInstance oi)
  89. {
  90. return _engine.Iterator.Construct(oi);
  91. }
  92. return ExceptionHelper.ThrowTypeError<ObjectInstance>(_engine, "cannot construct iterator");
  93. }
  94. private JsValue Fill(JsValue thisObj, JsValue[] arguments)
  95. {
  96. if (thisObj.IsNullOrUndefined())
  97. {
  98. ExceptionHelper.ThrowTypeError(_engine, "Cannot convert undefined or null to object");
  99. }
  100. var operations = ArrayOperations.For(thisObj as ObjectInstance);
  101. var length = operations.GetLength();
  102. var value = arguments.At(0);
  103. var start = ConvertAndCheckForInfinity(arguments.At(1), 0);
  104. var relativeStart = TypeConverter.ToInteger(start);
  105. uint actualStart;
  106. if (relativeStart < 0)
  107. {
  108. actualStart = (uint) System.Math.Max(length + relativeStart, 0);
  109. }
  110. else
  111. {
  112. actualStart = (uint) System.Math.Min(relativeStart, length);
  113. }
  114. var end = ConvertAndCheckForInfinity(arguments.At(2), length);
  115. var relativeEnd = TypeConverter.ToInteger(end);
  116. uint actualEnd;
  117. if (relativeEnd < 0)
  118. {
  119. actualEnd = (uint) System.Math.Max(length + relativeEnd, 0);
  120. }
  121. else
  122. {
  123. actualEnd = (uint) System.Math.Min(relativeEnd, length);
  124. }
  125. for (var i = actualStart; i < actualEnd; ++i)
  126. {
  127. operations.Set(i, value, false);
  128. }
  129. return thisObj;
  130. }
  131. private JsValue CopyWithin(JsValue thisObj, JsValue[] arguments)
  132. {
  133. // Steps 1-2.
  134. if (thisObj.IsNullOrUndefined())
  135. {
  136. return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "this is null or not defined");
  137. }
  138. JsValue target = arguments.At(0);
  139. JsValue start = arguments.At(1);
  140. JsValue end = arguments.At(2);
  141. var operations = ArrayOperations.For(thisObj as ObjectInstance);
  142. var initialLength = operations.GetLength();
  143. var len = ConvertAndCheckForInfinity(initialLength, 0);
  144. var relativeTarget = ConvertAndCheckForInfinity(target, 0);
  145. var to = relativeTarget < 0 ?
  146. System.Math.Max(len + relativeTarget, 0) :
  147. System.Math.Min(relativeTarget, len);
  148. var relativeStart = ConvertAndCheckForInfinity(start, 0);
  149. var from = relativeStart < 0 ?
  150. System.Math.Max(len + relativeStart, 0) :
  151. System.Math.Min(relativeStart, len);
  152. var relativeEnd = ConvertAndCheckForInfinity(end, len);
  153. var final = relativeEnd < 0 ?
  154. System.Math.Max(len + relativeEnd, 0) :
  155. System.Math.Min(relativeEnd, len);
  156. var count = System.Math.Min(final - from, len - to);
  157. var direction = 1;
  158. if (from < to && to < from + count) {
  159. direction = -1;
  160. from += (uint) count - 1;
  161. to += (uint) count - 1;
  162. }
  163. while (count > 0)
  164. {
  165. var fromPresent = operations.HasProperty((ulong) from);
  166. if (fromPresent)
  167. {
  168. var fromValue = operations.Get((ulong) from);
  169. operations.Set((ulong) to, fromValue, throwOnError: true);
  170. }
  171. else
  172. {
  173. operations.DeletePropertyOrThrow((ulong) to);
  174. }
  175. from = (uint) (from + direction);
  176. to = (uint) (to + direction);
  177. count--;
  178. }
  179. return thisObj;
  180. }
  181. long ConvertAndCheckForInfinity(JsValue jsValue, long defaultValue)
  182. {
  183. if (jsValue.IsUndefined())
  184. {
  185. return defaultValue;
  186. }
  187. var num = TypeConverter.ToNumber(jsValue);
  188. if (double.IsPositiveInfinity(num))
  189. {
  190. return long.MaxValue;
  191. }
  192. return (long) num;
  193. }
  194. private JsValue LastIndexOf(JsValue thisObj, JsValue[] arguments)
  195. {
  196. var o = ArrayOperations.For(Engine, thisObj);
  197. var len = o.GetLongLength();
  198. if (len == 0)
  199. {
  200. return -1;
  201. }
  202. var n = arguments.Length > 1
  203. ? TypeConverter.ToInteger(arguments[1])
  204. : len - 1;
  205. double k;
  206. if (n >= 0)
  207. {
  208. k = System.Math.Min(n, len - 1); // min
  209. }
  210. else
  211. {
  212. k = len - System.Math.Abs(n);
  213. }
  214. if (k < 0 || k > uint.MaxValue)
  215. {
  216. return -1;
  217. }
  218. var searchElement = arguments.At(0);
  219. var i = (uint) k;
  220. for (;; i--)
  221. {
  222. var kPresent = o.HasProperty(i);
  223. if (kPresent)
  224. {
  225. var elementK = o.Get(i);
  226. var same = JintBinaryExpression.StrictlyEqual(elementK, searchElement);
  227. if (same)
  228. {
  229. return i;
  230. }
  231. }
  232. if (i == 0)
  233. {
  234. break;
  235. }
  236. }
  237. return -1;
  238. }
  239. private JsValue Reduce(JsValue thisObj, JsValue[] arguments)
  240. {
  241. var callbackfn = arguments.At(0);
  242. var initialValue = arguments.At(1);
  243. var o = ArrayOperations.For(Engine, thisObj);
  244. var len = o.GetLength();
  245. var callable = GetCallable(callbackfn);
  246. if (len == 0 && arguments.Length < 2)
  247. {
  248. ExceptionHelper.ThrowTypeError(Engine);
  249. }
  250. var k = 0;
  251. JsValue accumulator = Undefined;
  252. if (arguments.Length > 1)
  253. {
  254. accumulator = initialValue;
  255. }
  256. else
  257. {
  258. var kPresent = false;
  259. while (kPresent == false && k < len)
  260. {
  261. if (kPresent = o.TryGetValue((uint) k, out var temp))
  262. {
  263. accumulator = temp;
  264. }
  265. k++;
  266. }
  267. if (kPresent == false)
  268. {
  269. ExceptionHelper.ThrowTypeError(Engine);
  270. }
  271. }
  272. var args = new JsValue[4];
  273. args[3] = o.Target;
  274. while (k < len)
  275. {
  276. var i = (uint) k;
  277. if (o.TryGetValue(i, out var kvalue))
  278. {
  279. args[0] = accumulator;
  280. args[1] = kvalue;
  281. args[2] = i;
  282. accumulator = callable.Call(Undefined, args);
  283. }
  284. k++;
  285. }
  286. return accumulator;
  287. }
  288. private JsValue Filter(JsValue thisObj, JsValue[] arguments)
  289. {
  290. var callbackfn = arguments.At(0);
  291. var thisArg = arguments.At(1);
  292. var o = ArrayOperations.For(Engine, thisObj);
  293. var len = o.GetLength();
  294. var callable = GetCallable(callbackfn);
  295. var a = Engine.Array.ArraySpeciesCreate(TypeConverter.ToObject(_engine, thisObj), 0);
  296. uint to = 0;
  297. var args = _engine._jsValueArrayPool.RentArray(3);
  298. args[2] = o.Target;
  299. for (uint k = 0; k < len; k++)
  300. {
  301. if (o.TryGetValue(k, out var kvalue))
  302. {
  303. args[0] = kvalue;
  304. args[1] = k;
  305. var selected = callable.Call(thisArg, args);
  306. if (TypeConverter.ToBoolean(selected))
  307. {
  308. a.SetIndexValue(to, kvalue, updateLength: false);
  309. to++;
  310. }
  311. }
  312. }
  313. a.SetLength(to);
  314. _engine._jsValueArrayPool.ReturnArray(args);
  315. return a;
  316. }
  317. private JsValue Map(JsValue thisObj, JsValue[] arguments)
  318. {
  319. if (thisObj is ArrayInstance arrayInstance)
  320. {
  321. return arrayInstance.Map(arguments);
  322. }
  323. var o = ArrayOperations.For(Engine, thisObj);
  324. var len = o.GetLongLength();
  325. if (len > ArrayOperations.MaxArrayLength)
  326. {
  327. ExceptionHelper.ThrowRangeError(_engine, "Invalid array length");;
  328. }
  329. var callbackfn = arguments.At(0);
  330. var thisArg = arguments.At(1);
  331. var callable = GetCallable(callbackfn);
  332. var a = Engine.Array.ArraySpeciesCreate(TypeConverter.ToObject(_engine, thisObj), (uint) len);
  333. var args = _engine._jsValueArrayPool.RentArray(3);
  334. args[2] = o.Target;
  335. for (uint k = 0; k < len; k++)
  336. {
  337. if (o.TryGetValue(k, out var kvalue))
  338. {
  339. args[0] = kvalue;
  340. args[1] = k;
  341. var mappedValue = callable.Call(thisArg, args);
  342. a.SetIndexValue(k, mappedValue, updateLength: false);
  343. }
  344. }
  345. _engine._jsValueArrayPool.ReturnArray(args);
  346. return a;
  347. }
  348. private JsValue ForEach(JsValue thisObj, JsValue[] arguments)
  349. {
  350. var callbackfn = arguments.At(0);
  351. var thisArg = arguments.At(1);
  352. var o = ArrayOperations.For(Engine, thisObj);
  353. var len = o.GetLength();
  354. var callable = GetCallable(callbackfn);
  355. var args = _engine._jsValueArrayPool.RentArray(3);
  356. args[2] = o.Target;
  357. for (uint k = 0; k < len; k++)
  358. {
  359. if (o.TryGetValue(k, out var kvalue))
  360. {
  361. args[0] = kvalue;
  362. args[1] = k;
  363. callable.Call(thisArg, args);
  364. }
  365. }
  366. _engine._jsValueArrayPool.ReturnArray(args);
  367. return Undefined;
  368. }
  369. private JsValue Includes(JsValue thisObj, JsValue[] arguments)
  370. {
  371. var o = ArrayOperations.For(Engine, thisObj);
  372. var len = o.GetLongLength();
  373. if (len == 0)
  374. {
  375. return false;
  376. }
  377. var searchElement = arguments.At(0);
  378. var fromIndex = arguments.At(1, 0);
  379. var n = TypeConverter.ToNumber(fromIndex);
  380. n = n > ArrayOperations.MaxArrayLikeLength
  381. ? ArrayOperations.MaxArrayLikeLength
  382. : n;
  383. var k = (ulong) System.Math.Max(
  384. n >= 0
  385. ? n
  386. : len - System.Math.Abs(n), 0);
  387. static bool SameValueZero(JsValue x, JsValue y) {
  388. return x == y
  389. || (x is JsNumber xNum && y is JsNumber yNum && double.IsNaN(xNum._value) && double.IsNaN(yNum._value));
  390. }
  391. while (k < len)
  392. {
  393. var value = o.Get(k);
  394. if (SameValueZero(value, searchElement))
  395. {
  396. return true;
  397. }
  398. k++;
  399. }
  400. return false;
  401. }
  402. private JsValue Some(JsValue thisObj, JsValue[] arguments)
  403. {
  404. var target = TypeConverter.ToObject(Engine, thisObj);
  405. return target.FindWithCallback(arguments, out _, out _, false);
  406. }
  407. private JsValue Every(JsValue thisObj, JsValue[] arguments)
  408. {
  409. var o = ArrayOperations.For(Engine, thisObj);
  410. ulong len = o.GetLongLength();
  411. if (len == 0)
  412. {
  413. return JsBoolean.True;
  414. }
  415. var callbackfn = arguments.At(0);
  416. var thisArg = arguments.At(1);
  417. var callable = GetCallable(callbackfn);
  418. var args = _engine._jsValueArrayPool.RentArray(3);
  419. args[2] = o.Target;
  420. for (uint k = 0; k < len; k++)
  421. {
  422. if (o.TryGetValue(k, out var kvalue))
  423. {
  424. args[0] = kvalue;
  425. args[1] = k;
  426. var testResult = callable.Call(thisArg, args);
  427. if (false == TypeConverter.ToBoolean(testResult))
  428. {
  429. return JsBoolean.False;
  430. }
  431. }
  432. }
  433. _engine._jsValueArrayPool.ReturnArray(args);
  434. return JsBoolean.True;
  435. }
  436. private JsValue IndexOf(JsValue thisObj, JsValue[] arguments)
  437. {
  438. var o = ArrayOperations.For(Engine, thisObj);
  439. var len = o.GetLongLength();
  440. if (len == 0)
  441. {
  442. return -1;
  443. }
  444. var startIndex = arguments.Length > 1
  445. ? TypeConverter.ToNumber(arguments[1])
  446. : 0;
  447. if (startIndex > uint.MaxValue)
  448. {
  449. return -1;
  450. }
  451. ulong k;
  452. if (startIndex < 0)
  453. {
  454. var abs = System.Math.Abs(startIndex);
  455. ulong temp = len - (uint) abs;
  456. if (abs > len || temp < 0)
  457. {
  458. temp = 0;
  459. }
  460. k = temp;
  461. }
  462. else
  463. {
  464. k = (ulong) startIndex;
  465. }
  466. if (k >= len)
  467. {
  468. return -1;
  469. }
  470. ulong smallestIndex = o.GetSmallestIndex(len);
  471. if (smallestIndex > k)
  472. {
  473. k = smallestIndex;
  474. }
  475. var searchElement = arguments.At(0);
  476. for (; k < len; k++)
  477. {
  478. var kPresent = o.HasProperty(k);
  479. if (kPresent)
  480. {
  481. var elementK = o.Get(k);
  482. var same = JintBinaryExpression.StrictlyEqual(elementK, searchElement);
  483. if (same)
  484. {
  485. return k;
  486. }
  487. }
  488. }
  489. return -1;
  490. }
  491. private JsValue Find(JsValue thisObj, JsValue[] arguments)
  492. {
  493. var target = TypeConverter.ToObject(Engine, thisObj);
  494. target.FindWithCallback(arguments, out _, out var value, true);
  495. return value;
  496. }
  497. private JsValue FindIndex(JsValue thisObj, JsValue[] arguments)
  498. {
  499. var target = TypeConverter.ToObject(Engine, thisObj);
  500. if (target.FindWithCallback(arguments, out var index, out _, true))
  501. {
  502. return index;
  503. }
  504. return -1;
  505. }
  506. private JsValue Splice(JsValue thisObj, JsValue[] arguments)
  507. {
  508. var start = arguments.At(0);
  509. var deleteCount = arguments.At(1);
  510. var o = ArrayOperations.For(Engine, thisObj);
  511. var len = o.GetLongLength();
  512. var relativeStart = TypeConverter.ToInteger(start);
  513. ulong actualStart;
  514. if (relativeStart < 0)
  515. {
  516. actualStart = (ulong) System.Math.Max(len + relativeStart, 0);
  517. }
  518. else
  519. {
  520. actualStart = (ulong) System.Math.Min(relativeStart, len);
  521. }
  522. var items = ArrayExt.Empty<JsValue>();
  523. ulong insertCount;
  524. ulong actualDeleteCount;
  525. if (arguments.Length == 0)
  526. {
  527. insertCount = 0;
  528. actualDeleteCount = 0;
  529. }
  530. else if (arguments.Length == 1)
  531. {
  532. insertCount = 0;
  533. actualDeleteCount = len - actualStart;
  534. }
  535. else
  536. {
  537. insertCount = (ulong) (arguments.Length - 2);
  538. var dc = TypeConverter.ToInteger(deleteCount);
  539. actualDeleteCount = (ulong) System.Math.Min(System.Math.Max(dc,0), len - actualStart);
  540. items = new JsValue[arguments.Length - 2];
  541. System.Array.Copy(arguments, 2, items, 0, items.Length);
  542. }
  543. if (len + insertCount - actualDeleteCount > ArrayOperations.MaxArrayLikeLength)
  544. {
  545. return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Invalid array length");
  546. }
  547. if (actualDeleteCount > ArrayOperations.MaxArrayLength)
  548. {
  549. return ExceptionHelper.ThrowRangeError<JsValue>(_engine, "Invalid array length");
  550. }
  551. var a = Engine.Array.ArraySpeciesCreate(TypeConverter.ToObject(_engine, thisObj), (uint) actualDeleteCount);
  552. for (uint k = 0; k < actualDeleteCount; k++)
  553. {
  554. if (o.TryGetValue(actualStart + k, out var fromValue))
  555. {
  556. a.SetIndexValue(k, fromValue, updateLength: false);
  557. }
  558. }
  559. a.SetLength((uint) actualDeleteCount);
  560. var length = len - actualDeleteCount + (uint) items.Length;
  561. o.EnsureCapacity(length);
  562. if ((ulong) items.Length < actualDeleteCount)
  563. {
  564. for (ulong k = actualStart; k < len - actualDeleteCount; k++)
  565. {
  566. var from = k + actualDeleteCount;
  567. var to = k + (ulong) items.Length;
  568. if (o.TryGetValue(from, out var fromValue))
  569. {
  570. o.Set(to, fromValue, true);
  571. }
  572. else
  573. {
  574. o.DeletePropertyOrThrow(to);
  575. }
  576. }
  577. for (var k = len; k > len - actualDeleteCount + (ulong) items.Length; k--)
  578. {
  579. o.DeletePropertyOrThrow(k - 1);
  580. }
  581. }
  582. else if ((ulong) items.Length > actualDeleteCount)
  583. {
  584. for (var k = len - actualDeleteCount; k > actualStart; k--)
  585. {
  586. var from = k + actualDeleteCount - 1;
  587. var to = k + (ulong) items.Length - 1;
  588. if (o.TryGetValue(from, out var fromValue))
  589. {
  590. o.Set(to, fromValue, true);
  591. }
  592. else
  593. {
  594. o.DeletePropertyOrThrow(to);
  595. }
  596. }
  597. }
  598. for (uint k = 0; k < items.Length; k++)
  599. {
  600. var e = items[k];
  601. o.Set(k + actualStart, e, true);
  602. }
  603. o.SetLength(length);
  604. return a;
  605. }
  606. private JsValue Unshift(JsValue thisObj, JsValue[] arguments)
  607. {
  608. var o = ArrayOperations.For(Engine, thisObj);
  609. var len = o.GetLongLength();
  610. var argCount = (uint) arguments.Length;
  611. if (len + argCount > ArrayOperations.MaxArrayLikeLength)
  612. {
  613. return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Invalid array length");
  614. }
  615. o.EnsureCapacity(len + argCount);
  616. var minIndex = o.GetSmallestIndex(len);
  617. for (var k = len; k > minIndex; k--)
  618. {
  619. var from = k - 1;
  620. var to = k + argCount - 1;
  621. if (o.TryGetValue(from, out var fromValue))
  622. {
  623. o.Set(to, fromValue, true);
  624. }
  625. else
  626. {
  627. o.DeletePropertyOrThrow(to);
  628. }
  629. }
  630. for (uint j = 0; j < argCount; j++)
  631. {
  632. o.Set(j, arguments[j], true);
  633. }
  634. o.SetLength(len + argCount);
  635. return len + argCount;
  636. }
  637. private JsValue Sort(JsValue thisObj, JsValue[] arguments)
  638. {
  639. if (!thisObj.IsObject())
  640. {
  641. ExceptionHelper.ThrowTypeError(_engine, "Array.prorotype.sort can only be applied on objects");
  642. }
  643. var obj = ArrayOperations.For(thisObj.AsObject());
  644. var compareArg = arguments.At(0);
  645. ICallable compareFn = null;
  646. if (!compareArg.IsUndefined())
  647. {
  648. if (compareArg.IsNull() || !(compareArg is ICallable))
  649. {
  650. ExceptionHelper.ThrowTypeError(_engine, "The comparison function must be either a function or undefined");
  651. }
  652. compareFn = (ICallable) compareArg;
  653. }
  654. var len = obj.GetLength();
  655. if (len <= 1)
  656. {
  657. return obj.Target;
  658. }
  659. int Comparer(JsValue x, JsValue y)
  660. {
  661. if (ReferenceEquals(x, null))
  662. {
  663. return 1;
  664. }
  665. if (ReferenceEquals(y, null))
  666. {
  667. return -1;
  668. }
  669. var xUndefined = x.IsUndefined();
  670. var yUndefined = y.IsUndefined();
  671. if (xUndefined && yUndefined)
  672. {
  673. return 0;
  674. }
  675. if (xUndefined)
  676. {
  677. return 1;
  678. }
  679. if (yUndefined)
  680. {
  681. return -1;
  682. }
  683. if (compareFn != null)
  684. {
  685. var s = TypeConverter.ToNumber(compareFn.Call(Undefined, new[] {x, y}));
  686. if (s < 0)
  687. {
  688. return -1;
  689. }
  690. if (s > 0)
  691. {
  692. return 1;
  693. }
  694. return 0;
  695. }
  696. var xString = TypeConverter.ToString(x);
  697. var yString = TypeConverter.ToString(y);
  698. var r = CompareOrdinal(xString, yString);
  699. return r;
  700. }
  701. var array = new JsValue[len];
  702. for (uint i = 0; i < (uint) array.Length; ++i)
  703. {
  704. var value = obj.TryGetValue(i, out var temp)
  705. ? temp
  706. : null;
  707. array[i] = value;
  708. }
  709. // don't eat inner exceptions
  710. try
  711. {
  712. System.Array.Sort(array, Comparer);
  713. }
  714. catch (InvalidOperationException e)
  715. {
  716. throw e.InnerException;
  717. }
  718. for (uint i = 0; i < (uint) array.Length; ++i)
  719. {
  720. if (!ReferenceEquals(array[i], null))
  721. {
  722. obj.Set(i, array[i], false);
  723. }
  724. else
  725. {
  726. obj.DeletePropertyOrThrow(i);
  727. }
  728. }
  729. return obj.Target;
  730. }
  731. internal JsValue Slice(JsValue thisObj, JsValue[] arguments)
  732. {
  733. var start = arguments.At(0);
  734. var end = arguments.At(1);
  735. var o = ArrayOperations.For(Engine, thisObj);
  736. var len = o.GetLongLength();
  737. var relativeStart = TypeConverter.ToInteger(start);
  738. ulong k;
  739. if (relativeStart < 0)
  740. {
  741. k = (ulong) System.Math.Max(len + relativeStart, 0);
  742. }
  743. else
  744. {
  745. k = (ulong) System.Math.Min(TypeConverter.ToInteger(start), len);
  746. }
  747. ulong final;
  748. if (end.IsUndefined())
  749. {
  750. final = (ulong) TypeConverter.ToNumber(len);
  751. }
  752. else
  753. {
  754. double relativeEnd = TypeConverter.ToInteger(end);
  755. if (relativeEnd < 0)
  756. {
  757. final = (ulong) System.Math.Max(len + relativeEnd, 0);
  758. }
  759. else
  760. {
  761. final = (ulong) System.Math.Min(TypeConverter.ToInteger(relativeEnd), len);
  762. }
  763. }
  764. if (k < final && final - k > ArrayOperations.MaxArrayLength)
  765. {
  766. ExceptionHelper.ThrowRangeError(_engine, "Invalid array length");;
  767. }
  768. var length = (uint) System.Math.Max(0, (long) final - (long) k);
  769. var a = Engine.Array.ArraySpeciesCreate(TypeConverter.ToObject(_engine, thisObj), length);
  770. if (thisObj is ArrayInstance ai)
  771. {
  772. a.CopyValues(ai, (uint) k, 0, length);
  773. }
  774. else
  775. {
  776. // slower path
  777. for (uint n = 0; k < final; k++, n++)
  778. {
  779. if (o.TryGetValue(k, out var kValue))
  780. {
  781. a.SetIndexValue(n, kValue, updateLength: false);
  782. }
  783. }
  784. }
  785. a.DefineOwnProperty("length", new PropertyDescriptor(length, PropertyFlag.None));
  786. return a;
  787. }
  788. private JsValue Shift(JsValue thisObj, JsValue[] arg2)
  789. {
  790. var o = ArrayOperations.For(Engine, thisObj);
  791. var len = o.GetLength();
  792. if (len == 0)
  793. {
  794. o.SetLength(0);
  795. return Undefined;
  796. }
  797. var first = o.Get(0);
  798. for (uint k = 1; k < len; k++)
  799. {
  800. var to = k - 1;
  801. if (o.TryGetValue(k, out var fromVal))
  802. {
  803. o.Set(to, fromVal, true);
  804. }
  805. else
  806. {
  807. o.DeletePropertyOrThrow(to);
  808. }
  809. }
  810. o.DeletePropertyOrThrow(len - 1);
  811. o.SetLength(len - 1);
  812. return first;
  813. }
  814. private JsValue Reverse(JsValue thisObj, JsValue[] arguments)
  815. {
  816. var o = ArrayOperations.For(Engine, thisObj);
  817. var len = o.GetLongLength();
  818. var middle = (ulong) System.Math.Floor(len / 2.0);
  819. uint lower = 0;
  820. while (lower != middle)
  821. {
  822. var upper = len - lower - 1;
  823. var lowerExists = o.HasProperty(lower);
  824. var lowerValue = lowerExists ? o.Get(lower) : null;
  825. var upperExists = o.HasProperty(upper);
  826. var upperValue = upperExists ? o.Get(upper) : null;
  827. if (lowerExists && upperExists)
  828. {
  829. o.Set(lower, upperValue, true);
  830. o.Set(upper, lowerValue, true);
  831. }
  832. if (!lowerExists && upperExists)
  833. {
  834. o.Set(lower, upperValue, true);
  835. o.DeletePropertyOrThrow(upper);
  836. }
  837. if (lowerExists && !upperExists)
  838. {
  839. o.DeletePropertyOrThrow(lower);
  840. o.Set(upper, lowerValue, true);
  841. }
  842. lower++;
  843. }
  844. return o.Target;
  845. }
  846. private JsValue Join(JsValue thisObj, JsValue[] arguments)
  847. {
  848. var separator = arguments.At(0);
  849. var o = ArrayOperations.For(Engine, thisObj);
  850. var len = o.GetLength();
  851. if (separator.IsUndefined())
  852. {
  853. separator = ",";
  854. }
  855. var sep = TypeConverter.ToString(separator);
  856. // as per the spec, this has to be called after ToString(separator)
  857. if (len == 0)
  858. {
  859. return "";
  860. }
  861. string StringFromJsValue(JsValue value)
  862. {
  863. return value.IsNullOrUndefined()
  864. ? ""
  865. : TypeConverter.ToString(value);
  866. }
  867. var s = StringFromJsValue(o.Get(0));
  868. if (len == 1)
  869. {
  870. return s;
  871. }
  872. using (var sb = StringBuilderPool.Rent())
  873. {
  874. sb.Builder.Append(s);
  875. for (uint k = 1; k < len; k++)
  876. {
  877. sb.Builder.Append(sep);
  878. sb.Builder.Append(StringFromJsValue(o.Get(k)));
  879. }
  880. return sb.ToString();
  881. }
  882. }
  883. private JsValue ToLocaleString(JsValue thisObj, JsValue[] arguments)
  884. {
  885. var array = ArrayOperations.For(Engine, thisObj);
  886. var len = array.GetLength();
  887. const string separator = ",";
  888. if (len == 0)
  889. {
  890. return "";
  891. }
  892. JsValue r;
  893. if (!array.TryGetValue(0, out var firstElement) || firstElement.IsNull() || firstElement.IsUndefined())
  894. {
  895. r = "";
  896. }
  897. else
  898. {
  899. var elementObj = TypeConverter.ToObject(Engine, firstElement);
  900. var func = elementObj.Get("toLocaleString", elementObj) as ICallable ?? ExceptionHelper.ThrowTypeError<ICallable>(_engine);
  901. r = func.Call(elementObj, Arguments.Empty);
  902. }
  903. for (uint k = 1; k < len; k++)
  904. {
  905. string s = r + separator;
  906. if (!array.TryGetValue(k, out var nextElement) || nextElement.IsNull())
  907. {
  908. r = "";
  909. }
  910. else
  911. {
  912. var elementObj = TypeConverter.ToObject(Engine, nextElement);
  913. var func = elementObj.Get("toLocaleString", elementObj) as ICallable ?? ExceptionHelper.ThrowTypeError<ICallable>(_engine);
  914. r = func.Call(elementObj, Arguments.Empty);
  915. }
  916. r = s + r;
  917. }
  918. return r;
  919. }
  920. private JsValue Concat(JsValue thisObj, JsValue[] arguments)
  921. {
  922. var o = TypeConverter.ToObject(Engine, thisObj);
  923. var items = new List<JsValue>(arguments.Length + 1) {o};
  924. items.AddRange(arguments);
  925. // try to find best capacity
  926. bool hasObjectSpreadables = false;
  927. uint capacity = 0;
  928. for (var i = 0; i < items.Count; i++)
  929. {
  930. uint increment;
  931. var objectInstance = items[i] as ObjectInstance;
  932. if (objectInstance == null)
  933. {
  934. increment = 1;
  935. }
  936. else
  937. {
  938. var isConcatSpreadable = objectInstance.IsConcatSpreadable;
  939. hasObjectSpreadables |= isConcatSpreadable;
  940. increment = isConcatSpreadable ? ArrayOperations.For(objectInstance).GetLength() : 1;
  941. }
  942. capacity += increment;
  943. }
  944. uint n = 0;
  945. var a = Engine.Array.ArraySpeciesCreate(TypeConverter.ToObject(_engine, thisObj), capacity);
  946. for (var i = 0; i < items.Count; i++)
  947. {
  948. var e = items[i];
  949. if (e is ArrayInstance eArray
  950. && eArray.IsConcatSpreadable)
  951. {
  952. a.CopyValues(eArray, 0, n, eArray.GetLength());
  953. n += eArray.GetLength();
  954. }
  955. else if (hasObjectSpreadables
  956. && e is ObjectInstance oi
  957. && oi.IsConcatSpreadable)
  958. {
  959. var operations = ArrayOperations.For(oi);
  960. var len = operations.GetLength();
  961. for (uint k = 0; k < len; k++)
  962. {
  963. operations.TryGetValue(k, out var subElement);
  964. a.SetIndexValue(n, subElement, updateLength: false);
  965. n++;
  966. }
  967. }
  968. else
  969. {
  970. a.SetIndexValue(n, e, updateLength: false);
  971. n++;
  972. }
  973. }
  974. // this is not in the specs, but is necessary in case the last element of the last
  975. // array doesn't exist, and thus the length would not be incremented
  976. a.DefineOwnProperty("length", new PropertyDescriptor(n, PropertyFlag.None));
  977. return a;
  978. }
  979. private JsValue ToString(JsValue thisObj, JsValue[] arguments)
  980. {
  981. var array = TypeConverter.ToObject(Engine, thisObj);
  982. ICallable func;
  983. func = array.Get("join", array).TryCast<ICallable>(x =>
  984. {
  985. func = Engine.Object.PrototypeObject.Get("toString", array).TryCast<ICallable>(y => ExceptionHelper.ThrowArgumentException());
  986. });
  987. if (array.IsArrayLike == false || func == null)
  988. return _engine.Object.PrototypeObject.ToObjectString(array, Arguments.Empty);
  989. return func.Call(array, Arguments.Empty);
  990. }
  991. private JsValue ReduceRight(JsValue thisObj, JsValue[] arguments)
  992. {
  993. var callbackfn = arguments.At(0);
  994. var initialValue = arguments.At(1);
  995. var o = ArrayOperations.For(TypeConverter.ToObject(_engine, thisObj));
  996. var len = o.GetLongLength();
  997. var callable = GetCallable(callbackfn);
  998. if (len == 0 && arguments.Length < 2)
  999. {
  1000. ExceptionHelper.ThrowTypeError(Engine);
  1001. }
  1002. long k = (long) (len - 1);
  1003. JsValue accumulator = Undefined;
  1004. if (arguments.Length > 1)
  1005. {
  1006. accumulator = initialValue;
  1007. }
  1008. else
  1009. {
  1010. var kPresent = false;
  1011. while (kPresent == false && k >= 0)
  1012. {
  1013. if ((kPresent = o.TryGetValue((ulong) k, out var temp)))
  1014. {
  1015. accumulator = temp;
  1016. }
  1017. k--;
  1018. }
  1019. if (kPresent == false)
  1020. {
  1021. ExceptionHelper.ThrowTypeError(Engine);
  1022. }
  1023. }
  1024. var jsValues = new JsValue[4];
  1025. jsValues[3] = o.Target;
  1026. for (; k >= 0; k--)
  1027. {
  1028. if (o.TryGetValue((ulong) k, out var kvalue))
  1029. {
  1030. jsValues[0] = accumulator;
  1031. jsValues[1] = kvalue;
  1032. jsValues[2] = k;
  1033. accumulator = callable.Call(Undefined, jsValues);
  1034. }
  1035. }
  1036. return accumulator;
  1037. }
  1038. public JsValue Push(JsValue thisObject, JsValue[] arguments)
  1039. {
  1040. if (thisObject is ArrayInstance arrayInstance)
  1041. {
  1042. return arrayInstance.Push(arguments);
  1043. }
  1044. var o = ArrayOperations.For(thisObject as ObjectInstance);
  1045. var n = o.GetLongLength();
  1046. if (n + (ulong) arguments.Length > ArrayOperations.MaxArrayLikeLength)
  1047. {
  1048. return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Invalid array length");
  1049. }
  1050. // cast to double as we need to prevent an overflow
  1051. foreach (var a in arguments)
  1052. {
  1053. o.Set(n, a, true);
  1054. n++;
  1055. }
  1056. o.SetLength(n);
  1057. return n;
  1058. }
  1059. public JsValue Pop(JsValue thisObject, JsValue[] arguments)
  1060. {
  1061. var o = ArrayOperations.For(Engine, thisObject);
  1062. ulong len = o.GetLongLength();
  1063. if (len == 0)
  1064. {
  1065. o.SetLength(0);
  1066. return Undefined;
  1067. }
  1068. len = len - 1;
  1069. JsValue element = o.Get(len);
  1070. o.DeletePropertyOrThrow(len);
  1071. o.SetLength(len);
  1072. return element;
  1073. }
  1074. }
  1075. }