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 }