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