ref: 0edd10268310a9bfbd6bd0791a4558a17cc338e1
parent: f4bb8070715c0b847d9a69ab65c3c6b8abba8cf2
author: Tor Andersson <[email protected]>
date: Tue Feb 11 08:05:02 EST 2014
JSON.stringify() No replacer or space arguments.
--- a/jsi.h
+++ b/jsi.h
@@ -47,6 +47,10 @@
void js_dup(js_State *J);
void js_rot2(js_State *J);
void js_rot3(js_State *J);
+void js_rot2pop1(js_State *J);
+void js_rot3pop2(js_State *J);
+void js_dup1rot3(js_State *J);
+void js_dup1rot4(js_State *J);
int js_RegExp_prototype_exec(js_State *J, js_Regexp *re, const char *text);
--- a/json.c
+++ b/json.c
@@ -3,6 +3,8 @@
#include "jsvalue.h"
#include "jsbuiltin.h"
+#include "utf.h"
+
static inline void jsonnext(js_State *J)
{
J->lookahead = jsY_lexjson(J);
@@ -100,8 +102,158 @@
return 1;
}
+static void fmtnum(js_Buffer **sb, double n)
+{
+ if (isnan(n)) sb_puts(sb, "NaN");
+ else if (isinf(n)) sb_puts(sb, n < 0 ? "-Infinity" : "Infinity");
+ else if (n == 0) sb_puts(sb, "0");
+ else {
+ char buf[40];
+ sprintf(buf, "%.17g", n);
+ sb_puts(sb, buf);
+ }
+}
+
+static void fmtstr(js_Buffer **sb, const char *s)
+{
+ static const char *HEX = "0123456789ABCDEF";
+ Rune c;
+ sb_putc(sb, '"');
+ while (*s) {
+ s += chartorune(&c, s);
+ switch (c) {
+ case '"': sb_puts(sb, "\\\""); break;
+ case '\\': sb_puts(sb, "\\\\"); break;
+ case '\b': sb_puts(sb, "\\b"); break;
+ case '\f': sb_puts(sb, "\\f"); break;
+ case '\n': sb_puts(sb, "\\n"); break;
+ case '\r': sb_puts(sb, "\\r"); break;
+ case '\t': sb_puts(sb, "\\t"); break;
+ default:
+ if (c < ' ') {
+ sb_puts(sb, "\\u");
+ sb_putc(sb, HEX[(c>>12)&15]);
+ sb_putc(sb, HEX[(c>>8)&15]);
+ sb_putc(sb, HEX[(c>>4)&15]);
+ sb_putc(sb, HEX[c&15]);
+ } else {
+ sb_putc(sb, c); break;
+ }
+ }
+ }
+ sb_putc(sb, '"');
+}
+
+static int fmtvalue(js_State *J, js_Buffer **sb, const char *key);
+
+static void fmtobject(js_State *J, js_Buffer **sb, js_Object *obj)
+{
+ js_Property *ref;
+ int save;
+ int n = 0;
+
+ sb_putc(sb, '{');
+ for (ref = obj->head; ref; ref = ref->next) {
+ if (ref->atts & JS_DONTENUM)
+ continue;
+ save = (*sb)->n;
+ if (n) sb_putc(sb, ',');
+ fmtstr(sb, ref->name);
+ sb_putc(sb, ':');
+ js_pushvalue(J, ref->value);
+ if (!fmtvalue(J, sb, ref->name))
+ (*sb)->n = save;
+ else
+ ++n;
+ js_pop(J, 1);
+ }
+ sb_putc(sb, '}');
+}
+
+static void fmtarray(js_State *J, js_Buffer **sb)
+{
+ unsigned int len, k;
+ char buf[40];
+
+ js_getproperty(J, -1, "length");
+ len = js_touint32(J, -1);
+ js_pop(J, 1);
+
+ sb_putc(sb, '[');
+ for (k = 0; k < len; ++k) {
+ if (k) sb_putc(sb, ',');
+ sprintf(buf, "%u", k);
+ js_getproperty(J, -1, buf);
+ if (!fmtvalue(J, sb, buf))
+ sb_puts(sb, "null");
+ js_pop(J, 1);
+ }
+ sb_putc(sb, ']');
+}
+
+static int fmtvalue(js_State *J, js_Buffer **sb, const char *key)
+{
+ if (js_try(J)) {
+ free(*sb);
+ js_throw(J);
+ }
+ if (js_isobject(J, -1)) {
+ if (js_hasproperty(J, -1, "toJSON")) {
+ if (js_iscallable(J, -1)) {
+ js_copy(J, -2);
+ js_pushliteral(J, key);
+ js_call(J, 1);
+ js_rot2pop1(J);
+ } else {
+ js_pop(J, 1);
+ }
+ }
+ }
+ js_endtry(J);
+
+ // TODO: replacer()
+
+ if (js_isobject(J, -1) && !js_iscallable(J, -1)) {
+ js_Object *obj = js_toobject(J, -1);
+ switch (obj->type) {
+ case JS_CNUMBER: fmtnum(sb, obj->u.number); break;
+ case JS_CSTRING: fmtstr(sb, obj->u.s.string); break;
+ case JS_CBOOLEAN: sb_puts(sb, obj->u.boolean ? "true" : "false"); break;
+ case JS_CARRAY: fmtarray(J, sb); break;
+ default: fmtobject(J, sb, obj); break;
+ }
+ }
+ else if (js_isboolean(J, -1))
+ sb_puts(sb, js_toboolean(J, -1) ? "true" : "false");
+ else if (js_isnumber(J, -1))
+ fmtnum(sb, js_tonumber(J, -1));
+ else if (js_isstring(J, -1))
+ fmtstr(sb, js_tostring(J, -1));
+ else if (js_isnull(J, -1))
+ sb_puts(sb, "null");
+ else
+ return 0;
+
+ return 1;
+}
+
static int JSON_stringify(js_State *J, int argc)
{
+ js_Buffer *sb = NULL;
+ if (argc > 0) {
+ js_copy(J, 1);
+ if (fmtvalue(J, &sb, "")) {
+ sb_putc(&sb, 0);
+ if (js_try(J)) {
+ free(sb);
+ js_throw(J);
+ }
+ js_pushstring(J, sb ? sb->s : "");
+ js_endtry(J);
+ free(sb);
+ return 1;
+ }
+ }
return 0;
}