Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. #include "jsi.h"
  2. #include "jslex.h"
  3. #include "jsvalue.h"
  4. #include "jsbuiltin.h"
  5.  
  6. #include "utf.h"
  7.  
  8. int js_isnumberobject(js_State *J, int idx)
  9. {
  10.         return js_isobject(J, idx) && js_toobject(J, idx)->type == JS_CNUMBER;
  11. }
  12.  
  13. int js_isstringobject(js_State *J, int idx)
  14. {
  15.         return js_isobject(J, idx) && js_toobject(J, idx)->type == JS_CSTRING;
  16. }
  17.  
  18. static void jsonnext(js_State *J)
  19. {
  20.         J->lookahead = jsY_lexjson(J);
  21. }
  22.  
  23. static int jsonaccept(js_State *J, int t)
  24. {
  25.         if (J->lookahead == t) {
  26.                 jsonnext(J);
  27.                 return 1;
  28.         }
  29.         return 0;
  30. }
  31.  
  32. static void jsonexpect(js_State *J, int t)
  33. {
  34.         if (!jsonaccept(J, t))
  35.                 js_syntaxerror(J, "JSON: unexpected token: %s (expected %s)",
  36.                                 jsY_tokenstring(J->lookahead), jsY_tokenstring(t));
  37. }
  38.  
  39. static void jsonvalue(js_State *J)
  40. {
  41.         int i;
  42.         const char *name;
  43.  
  44.         switch (J->lookahead) {
  45.         case TK_STRING:
  46.                 js_pushstring(J, J->text);
  47.                 jsonnext(J);
  48.                 break;
  49.  
  50.         case TK_NUMBER:
  51.                 js_pushnumber(J, J->number);
  52.                 jsonnext(J);
  53.                 break;
  54.  
  55.         case '{':
  56.                 js_newobject(J);
  57.                 jsonnext(J);
  58.                 if (jsonaccept(J, '}'))
  59.                         return;
  60.                 do {
  61.                         if (J->lookahead != TK_STRING)
  62.                                 js_syntaxerror(J, "JSON: unexpected token: %s (expected string)", jsY_tokenstring(J->lookahead));
  63.                         name = J->text;
  64.                         jsonnext(J);
  65.                         jsonexpect(J, ':');
  66.                         jsonvalue(J);
  67.                         js_setproperty(J, -2, name);
  68.                 } while (jsonaccept(J, ','));
  69.                 jsonexpect(J, '}');
  70.                 break;
  71.  
  72.         case '[':
  73.                 js_newarray(J);
  74.                 jsonnext(J);
  75.                 i = 0;
  76.                 if (jsonaccept(J, ']'))
  77.                         return;
  78.                 do {
  79.                         jsonvalue(J);
  80.                         js_setindex(J, -2, i++);
  81.                 } while (jsonaccept(J, ','));
  82.                 jsonexpect(J, ']');
  83.                 break;
  84.  
  85.         case TK_TRUE:
  86.                 js_pushboolean(J, 1);
  87.                 jsonnext(J);
  88.                 break;
  89.  
  90.         case TK_FALSE:
  91.                 js_pushboolean(J, 0);
  92.                 jsonnext(J);
  93.                 break;
  94.  
  95.         case TK_NULL:
  96.                 js_pushnull(J);
  97.                 jsonnext(J);
  98.                 break;
  99.  
  100.         default:
  101.                 js_syntaxerror(J, "JSON: unexpected token: %s", jsY_tokenstring(J->lookahead));
  102.         }
  103. }
  104.  
  105. static void jsonrevive(js_State *J, const char *name)
  106. {
  107.         const char *key;
  108.         char buf[32];
  109.  
  110.         /* revive is in 2 */
  111.         /* holder is in -1 */
  112.  
  113.         js_getproperty(J, -1, name); /* get value from holder */
  114.  
  115.         if (js_isobject(J, -1)) {
  116.                 if (js_isarray(J, -1)) {
  117.                         int i = 0;
  118.                         int n = js_getlength(J, -1);
  119.                         for (i = 0; i < n; ++i) {
  120.                                 jsonrevive(J, js_itoa(buf, i));
  121.                                 if (js_isundefined(J, -1)) {
  122.                                         js_pop(J, 1);
  123.                                         js_delproperty(J, -1, buf);
  124.                                 } else {
  125.                                         js_setproperty(J, -2, buf);
  126.                                 }
  127.                         }
  128.                 } else {
  129.                         js_pushiterator(J, -1, 1);
  130.                         while ((key = js_nextiterator(J, -1))) {
  131.                                 js_rot2(J);
  132.                                 jsonrevive(J, key);
  133.                                 if (js_isundefined(J, -1)) {
  134.                                         js_pop(J, 1);
  135.                                         js_delproperty(J, -1, key);
  136.                                 } else {
  137.                                         js_setproperty(J, -2, key);
  138.                                 }
  139.                                 js_rot2(J);
  140.                         }
  141.                         js_pop(J, 1);
  142.                 }
  143.         }
  144.  
  145.         js_copy(J, 2); /* reviver function */
  146.         js_copy(J, -3); /* holder as this */
  147.         js_pushstring(J, name); /* name */
  148.         js_copy(J, -4); /* value */
  149.         js_call(J, 2);
  150.         js_rot2pop1(J); /* pop old value, leave new value on stack */
  151. }
  152.  
  153. static void JSON_parse(js_State *J)
  154. {
  155.         const char *source = js_tostring(J, 1);
  156.         jsY_initlex(J, "JSON", source);
  157.         jsonnext(J);
  158.  
  159.         if (js_iscallable(J, 2)) {
  160.                 js_newobject(J);
  161.                 jsonvalue(J);
  162.                 js_defproperty(J, -2, "", 0);
  163.                 jsonrevive(J, "");
  164.         } else {
  165.                 jsonvalue(J);
  166.         }
  167. }
  168.  
  169. static void fmtnum(js_State *J, js_Buffer **sb, double n)
  170. {
  171.         if (isnan(n)) js_puts(J, sb, "null");
  172.         else if (isinf(n)) js_puts(J, sb, "null");
  173.         else if (n == 0) js_puts(J, sb, "0");
  174.         else {
  175.                 char buf[40];
  176.                 js_puts(J, sb, jsV_numbertostring(J, buf, n));
  177.         }
  178. }
  179.  
  180. static void fmtstr(js_State *J, js_Buffer **sb, const char *s)
  181. {
  182.         static const char *HEX = "0123456789ABCDEF";
  183.         int i, n;
  184.         Rune c;
  185.         js_putc(J, sb, '"');
  186.         while (*s) {
  187.                 n = chartorune(&c, s);
  188.                 switch (c) {
  189.                 case '"': js_puts(J, sb, "\\\""); break;
  190.                 case '\\': js_puts(J, sb, "\\\\"); break;
  191.                 case '\b': js_puts(J, sb, "\\b"); break;
  192.                 case '\f': js_puts(J, sb, "\\f"); break;
  193.                 case '\n': js_puts(J, sb, "\\n"); break;
  194.                 case '\r': js_puts(J, sb, "\\r"); break;
  195.                 case '\t': js_puts(J, sb, "\\t"); break;
  196.                 default:
  197.                         if (c < ' ') {
  198.                                 js_putc(J, sb, '\\');
  199.                                 js_putc(J, sb, 'u');
  200.                                 js_putc(J, sb, HEX[(c>>12)&15]);
  201.                                 js_putc(J, sb, HEX[(c>>8)&15]);
  202.                                 js_putc(J, sb, HEX[(c>>4)&15]);
  203.                                 js_putc(J, sb, HEX[c&15]);
  204.                         } else if (c < 128) {
  205.                                 js_putc(J, sb, c);
  206.                         } else {
  207.                                 for (i = 0; i < n; ++i)
  208.                                         js_putc(J, sb, s[i]);
  209.                         }
  210.                         break;
  211.                 }
  212.                 s += n;
  213.         }
  214.         js_putc(J, sb, '"');
  215. }
  216.  
  217. static void fmtindent(js_State *J, js_Buffer **sb, const char *gap, int level)
  218. {
  219.         js_putc(J, sb, '\n');
  220.         while (level--)
  221.                 js_puts(J, sb, gap);
  222. }
  223.  
  224. static int fmtvalue(js_State *J, js_Buffer **sb, const char *key, const char *gap, int level);
  225.  
  226. static int filterprop(js_State *J, const char *key)
  227. {
  228.         int i, n, found;
  229.         /* replacer/property-list is in stack slot 2 */
  230.         if (js_isarray(J, 2)) {
  231.                 found = 0;
  232.                 n = js_getlength(J, 2);
  233.                 for (i = 0; i < n && !found; ++i) {
  234.                         js_getindex(J, 2, i);
  235.                         if (js_isstring(J, -1) || js_isnumber(J, -1) ||
  236.                                 js_isstringobject(J, -1) || js_isnumberobject(J, -1))
  237.                                 found = !strcmp(key, js_tostring(J, -1));
  238.                         js_pop(J, 1);
  239.                 }
  240.                 return found;
  241.         }
  242.         return 1;
  243. }
  244.  
  245. static void fmtobject(js_State *J, js_Buffer **sb, js_Object *obj, const char *gap, int level)
  246. {
  247.         const char *key;
  248.         int save;
  249.         int i, n;
  250.  
  251.         n = js_gettop(J) - 1;
  252.         for (i = 4; i < n; ++i)
  253.                 if (js_isobject(J, i))
  254.                         if (js_toobject(J, i) == js_toobject(J, -1))
  255.                                 js_typeerror(J, "cyclic object value");
  256.  
  257.         n = 0;
  258.         js_putc(J, sb, '{');
  259.         js_pushiterator(J, -1, 1);
  260.         while ((key = js_nextiterator(J, -1))) {
  261.                 if (filterprop(J, key)) {
  262.                         save = (*sb)->n;
  263.                         if (n) js_putc(J, sb, ',');
  264.                         if (gap) fmtindent(J, sb, gap, level + 1);
  265.                         fmtstr(J, sb, key);
  266.                         js_putc(J, sb, ':');
  267.                         if (gap)
  268.                                 js_putc(J, sb, ' ');
  269.                         js_rot2(J);
  270.                         if (!fmtvalue(J, sb, key, gap, level + 1))
  271.                                 (*sb)->n = save;
  272.                         else
  273.                                 ++n;
  274.                         js_rot2(J);
  275.                 }
  276.         }
  277.         js_pop(J, 1);
  278.         if (gap && n) fmtindent(J, sb, gap, level);
  279.         js_putc(J, sb, '}');
  280. }
  281.  
  282. static void fmtarray(js_State *J, js_Buffer **sb, const char *gap, int level)
  283. {
  284.         int n, i;
  285.         char buf[32];
  286.  
  287.         n = js_gettop(J) - 1;
  288.         for (i = 4; i < n; ++i)
  289.                 if (js_isobject(J, i))
  290.                         if (js_toobject(J, i) == js_toobject(J, -1))
  291.                                 js_typeerror(J, "cyclic object value");
  292.  
  293.         js_putc(J, sb, '[');
  294.         n = js_getlength(J, -1);
  295.         for (i = 0; i < n; ++i) {
  296.                 if (i) js_putc(J, sb, ',');
  297.                 if (gap) fmtindent(J, sb, gap, level + 1);
  298.                 if (!fmtvalue(J, sb, js_itoa(buf, i), gap, level + 1))
  299.                         js_puts(J, sb, "null");
  300.         }
  301.         if (gap && n) fmtindent(J, sb, gap, level);
  302.         js_putc(J, sb, ']');
  303. }
  304.  
  305. static int fmtvalue(js_State *J, js_Buffer **sb, const char *key, const char *gap, int level)
  306. {
  307.         /* replacer/property-list is in 2 */
  308.         /* holder is in -1 */
  309.  
  310.         js_getproperty(J, -1, key);
  311.  
  312.         if (js_isobject(J, -1)) {
  313.                 if (js_hasproperty(J, -1, "toJSON")) {
  314.                         if (js_iscallable(J, -1)) {
  315.                                 js_copy(J, -2);
  316.                                 js_pushstring(J, key);
  317.                                 js_call(J, 1);
  318.                                 js_rot2pop1(J);
  319.                         } else {
  320.                                 js_pop(J, 1);
  321.                         }
  322.                 }
  323.         }
  324.  
  325.         if (js_iscallable(J, 2)) {
  326.                 js_copy(J, 2); /* replacer function */
  327.                 js_copy(J, -3); /* holder as this */
  328.                 js_pushstring(J, key); /* name */
  329.                 js_copy(J, -4); /* old value */
  330.                 js_call(J, 2);
  331.                 js_rot2pop1(J); /* pop old value, leave new value on stack */
  332.         }
  333.  
  334.         if (js_isobject(J, -1) && !js_iscallable(J, -1)) {
  335.                 js_Object *obj = js_toobject(J, -1);
  336.                 switch (obj->type) {
  337.                 case JS_CNUMBER: fmtnum(J, sb, obj->u.number); break;
  338.                 case JS_CSTRING: fmtstr(J, sb, obj->u.s.string); break;
  339.                 case JS_CBOOLEAN: js_puts(J, sb, obj->u.boolean ? "true" : "false"); break;
  340.                 case JS_CARRAY: fmtarray(J, sb, gap, level); break;
  341.                 default: fmtobject(J, sb, obj, gap, level); break;
  342.                 }
  343.         }
  344.         else if (js_isboolean(J, -1))
  345.                 js_puts(J, sb, js_toboolean(J, -1) ? "true" : "false");
  346.         else if (js_isnumber(J, -1))
  347.                 fmtnum(J, sb, js_tonumber(J, -1));
  348.         else if (js_isstring(J, -1))
  349.                 fmtstr(J, sb, js_tostring(J, -1));
  350.         else if (js_isnull(J, -1))
  351.                 js_puts(J, sb, "null");
  352.         else {
  353.                 js_pop(J, 1);
  354.                 return 0;
  355.         }
  356.  
  357.         js_pop(J, 1);
  358.         return 1;
  359. }
  360.  
  361. static void JSON_stringify(js_State *J)
  362. {
  363.         js_Buffer *sb = NULL;
  364.         char buf[12];
  365.         const char *s, *gap;
  366.         int n;
  367.  
  368.         gap = NULL;
  369.  
  370.         if (js_isnumber(J, 3) || js_isnumberobject(J, 3)) {
  371.                 n = js_tointeger(J, 3);
  372.                 if (n < 0) n = 0;
  373.                 if (n > 10) n = 10;
  374.                 memset(buf, ' ', n);
  375.                 buf[n] = 0;
  376.                 if (n > 0) gap = buf;
  377.         } else if (js_isstring(J, 3) || js_isstringobject(J, 3)) {
  378.                 s = js_tostring(J, 3);
  379.                 n = strlen(s);
  380.                 if (n > 10) n = 10;
  381.                 memcpy(buf, s, n);
  382.                 buf[n] = 0;
  383.                 if (n > 0) gap = buf;
  384.         }
  385.  
  386.         if (js_try(J)) {
  387.                 js_free(J, sb);
  388.                 js_throw(J);
  389.         }
  390.  
  391.         js_newobject(J); /* wrapper */
  392.         js_copy(J, 1);
  393.         js_defproperty(J, -2, "", 0);
  394.         if (!fmtvalue(J, &sb, "", gap, 0)) {
  395.                 js_pushundefined(J);
  396.         } else {
  397.                 js_putc(J, &sb, 0);
  398.                 js_pushstring(J, sb ? sb->s : "");
  399.                 js_rot2pop1(J);
  400.         }
  401.  
  402.         js_endtry(J);
  403.         js_free(J, sb);
  404. }
  405.  
  406. void jsB_initjson(js_State *J)
  407. {
  408.         js_pushobject(J, jsV_newobject(J, JS_CJSON, J->Object_prototype));
  409.         {
  410.                 jsB_propf(J, "JSON.parse", JSON_parse, 2);
  411.                 jsB_propf(J, "JSON.stringify", JSON_stringify, 3);
  412.         }
  413.         js_defglobal(J, "JSON", JS_DONTENUM);
  414. }
  415.