| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- #include "jsi.h"
- #include "utf.h"
- int js_isnumberobject(js_State *J, int idx)
- {
- return js_isobject(J, idx) && js_toobject(J, idx)->type == JS_CNUMBER;
- }
- int js_isstringobject(js_State *J, int idx)
- {
- return js_isobject(J, idx) && js_toobject(J, idx)->type == JS_CSTRING;
- }
- int js_isbooleanobject(js_State *J, int idx)
- {
- return js_isobject(J, idx) && js_toobject(J, idx)->type == JS_CBOOLEAN;
- }
- int js_isdateobject(js_State *J, int idx)
- {
- return js_isobject(J, idx) && js_toobject(J, idx)->type == JS_CDATE;
- }
- static void jsonnext(js_State *J)
- {
- J->lookahead = jsY_lexjson(J);
- }
- static int jsonaccept(js_State *J, int t)
- {
- if (J->lookahead == t) {
- jsonnext(J);
- return 1;
- }
- return 0;
- }
- static void jsonexpect(js_State *J, int t)
- {
- if (!jsonaccept(J, t))
- js_syntaxerror(J, "JSON: unexpected token: %s (expected %s)",
- jsY_tokenstring(J->lookahead), jsY_tokenstring(t));
- }
- static void jsonvalue(js_State *J)
- {
- int i;
- const char *name;
- switch (J->lookahead) {
- case TK_STRING:
- js_pushstring(J, J->text);
- jsonnext(J);
- break;
- case TK_NUMBER:
- js_pushnumber(J, J->number);
- jsonnext(J);
- break;
- case '{':
- js_newobject(J);
- jsonnext(J);
- if (jsonaccept(J, '}'))
- return;
- do {
- if (J->lookahead != TK_STRING)
- js_syntaxerror(J, "JSON: unexpected token: %s (expected string)", jsY_tokenstring(J->lookahead));
- name = J->text;
- jsonnext(J);
- jsonexpect(J, ':');
- jsonvalue(J);
- js_setproperty(J, -2, name);
- } while (jsonaccept(J, ','));
- jsonexpect(J, '}');
- break;
- case '[':
- js_newarray(J);
- jsonnext(J);
- i = 0;
- if (jsonaccept(J, ']'))
- return;
- do {
- jsonvalue(J);
- js_setindex(J, -2, i++);
- } while (jsonaccept(J, ','));
- jsonexpect(J, ']');
- break;
- case TK_TRUE:
- js_pushboolean(J, 1);
- jsonnext(J);
- break;
- case TK_FALSE:
- js_pushboolean(J, 0);
- jsonnext(J);
- break;
- case TK_NULL:
- js_pushnull(J);
- jsonnext(J);
- break;
- default:
- js_syntaxerror(J, "JSON: unexpected token: %s", jsY_tokenstring(J->lookahead));
- }
- }
- static void jsonrevive(js_State *J, const char *name)
- {
- const char *key;
- char buf[32];
- /* revive is in 2 */
- /* holder is in -1 */
- js_getproperty(J, -1, name); /* get value from holder */
- if (js_isobject(J, -1)) {
- if (js_isarray(J, -1)) {
- int i = 0;
- int n = js_getlength(J, -1);
- for (i = 0; i < n; ++i) {
- jsonrevive(J, js_itoa(buf, i));
- if (js_isundefined(J, -1)) {
- js_pop(J, 1);
- js_delproperty(J, -1, buf);
- } else {
- js_setproperty(J, -2, buf);
- }
- }
- } else {
- js_pushiterator(J, -1, 1);
- while ((key = js_nextiterator(J, -1))) {
- js_rot2(J);
- jsonrevive(J, key);
- if (js_isundefined(J, -1)) {
- js_pop(J, 1);
- js_delproperty(J, -1, key);
- } else {
- js_setproperty(J, -2, key);
- }
- js_rot2(J);
- }
- js_pop(J, 1);
- }
- }
- js_copy(J, 2); /* reviver function */
- js_copy(J, -3); /* holder as this */
- js_pushstring(J, name); /* name */
- js_copy(J, -4); /* value */
- js_call(J, 2);
- js_rot2pop1(J); /* pop old value, leave new value on stack */
- }
- static void JSON_parse(js_State *J)
- {
- const char *source = js_tostring(J, 1);
- jsY_initlex(J, "JSON", source);
- jsonnext(J);
- if (js_iscallable(J, 2)) {
- js_newobject(J);
- jsonvalue(J);
- js_defproperty(J, -2, "", 0);
- jsonrevive(J, "");
- } else {
- jsonvalue(J);
- }
- }
- static void fmtnum(js_State *J, js_Buffer **sb, double n)
- {
- if (isnan(n)) js_puts(J, sb, "null");
- else if (isinf(n)) js_puts(J, sb, "null");
- else if (n == 0) js_puts(J, sb, "0");
- else {
- char buf[40];
- js_puts(J, sb, jsV_numbertostring(J, buf, n));
- }
- }
- static void fmtstr(js_State *J, js_Buffer **sb, const char *s)
- {
- static const char *HEX = "0123456789abcdef";
- int i, n;
- Rune c;
- js_putc(J, sb, '"');
- while (*s) {
- n = chartorune(&c, s);
- switch (c) {
- case '"': js_puts(J, sb, "\\\""); break;
- case '\\': js_puts(J, sb, "\\\\"); break;
- case '\b': js_puts(J, sb, "\\b"); break;
- case '\f': js_puts(J, sb, "\\f"); break;
- case '\n': js_puts(J, sb, "\\n"); break;
- case '\r': js_puts(J, sb, "\\r"); break;
- case '\t': js_puts(J, sb, "\\t"); break;
- default:
- if (c < ' ' || (c >= 0xd800 && c <= 0xdfff)) {
- js_putc(J, sb, '\\');
- js_putc(J, sb, 'u');
- js_putc(J, sb, HEX[(c>>12)&15]);
- js_putc(J, sb, HEX[(c>>8)&15]);
- js_putc(J, sb, HEX[(c>>4)&15]);
- js_putc(J, sb, HEX[c&15]);
- } else if (c < 128) {
- js_putc(J, sb, c);
- } else {
- for (i = 0; i < n; ++i)
- js_putc(J, sb, s[i]);
- }
- break;
- }
- s += n;
- }
- js_putc(J, sb, '"');
- }
- static void fmtindent(js_State *J, js_Buffer **sb, const char *gap, int level)
- {
- js_putc(J, sb, '\n');
- while (level--)
- js_puts(J, sb, gap);
- }
- static int fmtvalue(js_State *J, js_Buffer **sb, const char *key, const char *gap, int level);
- static int filterprop(js_State *J, const char *key)
- {
- int i, n, found;
- /* replacer/property-list is in stack slot 2 */
- if (js_isarray(J, 2)) {
- found = 0;
- n = js_getlength(J, 2);
- for (i = 0; i < n && !found; ++i) {
- js_getindex(J, 2, i);
- if (js_isstring(J, -1) || js_isnumber(J, -1) ||
- js_isstringobject(J, -1) || js_isnumberobject(J, -1))
- found = !strcmp(key, js_tostring(J, -1));
- js_pop(J, 1);
- }
- return found;
- }
- return 1;
- }
- static void fmtobject(js_State *J, js_Buffer **sb, js_Object *obj, const char *gap, int level)
- {
- const char *key;
- int save;
- int i, n;
- n = js_gettop(J) - 1;
- for (i = 4; i < n; ++i)
- if (js_isobject(J, i))
- if (js_toobject(J, i) == js_toobject(J, -1))
- js_typeerror(J, "cyclic object value");
- n = 0;
- js_putc(J, sb, '{');
- js_pushiterator(J, -1, 1);
- while ((key = js_nextiterator(J, -1))) {
- if (filterprop(J, key)) {
- save = (*sb)->n;
- if (n) js_putc(J, sb, ',');
- if (gap) fmtindent(J, sb, gap, level + 1);
- fmtstr(J, sb, key);
- js_putc(J, sb, ':');
- if (gap)
- js_putc(J, sb, ' ');
- js_rot2(J);
- if (!fmtvalue(J, sb, key, gap, level + 1))
- (*sb)->n = save;
- else
- ++n;
- js_rot2(J);
- }
- }
- js_pop(J, 1);
- if (gap && n) fmtindent(J, sb, gap, level);
- js_putc(J, sb, '}');
- }
- static void fmtarray(js_State *J, js_Buffer **sb, const char *gap, int level)
- {
- int n, i;
- char buf[32];
- n = js_gettop(J) - 1;
- for (i = 4; i < n; ++i)
- if (js_isobject(J, i))
- if (js_toobject(J, i) == js_toobject(J, -1))
- js_typeerror(J, "cyclic object value");
- js_putc(J, sb, '[');
- n = js_getlength(J, -1);
- for (i = 0; i < n; ++i) {
- if (i) js_putc(J, sb, ',');
- if (gap) fmtindent(J, sb, gap, level + 1);
- if (!fmtvalue(J, sb, js_itoa(buf, i), gap, level + 1))
- js_puts(J, sb, "null");
- }
- if (gap && n) fmtindent(J, sb, gap, level);
- js_putc(J, sb, ']');
- }
- static int fmtvalue(js_State *J, js_Buffer **sb, const char *key, const char *gap, int level)
- {
- /* replacer/property-list is in 2 */
- /* holder is in -1 */
- js_getproperty(J, -1, key);
- if (js_isobject(J, -1)) {
- if (js_hasproperty(J, -1, "toJSON")) {
- if (js_iscallable(J, -1)) {
- js_copy(J, -2);
- js_pushstring(J, key);
- js_call(J, 1);
- js_rot2pop1(J);
- } else {
- js_pop(J, 1);
- }
- }
- }
- if (js_iscallable(J, 2)) {
- js_copy(J, 2); /* replacer function */
- js_copy(J, -3); /* holder as this */
- js_pushstring(J, key); /* name */
- js_copy(J, -4); /* old value */
- js_call(J, 2);
- js_rot2pop1(J); /* pop old value, leave new value on stack */
- }
- if (js_isobject(J, -1) && !js_iscallable(J, -1)) {
- js_Object *obj = js_toobject(J, -1);
- switch (obj->type) {
- case JS_CNUMBER: fmtnum(J, sb, obj->u.number); break;
- case JS_CSTRING: fmtstr(J, sb, obj->u.s.string); break;
- case JS_CBOOLEAN: js_puts(J, sb, obj->u.boolean ? "true" : "false"); break;
- case JS_CARRAY: fmtarray(J, sb, gap, level); break;
- default: fmtobject(J, sb, obj, gap, level); break;
- }
- }
- else if (js_isboolean(J, -1))
- js_puts(J, sb, js_toboolean(J, -1) ? "true" : "false");
- else if (js_isnumber(J, -1))
- fmtnum(J, sb, js_tonumber(J, -1));
- else if (js_isstring(J, -1))
- fmtstr(J, sb, js_tostring(J, -1));
- else if (js_isnull(J, -1))
- js_puts(J, sb, "null");
- else {
- js_pop(J, 1);
- return 0;
- }
- js_pop(J, 1);
- return 1;
- }
- static void JSON_stringify(js_State *J)
- {
- js_Buffer *sb = NULL;
- char buf[12];
- /* NOTE: volatile to silence GCC warning about longjmp clobbering a variable */
- const char * volatile gap;
- const char *s;
- int n;
- gap = NULL;
- if (js_isnumber(J, 3) || js_isnumberobject(J, 3)) {
- n = js_tointeger(J, 3);
- if (n < 0) n = 0;
- if (n > 10) n = 10;
- memset(buf, ' ', n);
- buf[n] = 0;
- if (n > 0) gap = buf;
- } else if (js_isstring(J, 3) || js_isstringobject(J, 3)) {
- s = js_tostring(J, 3);
- n = strlen(s);
- if (n > 10) n = 10;
- memcpy(buf, s, n);
- buf[n] = 0;
- if (n > 0) gap = buf;
- }
- if (js_try(J)) {
- js_free(J, sb);
- js_throw(J);
- }
- js_newobject(J); /* wrapper */
- js_copy(J, 1);
- js_defproperty(J, -2, "", 0);
- if (!fmtvalue(J, &sb, "", gap, 0)) {
- js_pushundefined(J);
- } else {
- js_putc(J, &sb, 0);
- js_pushstring(J, sb ? sb->s : "");
- js_rot2pop1(J);
- }
- js_endtry(J);
- js_free(J, sb);
- }
- void jsB_initjson(js_State *J)
- {
- js_pushobject(J, jsV_newobject(J, JS_CJSON, J->Object_prototype));
- {
- jsB_propf(J, "JSON.parse", JSON_parse, 2);
- jsB_propf(J, "JSON.stringify", JSON_stringify, 3);
- }
- js_defglobal(J, "JSON", JS_DONTENUM);
- }
|