2
0

ArrayPrototype.cs 45 KB

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