Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/mujs/json.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/mujs/json.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,422 @@ +#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); +}
