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