ArrayPrototype.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Jint.Native.Object;
  5. using Jint.Runtime;
  6. using Jint.Runtime.Descriptors;
  7. using Jint.Runtime.Interop;
  8. namespace Jint.Native.Array
  9. {
  10. /// <summary>
  11. /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4
  12. /// </summary>
  13. public sealed class ArrayPrototype : ArrayInstance
  14. {
  15. private ArrayPrototype(Engine engine) : base(engine)
  16. {
  17. }
  18. public static ArrayPrototype CreatePrototypeObject(Engine engine, ArrayConstructor arrayConstructor)
  19. {
  20. var obj = new ArrayPrototype(engine)
  21. {
  22. Extensible = true,
  23. Prototype = engine.Object.PrototypeObject
  24. };
  25. obj.FastAddProperty("length", 0, true, false, false);
  26. obj.FastAddProperty("constructor", arrayConstructor, false, false, false);
  27. return obj;
  28. }
  29. public void Configure()
  30. {
  31. FastAddProperty("toString", new ClrFunctionInstance<object, object>(Engine, ToString, 0), true, false, true);
  32. FastAddProperty("toLocaleString", new ClrFunctionInstance<object, object>(Engine, ToLocaleString), true, false, true);
  33. FastAddProperty("concat", new ClrFunctionInstance<object, object>(Engine, Concat, 1), true, false, true);
  34. FastAddProperty("join", new ClrFunctionInstance<object, object>(Engine, Join, 1), true, false, true);
  35. FastAddProperty("pop", new ClrFunctionInstance<object, object>(Engine, Pop), true, false, true);
  36. FastAddProperty("push", new ClrFunctionInstance<object, object>(Engine, Push, 1), true, false, true);
  37. FastAddProperty("reverse", new ClrFunctionInstance<object, object>(Engine, Reverse), true, false, true);
  38. FastAddProperty("shift", new ClrFunctionInstance<object, object>(Engine, Shift), true, false, true);
  39. FastAddProperty("slice", new ClrFunctionInstance<object, object>(Engine, Slice, 2), true, false, true);
  40. FastAddProperty("sort", new ClrFunctionInstance<object, ObjectInstance>(Engine, Sort, 1), true, false, true);
  41. FastAddProperty("splice", new ClrFunctionInstance<object, ObjectInstance>(Engine, Splice, 2), true, false, true);
  42. FastAddProperty("unshift", new ClrFunctionInstance<object, uint>(Engine, Unshift, 1), true, false, true);
  43. FastAddProperty("indexOf", new ClrFunctionInstance<object, int>(Engine, IndexOf, 1), true, false, true);
  44. FastAddProperty("lastIndexOf", new ClrFunctionInstance<object, int>(Engine, LastIndexOf, 1), true, false, true);
  45. FastAddProperty("every", new ClrFunctionInstance<object, bool>(Engine, Every, 1), true, false, true);
  46. FastAddProperty("some", new ClrFunctionInstance<object, bool>(Engine, Some, 1), true, false, true);
  47. FastAddProperty("forEach", new ClrFunctionInstance<object, object>(Engine, ForEach, 1), true, false, true);
  48. FastAddProperty("map", new ClrFunctionInstance<object, ArrayInstance>(Engine, Map, 1), true, false, true);
  49. FastAddProperty("filter", new ClrFunctionInstance<object, ArrayInstance>(Engine, Filter, 1), true, false, true);
  50. FastAddProperty("reduce", new ClrFunctionInstance<object, object>(Engine, Reduce, 1), true, false, true);
  51. FastAddProperty("reduceRight", new ClrFunctionInstance<object, object>(Engine, ReduceRight, 1), true, false, true);
  52. }
  53. private int LastIndexOf(object thisObj, object[] arguments)
  54. {
  55. var o = TypeConverter.ToObject(Engine, thisObj);
  56. var lenValue = o.Get("length");
  57. var len = TypeConverter.ToUint32(lenValue);
  58. if (len == 0)
  59. {
  60. return -1;
  61. }
  62. var n = arguments.Length > 1 ? (int)TypeConverter.ToInteger(arguments[1]) : (int)len - 1;
  63. int k;
  64. if (n >= 0)
  65. {
  66. k = System.Math.Min(n, (int)len - 1); // min
  67. }
  68. else
  69. {
  70. k = (int)len - System.Math.Abs(n);
  71. }
  72. var searchElement = arguments.Length > 0 ? arguments[0] : Undefined.Instance;
  73. for (; k > 0; k--)
  74. {
  75. var kString = TypeConverter.ToString(k);
  76. var kPresent = o.HasProperty(kString);
  77. if (kPresent)
  78. {
  79. var elementK = o.Get(kString);
  80. var same = ExpressionInterpreter.StriclyEqual(elementK, searchElement);
  81. if (same)
  82. {
  83. return k;
  84. }
  85. }
  86. }
  87. return -1;
  88. }
  89. private object Reduce(object thisObj, object[] arguments)
  90. {
  91. var callbackfn = arguments.Length > 0 ? arguments[0] : Undefined.Instance;
  92. var initialValue = arguments.Length > 1 ? arguments[1] : Undefined.Instance;
  93. var o = TypeConverter.ToObject(Engine, thisObj);
  94. var lenValue = o.Get("length");
  95. var len = TypeConverter.ToUint32(lenValue);
  96. var callable = callbackfn as ICallable;
  97. if (callable == null)
  98. {
  99. throw new JavaScriptException(Engine.TypeError, "Argument must be callable");
  100. }
  101. if (len == 0 && arguments.Length < 2)
  102. {
  103. throw new JavaScriptException(Engine.TypeError);
  104. }
  105. var k = 0;
  106. object accumulator = Undefined.Instance;
  107. if (arguments.Length > 2)
  108. {
  109. accumulator = initialValue;
  110. }
  111. else
  112. {
  113. var kPresent = false;
  114. while (kPresent == false && k < len)
  115. {
  116. var pk = k.ToString();
  117. kPresent = o.HasProperty(pk);
  118. if (kPresent)
  119. {
  120. accumulator = o.Get(pk);
  121. }
  122. k++;
  123. }
  124. if (kPresent == false)
  125. {
  126. throw new JavaScriptException(Engine.TypeError);
  127. }
  128. }
  129. while(k < len)
  130. {
  131. var pk = k.ToString();
  132. var kPresent = o.HasProperty(pk);
  133. if (kPresent)
  134. {
  135. var kvalue = o.Get(pk);
  136. accumulator = callable.Call(Undefined.Instance, new object[] { accumulator, kvalue, k, o });
  137. }
  138. k++;
  139. }
  140. return accumulator;
  141. }
  142. private ArrayInstance Filter(object thisObj, object[] arguments)
  143. {
  144. var callbackfn = arguments.Length > 0 ? arguments[0] : Undefined.Instance;
  145. var thisArg = arguments.Length > 1 ? arguments[1] : Undefined.Instance;
  146. var o = TypeConverter.ToObject(Engine, thisObj);
  147. var lenValue = o.Get("length");
  148. var len = TypeConverter.ToUint32(lenValue);
  149. var callable = callbackfn as ICallable;
  150. if (callable == null)
  151. {
  152. throw new JavaScriptException(Engine.TypeError, "Argument must be callable");
  153. }
  154. var a = (ArrayInstance)Engine.Array.Construct(Arguments.Empty);
  155. var to = 0;
  156. for (var k = 0; k < len; k++)
  157. {
  158. var pk = k.ToString();
  159. var kpresent = o.HasProperty(pk);
  160. if (kpresent)
  161. {
  162. var kvalue = o.Get(pk);
  163. var selected = callable.Call(thisArg, new object[] { kvalue, k, o });
  164. if (TypeConverter.ToBoolean(selected) == true)
  165. {
  166. a.DefineOwnProperty(to.ToString(), new DataDescriptor(kvalue) { Writable = true, Enumerable = true, Configurable = true }, false);
  167. to++;
  168. }
  169. }
  170. }
  171. return a;
  172. }
  173. private ArrayInstance Map(object thisObj, object[] arguments)
  174. {
  175. var callbackfn = arguments.Length > 0 ? arguments[0] : Undefined.Instance;
  176. var thisArg = arguments.Length > 1 ? arguments[1] : Undefined.Instance;
  177. var o = TypeConverter.ToObject(Engine, thisObj);
  178. var lenValue = o.Get("length");
  179. var len = TypeConverter.ToUint32(lenValue);
  180. var callable = callbackfn as ICallable;
  181. if (callable == null)
  182. {
  183. throw new JavaScriptException(Engine.TypeError, "Argument must be callable");
  184. }
  185. var a = (ArrayInstance)Engine.Array.Construct(Arguments.Empty);
  186. for (var k = 0; k < len; k++)
  187. {
  188. var pk = k.ToString();
  189. var kpresent = o.HasProperty(pk);
  190. if (kpresent)
  191. {
  192. var kvalue = o.Get(pk);
  193. var mappedValue = callable.Call(thisArg, new object[] { kvalue, k, o });
  194. a.DefineOwnProperty(pk, new DataDescriptor(mappedValue) { Writable = true, Enumerable = true, Configurable = true }, false);
  195. }
  196. }
  197. return a;
  198. }
  199. private object ForEach(object thisObj, object[] arguments)
  200. {
  201. var callbackfn = arguments.Length > 0 ? arguments[0] : Undefined.Instance;
  202. var thisArg = arguments.Length > 1 ? arguments[1] : Undefined.Instance;
  203. var o = TypeConverter.ToObject(Engine, thisObj);
  204. var lenValue = o.Get("length");
  205. var len = TypeConverter.ToUint32(lenValue);
  206. var callable = callbackfn as ICallable;
  207. if (callable == null)
  208. {
  209. throw new JavaScriptException(Engine.TypeError, "Argument must be callable");
  210. }
  211. for (var k = 0; k < len; k++)
  212. {
  213. var pk = k.ToString();
  214. var kpresent = o.HasProperty(pk);
  215. if (kpresent)
  216. {
  217. var kvalue = o.Get(pk);
  218. callable.Call(thisArg, new object[] { kvalue, k, o });
  219. }
  220. }
  221. return Undefined.Instance;
  222. }
  223. private bool Some(object thisObj, object[] arguments)
  224. {
  225. var callbackfn = arguments.Length > 0 ? arguments[0] : Undefined.Instance;
  226. var thisArg = arguments.Length > 1 ? arguments[1] : Undefined.Instance;
  227. var o = TypeConverter.ToObject(Engine, thisObj);
  228. var lenValue = o.Get("length");
  229. var len = TypeConverter.ToUint32(lenValue);
  230. var callable = callbackfn as ICallable;
  231. if (callable == null)
  232. {
  233. throw new JavaScriptException(Engine.TypeError, "Argument must be callable");
  234. }
  235. for (var k = 0; k < len; k++)
  236. {
  237. var pk = k.ToString();
  238. var kpresent = o.HasProperty(pk);
  239. if (kpresent)
  240. {
  241. var kvalue = o.Get(pk);
  242. var testResult = callable.Call(thisArg, new object[] { kvalue, k, o });
  243. if (TypeConverter.ToBoolean(testResult) == true)
  244. {
  245. return true;
  246. }
  247. }
  248. }
  249. return false;
  250. }
  251. private bool Every(object thisObj, object[] arguments)
  252. {
  253. var callbackfn = arguments.Length > 0 ? arguments[0] : Undefined.Instance;
  254. var thisArg = arguments.Length > 1 ? arguments[1] : Undefined.Instance;
  255. var o = TypeConverter.ToObject(Engine, thisObj);
  256. var lenValue = o.Get("length");
  257. var len = TypeConverter.ToUint32(lenValue);
  258. var callable = callbackfn as ICallable;
  259. if (callable == null)
  260. {
  261. throw new JavaScriptException(Engine.TypeError, "Argument must be callable");
  262. }
  263. for (var k = 0; k < len; k++)
  264. {
  265. var pk = k.ToString();
  266. var kpresent = o.HasProperty(pk);
  267. if (kpresent)
  268. {
  269. var kvalue = o.Get(pk);
  270. var testResult = callable.Call(thisArg, new object[] { kvalue, k, o });
  271. if (!TypeConverter.ToBoolean(testResult))
  272. {
  273. return false;
  274. }
  275. }
  276. }
  277. return true;
  278. }
  279. private int IndexOf(object thisObj, object[] arguments)
  280. {
  281. var o = TypeConverter.ToObject(Engine, thisObj);
  282. var lenValue = o.Get("length");
  283. var len = TypeConverter.ToUint32(lenValue);
  284. if (len == 0)
  285. {
  286. return -1;
  287. }
  288. var n = arguments.Length > 1 ? (int)TypeConverter.ToInteger(arguments[1]) : 0;
  289. if (n >= len)
  290. {
  291. return -1;
  292. }
  293. int k;
  294. if (n >= 0)
  295. {
  296. k = n;
  297. }
  298. else
  299. {
  300. k = (int)len - System.Math.Abs(n);
  301. if (k < 0)
  302. {
  303. k = 0;
  304. }
  305. }
  306. var searchElement = arguments.Length > 0 ? arguments[0] : Undefined.Instance;
  307. for (; k < len; k++)
  308. {
  309. var kString = TypeConverter.ToString(k);
  310. var kPresent = o.HasProperty(kString);
  311. if (kPresent)
  312. {
  313. var elementK = o.Get(kString);
  314. var same = ExpressionInterpreter.StriclyEqual(elementK, searchElement);
  315. if (same)
  316. {
  317. return k;
  318. }
  319. }
  320. }
  321. return -1;
  322. }
  323. private ObjectInstance Splice(object thisObj, object[] arguments)
  324. {
  325. var start = arguments.Length > 0 ? arguments[0] : Undefined.Instance;
  326. var deleteCount = arguments.Length > 1 ? arguments[1] : Undefined.Instance;
  327. var o = TypeConverter.ToObject(Engine, thisObj);
  328. var a = Engine.Array.Construct(Arguments.Empty);
  329. var lenVal = o.Get("length");
  330. var len = TypeConverter.ToUint32(lenVal);
  331. var relativeStart = TypeConverter.ToInteger(start);
  332. uint actualStart;
  333. if (relativeStart < 0)
  334. {
  335. actualStart = (uint)System.Math.Max(len + relativeStart, 0);
  336. }
  337. else
  338. {
  339. actualStart = (uint)System.Math.Min(relativeStart, len);
  340. }
  341. var actualDeleteCount = System.Math.Min(System.Math.Max(TypeConverter.ToInteger(deleteCount), 0), len - actualStart);
  342. for (var k = 0; k < actualDeleteCount; k++)
  343. {
  344. var from = (actualStart + k).ToString();
  345. var fromPresent = o.HasProperty(from);
  346. if (fromPresent)
  347. {
  348. var fromValue = o.Get(from);
  349. a.DefineOwnProperty(k.ToString(), new DataDescriptor(fromValue) { Writable = true, Enumerable = true, Configurable = true }, false);
  350. }
  351. }
  352. var items = arguments.Skip(2).ToArray();
  353. if (items.Length < actualDeleteCount)
  354. {
  355. for (var k = actualStart; k < len - actualDeleteCount; k++)
  356. {
  357. var from = (k + actualDeleteCount).ToString();
  358. var to = (k + items.Length).ToString();
  359. var fromPresent = o.HasProperty(from);
  360. if (fromPresent)
  361. {
  362. var fromValue = o.Get(from);
  363. o.Put(to, fromValue, true);
  364. }
  365. else
  366. {
  367. o.Delete(to, true);
  368. }
  369. }
  370. for (var k = len; k > len - actualDeleteCount + items.Length; k-- )
  371. {
  372. o.Delete((k - 1).ToString(), true);
  373. }
  374. }
  375. else if (items.Length > actualDeleteCount)
  376. {
  377. for (var k = len - actualDeleteCount; k > actualStart; k--)
  378. {
  379. var from = (k + actualDeleteCount - 1).ToString();
  380. var to = (k + items.Length - 1).ToString();
  381. var fromPresent = o.HasProperty(from);
  382. if (fromPresent)
  383. {
  384. var fromValue = o.Get(from);
  385. o.Put(to, fromValue, true);
  386. }
  387. else
  388. {
  389. o.Delete(to, true);
  390. }
  391. }
  392. }
  393. for(var k = 0; k< items.Length; k++)
  394. {
  395. var e = items[k];
  396. o.Put((k+actualStart).ToString(), e, true);
  397. }
  398. o.Put("length", len - actualDeleteCount + items.Length, true);
  399. return a;
  400. }
  401. private uint Unshift(object thisObj, object[] arguments)
  402. {
  403. var o = TypeConverter.ToObject(Engine, thisObj);
  404. var lenVal = o.Get("length");
  405. var len = TypeConverter.ToUint32(lenVal);
  406. var argCount = (uint)arguments.Length;
  407. for (var k = len; k > 0; k--)
  408. {
  409. var from = (k - 1).ToString();
  410. var to = (k + argCount - 1).ToString();
  411. var fromPresent = o.HasProperty(from);
  412. if (fromPresent)
  413. {
  414. var fromValue = o.Get(from);
  415. o.Put(to, fromValue, true);
  416. }
  417. else
  418. {
  419. o.Delete(to, true);
  420. }
  421. }
  422. for (var j = 0; j < argCount; j++)
  423. {
  424. o.Put(j.ToString(), arguments[j], true);
  425. }
  426. o.Put("length", len + argCount, true);
  427. return len + argCount;
  428. }
  429. private ObjectInstance Sort(object thisObj, object[] arguments)
  430. {
  431. var obj = thisObj as ObjectInstance;
  432. if(obj == null)
  433. {
  434. throw new JavaScriptException(Engine.TypeError, "Array.prorotype.sort can only be applied on objects");
  435. }
  436. var len = obj.Get("length");
  437. var lenVal = TypeConverter.ToInt32(len);
  438. if (lenVal <= 1)
  439. {
  440. return obj;
  441. }
  442. var compareArg = arguments.Length > 0 ? arguments[0] : Undefined.Instance;
  443. if (compareArg != Undefined.Instance && !(compareArg is ICallable))
  444. {
  445. throw new JavaScriptException(Engine.TypeError, "The sort argument must be a function");
  446. }
  447. var compareFn = compareArg as ICallable;
  448. Comparison<object> comparer = (x, y) =>
  449. {
  450. if (x == Undefined.Instance && y == Undefined.Instance)
  451. {
  452. return 0;
  453. }
  454. if (x == Undefined.Instance)
  455. {
  456. return 1;
  457. }
  458. if (y == Undefined.Instance)
  459. {
  460. return -1;
  461. }
  462. if (compareFn != null)
  463. {
  464. var s = (int) TypeConverter.ToUint32(compareFn.Call(Undefined.Instance, new[] {x, y}));
  465. return s;
  466. }
  467. var xString = TypeConverter.ToString(x);
  468. var yString = TypeConverter.ToString(y);
  469. var r = System.String.CompareOrdinal(xString, yString);
  470. return r;
  471. };
  472. var array = Enumerable.Range(0, lenVal).Select(i => obj.Get(i.ToString())).ToArray();
  473. // don't eat inner exceptions
  474. try
  475. {
  476. System.Array.Sort(array, comparer);
  477. }
  478. catch (InvalidOperationException e)
  479. {
  480. throw e.InnerException;
  481. }
  482. foreach (var i in Enumerable.Range(0, lenVal))
  483. {
  484. obj.Put(i.ToString(), array[i], false);
  485. }
  486. return obj;
  487. }
  488. private object Slice(object thisObj, object[] arguments)
  489. {
  490. var start = arguments.Length > 0 ? arguments[0] : Undefined.Instance;
  491. var end = arguments.Length > 1 ? arguments[1] : Undefined.Instance;
  492. var o = TypeConverter.ToObject(Engine, thisObj);
  493. var a = Engine.Array.Construct(Arguments.Empty);
  494. var lenVal = o.Get("length");
  495. var len = TypeConverter.ToUint32(lenVal);
  496. var relativeStart = TypeConverter.ToInteger(start);
  497. uint k;
  498. if (relativeStart < 0)
  499. {
  500. k = (uint)System.Math.Max(len + relativeStart, 0);
  501. }
  502. else
  503. {
  504. k = (uint)System.Math.Min(TypeConverter.ToInteger(start), len);
  505. }
  506. uint final;
  507. if (end == Undefined.Instance)
  508. {
  509. final = TypeConverter.ToUint32(len);
  510. }
  511. else
  512. {
  513. double relativeEnd = TypeConverter.ToInteger(end);
  514. if (relativeEnd < 0)
  515. {
  516. final = (uint)System.Math.Max(len + relativeEnd, 0);
  517. }
  518. else
  519. {
  520. final = (uint)System.Math.Min(TypeConverter.ToInteger(relativeEnd), len);
  521. }
  522. }
  523. var n = 0;
  524. for (; k < final; k++)
  525. {
  526. var pk = TypeConverter.ToString(k);
  527. var kPresent = o.HasProperty(pk);
  528. if (kPresent)
  529. {
  530. var kValue = o.Get(pk);
  531. a.DefineOwnProperty(TypeConverter.ToString(n),
  532. new DataDescriptor(kValue)
  533. {
  534. Writable = true,
  535. Enumerable = true,
  536. Configurable = true
  537. }, false);
  538. }
  539. n++;
  540. }
  541. return a;
  542. }
  543. private object Shift(object thisObj, object[] arg2)
  544. {
  545. var o = TypeConverter.ToObject(Engine, thisObj);
  546. var lenVal = o.Get("length");
  547. var len = TypeConverter.ToUint32(lenVal);
  548. if (len == 0)
  549. {
  550. o.Put("length", 0, true);
  551. return Undefined.Instance;
  552. }
  553. var first = o.Get("0");
  554. for (var k = 1; k < len; k++)
  555. {
  556. var from = TypeConverter.ToString(k);
  557. var to = TypeConverter.ToString(k - 1);
  558. var fromPresent = o.HasProperty(from);
  559. if (fromPresent)
  560. {
  561. var fromVal = o.Get(from);
  562. o.Put(to, fromVal, true);
  563. }
  564. else
  565. {
  566. o.Delete(to, true);
  567. }
  568. }
  569. o.Delete(TypeConverter.ToString(len - 1), true);
  570. o.Put("length", len-1, true);
  571. return first;
  572. }
  573. private object Reverse(object thisObj, object[] arguments)
  574. {
  575. var o = TypeConverter.ToObject(Engine, thisObj);
  576. var lenVal = o.Get("length");
  577. var len = TypeConverter.ToUint32(lenVal);
  578. var middle = (uint)System.Math.Floor(len/2);
  579. uint lower = 0;
  580. while (lower != middle)
  581. {
  582. var upper = len - lower - 1;
  583. var upperP = TypeConverter.ToString(upper);
  584. var lowerP = TypeConverter.ToString(lower);
  585. var lowerValue = o.Get(lowerP);
  586. var upperValue = o.Get(upperP);
  587. var lowerExists = o.HasProperty(lowerP);
  588. var upperExists = o.HasProperty(upperP);
  589. if (lowerExists && upperExists)
  590. {
  591. o.Put(lowerP, upperValue, true);
  592. o.Put(upperP, lowerValue, true);
  593. }
  594. if (!lowerExists && upperExists)
  595. {
  596. o.Put(lowerP, upperValue, true);
  597. o.Delete(upperP, true);
  598. }
  599. if (lowerExists && !upperExists)
  600. {
  601. o.Delete(lowerP, true);
  602. o.Put(upperP, lowerValue, true);
  603. }
  604. lower++;
  605. }
  606. return o;
  607. }
  608. private object Join(object thisObj, object[] arguments)
  609. {
  610. var separator = arguments.Length > 0 ? arguments[0] : Undefined.Instance;
  611. var o = TypeConverter.ToObject(Engine, thisObj);
  612. var lenVal = o.Get("length");
  613. var len = TypeConverter.ToUint32(lenVal);
  614. if (separator == Undefined.Instance)
  615. {
  616. separator = ",";
  617. }
  618. var sep = TypeConverter.ToString(separator);
  619. // as per the spec, this has to be called after ToString(separator)
  620. if (len == 0)
  621. {
  622. return "";
  623. }
  624. var element0 = o.Get("0");
  625. string r = element0 == Undefined.Instance || element0 == Null.Instance
  626. ? ""
  627. : TypeConverter.ToString(element0);
  628. for (var k = 1; k < len; k++)
  629. {
  630. var s = r + sep;
  631. var element = o.Get(k.ToString());
  632. string next = element == Undefined.Instance || element == Null.Instance
  633. ? ""
  634. : TypeConverter.ToString(element);
  635. r = s + next;
  636. }
  637. return r;
  638. }
  639. private object ToLocaleString(object thisObj, object[] arguments)
  640. {
  641. var array = TypeConverter.ToObject(Engine, thisObj);
  642. var arrayLen = array.Get("length");
  643. var len = TypeConverter.ToUint32(arrayLen);
  644. var separator = ",";
  645. if (len == 0)
  646. {
  647. return "";
  648. }
  649. object r;
  650. var firstElement = array.Get("0");
  651. if (firstElement == Null.Instance || firstElement == Undefined.Instance)
  652. {
  653. r = "";
  654. }
  655. else
  656. {
  657. var elementObj = TypeConverter.ToObject(Engine, firstElement);
  658. var func = elementObj.Get("toLocaleString") as ICallable;
  659. if (func == null)
  660. {
  661. throw new JavaScriptException(Engine.TypeError);
  662. }
  663. r = func.Call(elementObj, Arguments.Empty);
  664. }
  665. for (var k = 1; k < len; k++)
  666. {
  667. string s = r + separator;
  668. var nextElement = array.Get(k.ToString());
  669. if (nextElement == Undefined.Instance || nextElement == Null.Instance)
  670. {
  671. r = "";
  672. }
  673. else
  674. {
  675. var elementObj = TypeConverter.ToObject(Engine, nextElement);
  676. var func = elementObj.Get("toLocaleString") as ICallable;
  677. if (func == null)
  678. {
  679. throw new JavaScriptException(Engine.TypeError);
  680. }
  681. r = func.Call(elementObj, Arguments.Empty);
  682. }
  683. r = s + r;
  684. }
  685. return r;
  686. }
  687. private object Concat(object thisObj, object[] arguments)
  688. {
  689. var o = TypeConverter.ToObject(Engine, thisObj);
  690. var a = Engine.Array.Construct(Arguments.Empty);
  691. var n = 0;
  692. var items = new List<object> {o};
  693. items.AddRange(arguments);
  694. foreach (var e in items)
  695. {
  696. var eArray = e as ArrayInstance;
  697. if (eArray != null)
  698. {
  699. var len = TypeConverter.ToUint32(eArray.Get("length"));
  700. for (var k = 0; k < len; k++)
  701. {
  702. var p = k.ToString();
  703. var exists = eArray.HasProperty(p);
  704. if (exists)
  705. {
  706. var subElement = eArray.Get(p);
  707. a.DefineOwnProperty(TypeConverter.ToString(n), new DataDescriptor(subElement) { Writable = true, Enumerable = true, Configurable = true}, false);
  708. }
  709. n++;
  710. }
  711. }
  712. else
  713. {
  714. a.DefineOwnProperty(TypeConverter.ToString(n), new DataDescriptor(e) { Writable = true, Enumerable = true, Configurable = true }, false);
  715. n++;
  716. }
  717. }
  718. return a;
  719. }
  720. private object ToString(object thisObj, object[] arguments)
  721. {
  722. var array = TypeConverter.ToObject(Engine, thisObj);
  723. var func = array.Get("join") as ICallable;
  724. if (func == null)
  725. {
  726. func = Engine.Object.PrototypeObject.Get("toString") as ICallable;
  727. if (func == null)
  728. {
  729. throw new ArgumentException();
  730. }
  731. }
  732. return func.Call(array, Arguments.Empty);
  733. }
  734. private object ReduceRight(object thisObj, object[] arguments)
  735. {
  736. var callbackfn = arguments.Length > 0 ? arguments[0] : Undefined.Instance;
  737. var initialValue = arguments.Length > 1 ? arguments[1] : Undefined.Instance;
  738. var o = TypeConverter.ToObject(Engine, thisObj);
  739. var lenValue = o.Get("length");
  740. var len = TypeConverter.ToUint32(lenValue);
  741. var callable = callbackfn as ICallable;
  742. if (callable == null)
  743. {
  744. throw new JavaScriptException(Engine.TypeError, "Argument must be callable");
  745. }
  746. if (len == 0 && arguments.Length < 2)
  747. {
  748. throw new JavaScriptException(Engine.TypeError);
  749. }
  750. var k = len - 1;
  751. object accumulator = Undefined.Instance;
  752. if (arguments.Length > 2)
  753. {
  754. accumulator = initialValue;
  755. }
  756. else
  757. {
  758. var kPresent = false;
  759. while (kPresent == false && k >= 0)
  760. {
  761. var pk = k.ToString();
  762. kPresent = o.HasProperty(pk);
  763. if (kPresent)
  764. {
  765. accumulator = o.Get(pk);
  766. }
  767. k--;
  768. }
  769. if (kPresent == false)
  770. {
  771. throw new JavaScriptException(Engine.TypeError);
  772. }
  773. }
  774. for (; k >= 0; k--)
  775. {
  776. var pk = k.ToString();
  777. var kPresent = o.HasProperty(pk);
  778. if (kPresent)
  779. {
  780. var kvalue = o.Get(pk);
  781. accumulator = callable.Call(Undefined.Instance, new object[] { accumulator, kvalue, k, o });
  782. }
  783. }
  784. return accumulator;
  785. }
  786. public object Push(object thisObject, object[] arguments)
  787. {
  788. ObjectInstance o = TypeConverter.ToObject(Engine, thisObject);
  789. object lenVal = o.Get("length");
  790. // cast to double as we need to prevent an overflow
  791. double n = TypeConverter.ToUint32(lenVal);
  792. foreach (object e in arguments)
  793. {
  794. o.Put(TypeConverter.ToString(n), e, true);
  795. n++;
  796. }
  797. o.Put("length", n, true);
  798. return n;
  799. }
  800. public object Pop(object thisObject, object[] arguments)
  801. {
  802. ObjectInstance o = TypeConverter.ToObject(Engine, thisObject);
  803. object lenVal = o.Get("length");
  804. uint len = TypeConverter.ToUint32(lenVal);
  805. if (len == 0)
  806. {
  807. o.Put("length", 0, true);
  808. return Undefined.Instance;
  809. }
  810. else
  811. {
  812. len = len - 1;
  813. string indx = TypeConverter.ToString(len);
  814. object element = o.Get(indx);
  815. o.Delete(indx, true);
  816. o.Put("length", len, true);
  817. return element;
  818. }
  819. }
  820. }
  821. }