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