ArrayPrototype.cs 33 KB

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