jsarray.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  1. #include "jsi.h"
  2. int js_getlength(js_State *J, int idx)
  3. {
  4. int len;
  5. js_getproperty(J, idx, "length");
  6. len = js_tointeger(J, -1);
  7. js_pop(J, 1);
  8. return len;
  9. }
  10. void js_setlength(js_State *J, int idx, int len)
  11. {
  12. js_pushnumber(J, len);
  13. js_setproperty(J, idx < 0 ? idx - 1 : idx, "length");
  14. }
  15. static void jsB_new_Array(js_State *J)
  16. {
  17. int i, top = js_gettop(J);
  18. js_newarray(J);
  19. if (top == 2) {
  20. if (js_isnumber(J, 1)) {
  21. js_copy(J, 1);
  22. js_setproperty(J, -2, "length");
  23. } else {
  24. js_copy(J, 1);
  25. js_setindex(J, -2, 0);
  26. }
  27. } else {
  28. for (i = 1; i < top; ++i) {
  29. js_copy(J, i);
  30. js_setindex(J, -2, i - 1);
  31. }
  32. }
  33. }
  34. static void Ap_concat(js_State *J)
  35. {
  36. int i, top = js_gettop(J);
  37. int n, k, len;
  38. js_newarray(J);
  39. n = 0;
  40. for (i = 0; i < top; ++i) {
  41. js_copy(J, i);
  42. if (js_isarray(J, -1)) {
  43. len = js_getlength(J, -1);
  44. for (k = 0; k < len; ++k)
  45. if (js_hasindex(J, -1, k))
  46. js_setindex(J, -3, n++);
  47. js_pop(J, 1);
  48. } else {
  49. js_setindex(J, -2, n++);
  50. }
  51. }
  52. }
  53. static void Ap_join(js_State *J)
  54. {
  55. char * volatile out = NULL;
  56. const char * volatile r = NULL;
  57. const char *sep;
  58. int seplen;
  59. int k, n, len, rlen;
  60. len = js_getlength(J, 0);
  61. if (js_isdefined(J, 1)) {
  62. sep = js_tostring(J, 1);
  63. seplen = strlen(sep);
  64. } else {
  65. sep = ",";
  66. seplen = 1;
  67. }
  68. if (len <= 0) {
  69. js_pushliteral(J, "");
  70. return;
  71. }
  72. if (js_try(J)) {
  73. js_free(J, out);
  74. js_throw(J);
  75. }
  76. n = 0;
  77. for (k = 0; k < len; ++k) {
  78. js_getindex(J, 0, k);
  79. if (js_iscoercible(J, -1)) {
  80. r = js_tostring(J, -1);
  81. rlen = strlen(r);
  82. } else {
  83. rlen = 0;
  84. }
  85. if (k == 0) {
  86. out = js_malloc(J, rlen + 1);
  87. if (rlen > 0) {
  88. memcpy(out, r, rlen);
  89. n += rlen;
  90. }
  91. } else {
  92. if (n + seplen + rlen > JS_STRLIMIT)
  93. js_rangeerror(J, "invalid string length");
  94. out = js_realloc(J, out, n + seplen + rlen + 1);
  95. if (seplen > 0) {
  96. memcpy(out + n, sep, seplen);
  97. n += seplen;
  98. }
  99. if (rlen > 0) {
  100. memcpy(out + n, r, rlen);
  101. n += rlen;
  102. }
  103. }
  104. js_pop(J, 1);
  105. }
  106. js_pushlstring(J, out, n);
  107. js_endtry(J);
  108. js_free(J, out);
  109. }
  110. static void Ap_pop(js_State *J)
  111. {
  112. int n;
  113. n = js_getlength(J, 0);
  114. if (n > 0) {
  115. js_getindex(J, 0, n - 1);
  116. js_delindex(J, 0, n - 1);
  117. js_setlength(J, 0, n - 1);
  118. } else {
  119. js_setlength(J, 0, 0);
  120. js_pushundefined(J);
  121. }
  122. }
  123. static void Ap_push(js_State *J)
  124. {
  125. int i, top = js_gettop(J);
  126. int n;
  127. n = js_getlength(J, 0);
  128. for (i = 1; i < top; ++i, ++n) {
  129. js_copy(J, i);
  130. js_setindex(J, 0, n);
  131. }
  132. js_setlength(J, 0, n);
  133. js_pushnumber(J, n);
  134. }
  135. static void Ap_reverse(js_State *J)
  136. {
  137. int len, middle, lower;
  138. len = js_getlength(J, 0);
  139. middle = len / 2;
  140. lower = 0;
  141. while (lower != middle) {
  142. int upper = len - lower - 1;
  143. int haslower = js_hasindex(J, 0, lower);
  144. int hasupper = js_hasindex(J, 0, upper);
  145. if (haslower && hasupper) {
  146. js_setindex(J, 0, lower);
  147. js_setindex(J, 0, upper);
  148. } else if (hasupper) {
  149. js_setindex(J, 0, lower);
  150. js_delindex(J, 0, upper);
  151. } else if (haslower) {
  152. js_setindex(J, 0, upper);
  153. js_delindex(J, 0, lower);
  154. }
  155. ++lower;
  156. }
  157. js_copy(J, 0);
  158. }
  159. static void Ap_shift(js_State *J)
  160. {
  161. int k, len;
  162. len = js_getlength(J, 0);
  163. if (len == 0) {
  164. js_setlength(J, 0, 0);
  165. js_pushundefined(J);
  166. return;
  167. }
  168. js_getindex(J, 0, 0);
  169. for (k = 1; k < len; ++k) {
  170. if (js_hasindex(J, 0, k))
  171. js_setindex(J, 0, k - 1);
  172. else
  173. js_delindex(J, 0, k - 1);
  174. }
  175. js_delindex(J, 0, len - 1);
  176. js_setlength(J, 0, len - 1);
  177. }
  178. static void Ap_slice(js_State *J)
  179. {
  180. int len, s, e, n;
  181. double sv, ev;
  182. js_newarray(J);
  183. len = js_getlength(J, 0);
  184. sv = js_tointeger(J, 1);
  185. ev = js_isdefined(J, 2) ? js_tointeger(J, 2) : len;
  186. if (sv < 0) sv = sv + len;
  187. if (ev < 0) ev = ev + len;
  188. s = sv < 0 ? 0 : sv > len ? len : sv;
  189. e = ev < 0 ? 0 : ev > len ? len : ev;
  190. for (n = 0; s < e; ++s, ++n)
  191. if (js_hasindex(J, 0, s))
  192. js_setindex(J, -2, n);
  193. }
  194. struct sortslot {
  195. js_Value v;
  196. js_State *J;
  197. };
  198. static int sortcmp(const void *avoid, const void *bvoid)
  199. {
  200. const struct sortslot *aslot = avoid, *bslot = bvoid;
  201. const js_Value *a = &aslot->v, *b = &bslot->v;
  202. js_State *J = aslot->J;
  203. const char *sx, *sy;
  204. double v;
  205. int c;
  206. int unx = (a->t.type == JS_TUNDEFINED);
  207. int uny = (b->t.type == JS_TUNDEFINED);
  208. if (unx) return !uny;
  209. if (uny) return -1;
  210. if (js_iscallable(J, 1)) {
  211. js_copy(J, 1); /* copy function */
  212. js_pushundefined(J);
  213. js_pushvalue(J, *a);
  214. js_pushvalue(J, *b);
  215. js_call(J, 2);
  216. v = js_tonumber(J, -1);
  217. c = (v == 0) ? 0 : (v < 0) ? -1 : 1;
  218. js_pop(J, 1);
  219. } else {
  220. js_pushvalue(J, *a);
  221. js_pushvalue(J, *b);
  222. sx = js_tostring(J, -2);
  223. sy = js_tostring(J, -1);
  224. c = strcmp(sx, sy);
  225. js_pop(J, 2);
  226. }
  227. return c;
  228. }
  229. static void Ap_sort(js_State *J)
  230. {
  231. struct sortslot * volatile array = NULL;
  232. int i, n, len;
  233. len = js_getlength(J, 0);
  234. if (len <= 0) {
  235. js_copy(J, 0);
  236. return;
  237. }
  238. if (len >= INT_MAX / (int)sizeof(*array))
  239. js_rangeerror(J, "array is too large to sort");
  240. /* Holding objects where the GC cannot see them is illegal, but if we
  241. * don't allow the GC to run we can use qsort() on a temporary array of
  242. * js_Values for fast sorting.
  243. */
  244. ++J->gcpause;
  245. if (js_try(J)) {
  246. --J->gcpause;
  247. js_free(J, array);
  248. js_throw(J);
  249. }
  250. array = js_malloc(J, len * sizeof *array);
  251. n = 0;
  252. for (i = 0; i < len; ++i) {
  253. if (js_hasindex(J, 0, i)) {
  254. array[n].v = *js_tovalue(J, -1);
  255. array[n].J = J;
  256. js_pop(J, 1);
  257. ++n;
  258. }
  259. }
  260. qsort(array, n, sizeof *array, sortcmp);
  261. for (i = 0; i < n; ++i) {
  262. js_pushvalue(J, array[i].v);
  263. js_setindex(J, 0, i);
  264. }
  265. for (i = len-i; i >= n; --i) {
  266. js_delindex(J, 0, i);
  267. }
  268. --J->gcpause;
  269. js_endtry(J);
  270. js_free(J, array);
  271. js_copy(J, 0);
  272. }
  273. static void Ap_splice(js_State *J)
  274. {
  275. int top = js_gettop(J);
  276. int len, start, del, add, k;
  277. len = js_getlength(J, 0);
  278. start = js_tointeger(J, 1);
  279. if (start < 0)
  280. start = (len + start) > 0 ? len + start : 0;
  281. else if (start > len)
  282. start = len;
  283. if (js_isdefined(J, 2))
  284. del = js_tointeger(J, 2);
  285. else
  286. del = len - start;
  287. if (del > len - start)
  288. del = len - start;
  289. if (del < 0)
  290. del = 0;
  291. js_newarray(J);
  292. /* copy deleted items to return array */
  293. for (k = 0; k < del; ++k)
  294. if (js_hasindex(J, 0, start + k))
  295. js_setindex(J, -2, k);
  296. js_setlength(J, -1, del);
  297. /* shift the tail to resize the hole left by deleted items */
  298. add = top - 3;
  299. if (add < del) {
  300. for (k = start; k < len - del; ++k) {
  301. if (js_hasindex(J, 0, k + del))
  302. js_setindex(J, 0, k + add);
  303. else
  304. js_delindex(J, 0, k + add);
  305. }
  306. for (k = len; k > len - del + add; --k)
  307. js_delindex(J, 0, k - 1);
  308. } else if (add > del) {
  309. for (k = len - del; k > start; --k) {
  310. if (js_hasindex(J, 0, k + del - 1))
  311. js_setindex(J, 0, k + add - 1);
  312. else
  313. js_delindex(J, 0, k + add - 1);
  314. }
  315. }
  316. /* copy new items into the hole */
  317. for (k = 0; k < add; ++k) {
  318. js_copy(J, 3 + k);
  319. js_setindex(J, 0, start + k);
  320. }
  321. js_setlength(J, 0, len - del + add);
  322. }
  323. static void Ap_unshift(js_State *J)
  324. {
  325. int i, top = js_gettop(J);
  326. int k, len;
  327. len = js_getlength(J, 0);
  328. for (k = len; k > 0; --k) {
  329. int from = k - 1;
  330. int to = k + top - 2;
  331. if (js_hasindex(J, 0, from))
  332. js_setindex(J, 0, to);
  333. else
  334. js_delindex(J, 0, to);
  335. }
  336. for (i = 1; i < top; ++i) {
  337. js_copy(J, i);
  338. js_setindex(J, 0, i - 1);
  339. }
  340. js_setlength(J, 0, len + top - 1);
  341. js_pushnumber(J, len + top - 1);
  342. }
  343. static void Ap_toString(js_State *J)
  344. {
  345. if (!js_iscoercible(J, 0))
  346. js_typeerror(J, "'this' is not an object");
  347. js_getproperty(J, 0, "join");
  348. if (!js_iscallable(J, -1)) {
  349. js_pop(J, 1);
  350. // TODO: call Object.prototype.toString implementation directly
  351. js_getglobal(J, "Object");
  352. js_getproperty(J, -1, "prototype");
  353. js_rot2pop1(J);
  354. js_getproperty(J, -1, "toString");
  355. js_rot2pop1(J);
  356. }
  357. js_copy(J, 0);
  358. js_call(J, 0);
  359. }
  360. static void Ap_indexOf(js_State *J)
  361. {
  362. int k, len, from;
  363. len = js_getlength(J, 0);
  364. from = js_isdefined(J, 2) ? js_tointeger(J, 2) : 0;
  365. if (from < 0) from = len + from;
  366. if (from < 0) from = 0;
  367. js_copy(J, 1);
  368. for (k = from; k < len; ++k) {
  369. if (js_hasindex(J, 0, k)) {
  370. if (js_strictequal(J)) {
  371. js_pushnumber(J, k);
  372. return;
  373. }
  374. js_pop(J, 1);
  375. }
  376. }
  377. js_pushnumber(J, -1);
  378. }
  379. static void Ap_lastIndexOf(js_State *J)
  380. {
  381. int k, len, from;
  382. len = js_getlength(J, 0);
  383. from = js_isdefined(J, 2) ? js_tointeger(J, 2) : len - 1;
  384. if (from > len - 1) from = len - 1;
  385. if (from < 0) from = len + from;
  386. js_copy(J, 1);
  387. for (k = from; k >= 0; --k) {
  388. if (js_hasindex(J, 0, k)) {
  389. if (js_strictequal(J)) {
  390. js_pushnumber(J, k);
  391. return;
  392. }
  393. js_pop(J, 1);
  394. }
  395. }
  396. js_pushnumber(J, -1);
  397. }
  398. static void Ap_every(js_State *J)
  399. {
  400. int hasthis = js_gettop(J) >= 3;
  401. int k, len;
  402. if (!js_iscallable(J, 1))
  403. js_typeerror(J, "callback is not a function");
  404. len = js_getlength(J, 0);
  405. for (k = 0; k < len; ++k) {
  406. if (js_hasindex(J, 0, k)) {
  407. js_copy(J, 1);
  408. if (hasthis)
  409. js_copy(J, 2);
  410. else
  411. js_pushundefined(J);
  412. js_copy(J, -3);
  413. js_pushnumber(J, k);
  414. js_copy(J, 0);
  415. js_call(J, 3);
  416. if (!js_toboolean(J, -1))
  417. return;
  418. js_pop(J, 2);
  419. }
  420. }
  421. js_pushboolean(J, 1);
  422. }
  423. static void Ap_some(js_State *J)
  424. {
  425. int hasthis = js_gettop(J) >= 3;
  426. int k, len;
  427. if (!js_iscallable(J, 1))
  428. js_typeerror(J, "callback is not a function");
  429. len = js_getlength(J, 0);
  430. for (k = 0; k < len; ++k) {
  431. if (js_hasindex(J, 0, k)) {
  432. js_copy(J, 1);
  433. if (hasthis)
  434. js_copy(J, 2);
  435. else
  436. js_pushundefined(J);
  437. js_copy(J, -3);
  438. js_pushnumber(J, k);
  439. js_copy(J, 0);
  440. js_call(J, 3);
  441. if (js_toboolean(J, -1))
  442. return;
  443. js_pop(J, 2);
  444. }
  445. }
  446. js_pushboolean(J, 0);
  447. }
  448. static void Ap_forEach(js_State *J)
  449. {
  450. int hasthis = js_gettop(J) >= 3;
  451. int k, len;
  452. if (!js_iscallable(J, 1))
  453. js_typeerror(J, "callback is not a function");
  454. len = js_getlength(J, 0);
  455. for (k = 0; k < len; ++k) {
  456. if (js_hasindex(J, 0, k)) {
  457. js_copy(J, 1);
  458. if (hasthis)
  459. js_copy(J, 2);
  460. else
  461. js_pushundefined(J);
  462. js_copy(J, -3);
  463. js_pushnumber(J, k);
  464. js_copy(J, 0);
  465. js_call(J, 3);
  466. js_pop(J, 2);
  467. }
  468. }
  469. js_pushundefined(J);
  470. }
  471. static void Ap_map(js_State *J)
  472. {
  473. int hasthis = js_gettop(J) >= 3;
  474. int k, len;
  475. if (!js_iscallable(J, 1))
  476. js_typeerror(J, "callback is not a function");
  477. js_newarray(J);
  478. len = js_getlength(J, 0);
  479. for (k = 0; k < len; ++k) {
  480. if (js_hasindex(J, 0, k)) {
  481. js_copy(J, 1);
  482. if (hasthis)
  483. js_copy(J, 2);
  484. else
  485. js_pushundefined(J);
  486. js_copy(J, -3);
  487. js_pushnumber(J, k);
  488. js_copy(J, 0);
  489. js_call(J, 3);
  490. js_setindex(J, -3, k);
  491. js_pop(J, 1);
  492. }
  493. }
  494. js_setlength(J, -1, len);
  495. }
  496. static void Ap_filter(js_State *J)
  497. {
  498. int hasthis = js_gettop(J) >= 3;
  499. int k, to, len;
  500. if (!js_iscallable(J, 1))
  501. js_typeerror(J, "callback is not a function");
  502. js_newarray(J);
  503. to = 0;
  504. len = js_getlength(J, 0);
  505. for (k = 0; k < len; ++k) {
  506. if (js_hasindex(J, 0, k)) {
  507. js_copy(J, 1);
  508. if (hasthis)
  509. js_copy(J, 2);
  510. else
  511. js_pushundefined(J);
  512. js_copy(J, -3);
  513. js_pushnumber(J, k);
  514. js_copy(J, 0);
  515. js_call(J, 3);
  516. if (js_toboolean(J, -1)) {
  517. js_pop(J, 1);
  518. js_setindex(J, -2, to++);
  519. } else {
  520. js_pop(J, 2);
  521. }
  522. }
  523. }
  524. }
  525. static void Ap_reduce(js_State *J)
  526. {
  527. int hasinitial = js_gettop(J) >= 3;
  528. int k, len;
  529. if (!js_iscallable(J, 1))
  530. js_typeerror(J, "callback is not a function");
  531. len = js_getlength(J, 0);
  532. k = 0;
  533. if (len == 0 && !hasinitial)
  534. js_typeerror(J, "no initial value");
  535. /* initial value of accumulator */
  536. if (hasinitial)
  537. js_copy(J, 2);
  538. else {
  539. while (k < len)
  540. if (js_hasindex(J, 0, k++))
  541. break;
  542. if (k == len)
  543. js_typeerror(J, "no initial value");
  544. }
  545. while (k < len) {
  546. if (js_hasindex(J, 0, k)) {
  547. js_copy(J, 1);
  548. js_pushundefined(J);
  549. js_rot(J, 4); /* accumulator on top */
  550. js_rot(J, 4); /* property on top */
  551. js_pushnumber(J, k);
  552. js_copy(J, 0);
  553. js_call(J, 4); /* calculate new accumulator */
  554. }
  555. ++k;
  556. }
  557. /* return accumulator */
  558. }
  559. static void Ap_reduceRight(js_State *J)
  560. {
  561. int hasinitial = js_gettop(J) >= 3;
  562. int k, len;
  563. if (!js_iscallable(J, 1))
  564. js_typeerror(J, "callback is not a function");
  565. len = js_getlength(J, 0);
  566. k = len - 1;
  567. if (len == 0 && !hasinitial)
  568. js_typeerror(J, "no initial value");
  569. /* initial value of accumulator */
  570. if (hasinitial)
  571. js_copy(J, 2);
  572. else {
  573. while (k >= 0)
  574. if (js_hasindex(J, 0, k--))
  575. break;
  576. if (k < 0)
  577. js_typeerror(J, "no initial value");
  578. }
  579. while (k >= 0) {
  580. if (js_hasindex(J, 0, k)) {
  581. js_copy(J, 1);
  582. js_pushundefined(J);
  583. js_rot(J, 4); /* accumulator on top */
  584. js_rot(J, 4); /* property on top */
  585. js_pushnumber(J, k);
  586. js_copy(J, 0);
  587. js_call(J, 4); /* calculate new accumulator */
  588. }
  589. --k;
  590. }
  591. /* return accumulator */
  592. }
  593. static void A_isArray(js_State *J)
  594. {
  595. if (js_isobject(J, 1)) {
  596. js_Object *T = js_toobject(J, 1);
  597. js_pushboolean(J, T->type == JS_CARRAY);
  598. } else {
  599. js_pushboolean(J, 0);
  600. }
  601. }
  602. void jsB_initarray(js_State *J)
  603. {
  604. js_pushobject(J, J->Array_prototype);
  605. {
  606. jsB_propf(J, "Array.prototype.toString", Ap_toString, 0);
  607. jsB_propf(J, "Array.prototype.concat", Ap_concat, 0); /* 1 */
  608. jsB_propf(J, "Array.prototype.join", Ap_join, 1);
  609. jsB_propf(J, "Array.prototype.pop", Ap_pop, 0);
  610. jsB_propf(J, "Array.prototype.push", Ap_push, 0); /* 1 */
  611. jsB_propf(J, "Array.prototype.reverse", Ap_reverse, 0);
  612. jsB_propf(J, "Array.prototype.shift", Ap_shift, 0);
  613. jsB_propf(J, "Array.prototype.slice", Ap_slice, 2);
  614. jsB_propf(J, "Array.prototype.sort", Ap_sort, 1);
  615. jsB_propf(J, "Array.prototype.splice", Ap_splice, 2);
  616. jsB_propf(J, "Array.prototype.unshift", Ap_unshift, 0); /* 1 */
  617. /* ES5 */
  618. jsB_propf(J, "Array.prototype.indexOf", Ap_indexOf, 1);
  619. jsB_propf(J, "Array.prototype.lastIndexOf", Ap_lastIndexOf, 1);
  620. jsB_propf(J, "Array.prototype.every", Ap_every, 1);
  621. jsB_propf(J, "Array.prototype.some", Ap_some, 1);
  622. jsB_propf(J, "Array.prototype.forEach", Ap_forEach, 1);
  623. jsB_propf(J, "Array.prototype.map", Ap_map, 1);
  624. jsB_propf(J, "Array.prototype.filter", Ap_filter, 1);
  625. jsB_propf(J, "Array.prototype.reduce", Ap_reduce, 1);
  626. jsB_propf(J, "Array.prototype.reduceRight", Ap_reduceRight, 1);
  627. }
  628. js_newcconstructor(J, jsB_new_Array, jsB_new_Array, "Array", 0); /* 1 */
  629. {
  630. /* ES5 */
  631. jsB_propf(J, "Array.isArray", A_isArray, 1);
  632. }
  633. js_defglobal(J, "Array", JS_DONTENUM);
  634. }