#include "jsi.h"#include "jslex.h"#include "jsvalue.h"#include "jsbuiltin.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;} 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 < ' ') { 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]; const char *s, *gap; 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);}
#include "jsi.h"
#include "jslex.h"
#include "jsvalue.h"
#include "jsbuiltin.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;
}
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 < ' ') {
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];
const char *s, *gap;
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);
}