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