[JSCRIPT] Sync with Wine Staging 3.9. CORE-14656
[reactos.git] / dll / win32 / jscript / json.c
1 /*
2 * Copyright 2016 Jacek Caban for CodeWeavers
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include <math.h>
20 #include <assert.h>
21
22 #include "jscript.h"
23 #include "parser.h"
24
25 #include "wine/debug.h"
26 #include "wine/unicode.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
29
30 static const WCHAR parseW[] = {'p','a','r','s','e',0};
31 static const WCHAR stringifyW[] = {'s','t','r','i','n','g','i','f','y',0};
32
33 static const WCHAR nullW[] = {'n','u','l','l',0};
34 static const WCHAR trueW[] = {'t','r','u','e',0};
35 static const WCHAR falseW[] = {'f','a','l','s','e',0};
36
37 static const WCHAR toJSONW[] = {'t','o','J','S','O','N',0};
38
39 typedef struct {
40 const WCHAR *ptr;
41 const WCHAR *end;
42 script_ctx_t *ctx;
43 } json_parse_ctx_t;
44
45 static BOOL is_json_space(WCHAR c)
46 {
47 return c == ' ' || c == '\t' || c == '\n' || c == '\r';
48 }
49
50 static WCHAR skip_spaces(json_parse_ctx_t *ctx)
51 {
52 while(is_json_space(*ctx->ptr))
53 ctx->ptr++;
54 return *ctx->ptr;
55 }
56
57 static BOOL is_keyword(json_parse_ctx_t *ctx, const WCHAR *keyword)
58 {
59 unsigned i;
60 for(i=0; keyword[i]; i++) {
61 if(!ctx->ptr[i] || keyword[i] != ctx->ptr[i])
62 return FALSE;
63 }
64 if(is_identifier_char(ctx->ptr[i]))
65 return FALSE;
66 ctx->ptr += i;
67 return TRUE;
68 }
69
70 /* ECMA-262 5.1 Edition 15.12.1.1 */
71 static HRESULT parse_json_string(json_parse_ctx_t *ctx, WCHAR **r)
72 {
73 const WCHAR *ptr = ++ctx->ptr;
74 size_t len;
75 WCHAR *buf;
76
77 while(*ctx->ptr && *ctx->ptr != '"') {
78 if(*ctx->ptr++ == '\\')
79 ctx->ptr++;
80 }
81 if(!*ctx->ptr) {
82 FIXME("unterminated string\n");
83 return E_FAIL;
84 }
85
86 len = ctx->ptr-ptr;
87 buf = heap_alloc((len+1)*sizeof(WCHAR));
88 if(!buf)
89 return E_OUTOFMEMORY;
90 if(len)
91 memcpy(buf, ptr, len*sizeof(WCHAR));
92 buf[len] = 0;
93
94 if(!unescape(buf)) {
95 FIXME("unescape failed\n");
96 heap_free(buf);
97 return E_FAIL;
98 }
99
100 ctx->ptr++;
101 *r = buf;
102 return S_OK;
103 }
104
105 /* ECMA-262 5.1 Edition 15.12.1.2 */
106 static HRESULT parse_json_value(json_parse_ctx_t *ctx, jsval_t *r)
107 {
108 HRESULT hres;
109
110 switch(skip_spaces(ctx)) {
111
112 /* JSONNullLiteral */
113 case 'n':
114 if(!is_keyword(ctx, nullW))
115 break;
116 *r = jsval_null();
117 return S_OK;
118
119 /* JSONBooleanLiteral */
120 case 't':
121 if(!is_keyword(ctx, trueW))
122 break;
123 *r = jsval_bool(TRUE);
124 return S_OK;
125 case 'f':
126 if(!is_keyword(ctx, falseW))
127 break;
128 *r = jsval_bool(FALSE);
129 return S_OK;
130
131 /* JSONObject */
132 case '{': {
133 WCHAR *prop_name;
134 jsdisp_t *obj;
135 jsval_t val;
136
137 hres = create_object(ctx->ctx, NULL, &obj);
138 if(FAILED(hres))
139 return hres;
140
141 ctx->ptr++;
142 if(skip_spaces(ctx) == '}') {
143 ctx->ptr++;
144 *r = jsval_obj(obj);
145 return S_OK;
146 }
147
148 while(1) {
149 if(*ctx->ptr != '"')
150 break;
151 hres = parse_json_string(ctx, &prop_name);
152 if(FAILED(hres))
153 break;
154
155 if(skip_spaces(ctx) != ':') {
156 FIXME("missing ':'\n");
157 heap_free(prop_name);
158 break;
159 }
160
161 ctx->ptr++;
162 hres = parse_json_value(ctx, &val);
163 if(SUCCEEDED(hres)) {
164 hres = jsdisp_propput_name(obj, prop_name, val);
165 jsval_release(val);
166 }
167 heap_free(prop_name);
168 if(FAILED(hres))
169 break;
170
171 if(skip_spaces(ctx) == '}') {
172 ctx->ptr++;
173 *r = jsval_obj(obj);
174 return S_OK;
175 }
176
177 if(*ctx->ptr++ != ',') {
178 FIXME("expected ','\n");
179 break;
180 }
181 skip_spaces(ctx);
182 }
183
184 jsdisp_release(obj);
185 break;
186 }
187
188 /* JSONString */
189 case '"': {
190 WCHAR *string;
191 jsstr_t *str;
192
193 hres = parse_json_string(ctx, &string);
194 if(FAILED(hres))
195 return hres;
196
197 /* FIXME: avoid reallocation */
198 str = jsstr_alloc(string);
199 heap_free(string);
200 if(!str)
201 return E_OUTOFMEMORY;
202
203 *r = jsval_string(str);
204 return S_OK;
205 }
206
207 /* JSONArray */
208 case '[': {
209 jsdisp_t *array;
210 unsigned i = 0;
211 jsval_t val;
212
213 hres = create_array(ctx->ctx, 0, &array);
214 if(FAILED(hres))
215 return hres;
216
217 ctx->ptr++;
218 if(skip_spaces(ctx) == ']') {
219 ctx->ptr++;
220 *r = jsval_obj(array);
221 return S_OK;
222 }
223
224 while(1) {
225 hres = parse_json_value(ctx, &val);
226 if(FAILED(hres))
227 break;
228
229 hres = jsdisp_propput_idx(array, i, val);
230 jsval_release(val);
231 if(FAILED(hres))
232 break;
233
234 if(skip_spaces(ctx) == ']') {
235 ctx->ptr++;
236 *r = jsval_obj(array);
237 return S_OK;
238 }
239
240 if(*ctx->ptr != ',') {
241 FIXME("expected ','\n");
242 break;
243 }
244
245 ctx->ptr++;
246 i++;
247 }
248
249 jsdisp_release(array);
250 break;
251 }
252
253 /* JSONNumber */
254 default: {
255 int sign = 1;
256 double n;
257
258 if(*ctx->ptr == '-') {
259 sign = -1;
260 ctx->ptr++;
261 skip_spaces(ctx);
262 }
263
264 if(!isdigitW(*ctx->ptr))
265 break;
266
267 if(*ctx->ptr == '0') {
268 ctx->ptr++;
269 n = 0;
270 if(is_identifier_char(*ctx->ptr))
271 break;
272 }else {
273 hres = parse_decimal(&ctx->ptr, ctx->end, &n);
274 if(FAILED(hres))
275 return hres;
276 }
277
278 *r = jsval_number(sign*n);
279 return S_OK;
280 }
281 }
282
283 FIXME("Syntax error at %s\n", debugstr_w(ctx->ptr));
284 return E_FAIL;
285 }
286
287 /* ECMA-262 5.1 Edition 15.12.2 */
288 static HRESULT JSON_parse(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
289 {
290 json_parse_ctx_t parse_ctx;
291 const WCHAR *buf;
292 jsstr_t *str;
293 jsval_t ret;
294 HRESULT hres;
295
296 if(argc != 1) {
297 FIXME("Unsupported args\n");
298 return E_INVALIDARG;
299 }
300
301 hres = to_flat_string(ctx, argv[0], &str, &buf);
302 if(FAILED(hres))
303 return hres;
304
305 TRACE("%s\n", debugstr_w(buf));
306
307 parse_ctx.ptr = buf;
308 parse_ctx.end = buf + jsstr_length(str);
309 parse_ctx.ctx = ctx;
310 hres = parse_json_value(&parse_ctx, &ret);
311 jsstr_release(str);
312 if(FAILED(hres))
313 return hres;
314
315 if(skip_spaces(&parse_ctx)) {
316 FIXME("syntax error\n");
317 jsval_release(ret);
318 return E_FAIL;
319 }
320
321 if(r)
322 *r = ret;
323 else
324 jsval_release(ret);
325 return S_OK;
326 }
327
328 typedef struct {
329 script_ctx_t *ctx;
330
331 WCHAR *buf;
332 size_t buf_size;
333 size_t buf_len;
334
335 jsdisp_t **stack;
336 size_t stack_top;
337 size_t stack_size;
338
339 WCHAR gap[11]; /* according to the spec, it's no longer than 10 chars */
340 } stringify_ctx_t;
341
342 static BOOL stringify_push_obj(stringify_ctx_t *ctx, jsdisp_t *obj)
343 {
344 if(!ctx->stack_size) {
345 ctx->stack = heap_alloc(4*sizeof(*ctx->stack));
346 if(!ctx->stack)
347 return FALSE;
348 ctx->stack_size = 4;
349 }else if(ctx->stack_top == ctx->stack_size) {
350 jsdisp_t **new_stack;
351
352 new_stack = heap_realloc(ctx->stack, ctx->stack_size*2*sizeof(*ctx->stack));
353 if(!new_stack)
354 return FALSE;
355 ctx->stack = new_stack;
356 ctx->stack_size *= 2;
357 }
358
359 ctx->stack[ctx->stack_top++] = obj;
360 return TRUE;
361 }
362
363 static void stringify_pop_obj(stringify_ctx_t *ctx)
364 {
365 ctx->stack_top--;
366 }
367
368 static BOOL is_on_stack(stringify_ctx_t *ctx, jsdisp_t *obj)
369 {
370 size_t i = ctx->stack_top;
371 while(i--) {
372 if(ctx->stack[i] == obj)
373 return TRUE;
374 }
375 return FALSE;
376 }
377
378 static BOOL append_string_len(stringify_ctx_t *ctx, const WCHAR *str, size_t len)
379 {
380 if(!ctx->buf_size) {
381 ctx->buf = heap_alloc(len*2*sizeof(WCHAR));
382 if(!ctx->buf)
383 return FALSE;
384 ctx->buf_size = len*2;
385 }else if(ctx->buf_len + len > ctx->buf_size) {
386 WCHAR *new_buf;
387 size_t new_size;
388
389 new_size = ctx->buf_size * 2 + len;
390 new_buf = heap_realloc(ctx->buf, new_size*sizeof(WCHAR));
391 if(!new_buf)
392 return FALSE;
393 ctx->buf = new_buf;
394 ctx->buf_size = new_size;
395 }
396
397 if(len)
398 memcpy(ctx->buf + ctx->buf_len, str, len*sizeof(WCHAR));
399 ctx->buf_len += len;
400 return TRUE;
401 }
402
403 static inline BOOL append_string(stringify_ctx_t *ctx, const WCHAR *str)
404 {
405 return append_string_len(ctx, str, strlenW(str));
406 }
407
408 static inline BOOL append_char(stringify_ctx_t *ctx, WCHAR c)
409 {
410 return append_string_len(ctx, &c, 1);
411 }
412
413 static inline BOOL append_simple_quote(stringify_ctx_t *ctx, WCHAR c)
414 {
415 WCHAR str[] = {'\\',c};
416 return append_string_len(ctx, str, 2);
417 }
418
419 static HRESULT maybe_to_primitive(script_ctx_t *ctx, jsval_t val, jsval_t *r)
420 {
421 jsdisp_t *obj;
422 HRESULT hres;
423
424 if(!is_object_instance(val) || !get_object(val) || !(obj = iface_to_jsdisp(get_object(val))))
425 return jsval_copy(val, r);
426
427 if(is_class(obj, JSCLASS_NUMBER)) {
428 double n;
429 hres = to_number(ctx, val, &n);
430 jsdisp_release(obj);
431 if(SUCCEEDED(hres))
432 *r = jsval_number(n);
433 return hres;
434 }
435
436 if(is_class(obj, JSCLASS_STRING)) {
437 jsstr_t *str;
438 hres = to_string(ctx, val, &str);
439 jsdisp_release(obj);
440 if(SUCCEEDED(hres))
441 *r = jsval_string(str);
442 return hres;
443 }
444
445 if(is_class(obj, JSCLASS_BOOLEAN)) {
446 *r = jsval_bool(bool_obj_value(obj));
447 jsdisp_release(obj);
448 return S_OK;
449 }
450
451 *r = jsval_obj(obj);
452 return S_OK;
453 }
454
455 /* ECMA-262 5.1 Edition 15.12.3 (abstract operation Quote) */
456 static HRESULT json_quote(stringify_ctx_t *ctx, const WCHAR *ptr, size_t len)
457 {
458 if(!ptr || !append_char(ctx, '"'))
459 return E_OUTOFMEMORY;
460
461 while(len--) {
462 switch(*ptr) {
463 case '"':
464 case '\\':
465 if(!append_simple_quote(ctx, *ptr))
466 return E_OUTOFMEMORY;
467 break;
468 case '\b':
469 if(!append_simple_quote(ctx, 'b'))
470 return E_OUTOFMEMORY;
471 break;
472 case '\f':
473 if(!append_simple_quote(ctx, 'f'))
474 return E_OUTOFMEMORY;
475 break;
476 case '\n':
477 if(!append_simple_quote(ctx, 'n'))
478 return E_OUTOFMEMORY;
479 break;
480 case '\r':
481 if(!append_simple_quote(ctx, 'r'))
482 return E_OUTOFMEMORY;
483 break;
484 case '\t':
485 if(!append_simple_quote(ctx, 't'))
486 return E_OUTOFMEMORY;
487 break;
488 default:
489 if(*ptr < ' ') {
490 const WCHAR formatW[] = {'\\','u','%','0','4','x',0};
491 WCHAR buf[7];
492 sprintfW(buf, formatW, *ptr);
493 if(!append_string(ctx, buf))
494 return E_OUTOFMEMORY;
495 }else {
496 if(!append_char(ctx, *ptr))
497 return E_OUTOFMEMORY;
498 }
499 }
500 ptr++;
501 }
502
503 return append_char(ctx, '"') ? S_OK : E_OUTOFMEMORY;
504 }
505
506 static inline BOOL is_callable(jsdisp_t *obj)
507 {
508 return is_class(obj, JSCLASS_FUNCTION);
509 }
510
511 static HRESULT stringify(stringify_ctx_t *ctx, jsval_t val);
512
513 /* ECMA-262 5.1 Edition 15.12.3 (abstract operation JA) */
514 static HRESULT stringify_array(stringify_ctx_t *ctx, jsdisp_t *obj)
515 {
516 unsigned length, i, j;
517 jsval_t val;
518 HRESULT hres;
519
520 if(is_on_stack(ctx, obj)) {
521 FIXME("Found a cycle\n");
522 return E_FAIL;
523 }
524
525 if(!stringify_push_obj(ctx, obj))
526 return E_OUTOFMEMORY;
527
528 if(!append_char(ctx, '['))
529 return E_OUTOFMEMORY;
530
531 length = array_get_length(obj);
532
533 for(i=0; i < length; i++) {
534 if(i && !append_char(ctx, ','))
535 return E_OUTOFMEMORY;
536
537 if(*ctx->gap) {
538 if(!append_char(ctx, '\n'))
539 return E_OUTOFMEMORY;
540
541 for(j=0; j < ctx->stack_top; j++) {
542 if(!append_string(ctx, ctx->gap))
543 return E_OUTOFMEMORY;
544 }
545 }
546
547 hres = jsdisp_get_idx(obj, i, &val);
548 if(SUCCEEDED(hres)) {
549 hres = stringify(ctx, val);
550 if(FAILED(hres))
551 return hres;
552 if(hres == S_FALSE && !append_string(ctx, nullW))
553 return E_OUTOFMEMORY;
554 }else if(hres == DISP_E_UNKNOWNNAME) {
555 if(!append_string(ctx, nullW))
556 return E_OUTOFMEMORY;
557 }else {
558 return hres;
559 }
560 }
561
562 if((length && *ctx->gap && !append_char(ctx, '\n')) || !append_char(ctx, ']'))
563 return E_OUTOFMEMORY;
564
565 stringify_pop_obj(ctx);
566 return S_OK;
567 }
568
569 /* ECMA-262 5.1 Edition 15.12.3 (abstract operation JO) */
570 static HRESULT stringify_object(stringify_ctx_t *ctx, jsdisp_t *obj)
571 {
572 DISPID dispid = DISPID_STARTENUM;
573 jsval_t val = jsval_undefined();
574 unsigned prop_cnt = 0, i;
575 size_t stepback;
576 BSTR prop_name;
577 HRESULT hres;
578
579 if(is_on_stack(ctx, obj)) {
580 FIXME("Found a cycle\n");
581 return E_FAIL;
582 }
583
584 if(!stringify_push_obj(ctx, obj))
585 return E_OUTOFMEMORY;
586
587 if(!append_char(ctx, '{'))
588 return E_OUTOFMEMORY;
589
590 while((hres = IDispatchEx_GetNextDispID(&obj->IDispatchEx_iface, fdexEnumDefault, dispid, &dispid)) == S_OK) {
591 jsval_release(val);
592 hres = jsdisp_propget(obj, dispid, &val);
593 if(FAILED(hres))
594 return hres;
595
596 if(is_undefined(val))
597 continue;
598
599 stepback = ctx->buf_len;
600
601 if(prop_cnt && !append_char(ctx, ',')) {
602 hres = E_OUTOFMEMORY;
603 break;
604 }
605
606 if(*ctx->gap) {
607 if(!append_char(ctx, '\n')) {
608 hres = E_OUTOFMEMORY;
609 break;
610 }
611
612 for(i=0; i < ctx->stack_top; i++) {
613 if(!append_string(ctx, ctx->gap)) {
614 hres = E_OUTOFMEMORY;
615 break;
616 }
617 }
618 }
619
620 hres = IDispatchEx_GetMemberName(&obj->IDispatchEx_iface, dispid, &prop_name);
621 if(FAILED(hres))
622 break;
623
624 hres = json_quote(ctx, prop_name, SysStringLen(prop_name));
625 SysFreeString(prop_name);
626 if(FAILED(hres))
627 break;
628
629 if(!append_char(ctx, ':') || (*ctx->gap && !append_char(ctx, ' '))) {
630 hres = E_OUTOFMEMORY;
631 break;
632 }
633
634 hres = stringify(ctx, val);
635 if(FAILED(hres))
636 break;
637
638 if(hres == S_FALSE) {
639 ctx->buf_len = stepback;
640 continue;
641 }
642
643 prop_cnt++;
644 }
645 jsval_release(val);
646 if(FAILED(hres))
647 return hres;
648
649 if(prop_cnt && *ctx->gap) {
650 if(!append_char(ctx, '\n'))
651 return E_OUTOFMEMORY;
652
653 for(i=1; i < ctx->stack_top; i++) {
654 if(!append_string(ctx, ctx->gap)) {
655 hres = E_OUTOFMEMORY;
656 break;
657 }
658 }
659 }
660
661 if(!append_char(ctx, '}'))
662 return E_OUTOFMEMORY;
663
664 stringify_pop_obj(ctx);
665 return S_OK;
666 }
667
668 /* ECMA-262 5.1 Edition 15.12.3 (abstract operation Str) */
669 static HRESULT stringify(stringify_ctx_t *ctx, jsval_t val)
670 {
671 jsval_t value;
672 HRESULT hres;
673
674 if(is_object_instance(val) && get_object(val)) {
675 jsdisp_t *obj;
676 DISPID id;
677
678 obj = iface_to_jsdisp(get_object(val));
679 if(!obj)
680 return S_FALSE;
681
682 hres = jsdisp_get_id(obj, toJSONW, 0, &id);
683 jsdisp_release(obj);
684 if(hres == S_OK)
685 FIXME("Use toJSON.\n");
686 }
687
688 /* FIXME: Support replacer replacer. */
689
690 hres = maybe_to_primitive(ctx->ctx, val, &value);
691 if(FAILED(hres))
692 return hres;
693
694 switch(jsval_type(value)) {
695 case JSV_NULL:
696 if(!append_string(ctx, nullW))
697 hres = E_OUTOFMEMORY;
698 break;
699 case JSV_BOOL:
700 if(!append_string(ctx, get_bool(value) ? trueW : falseW))
701 hres = E_OUTOFMEMORY;
702 break;
703 case JSV_STRING: {
704 jsstr_t *str = get_string(value);
705 const WCHAR *ptr = jsstr_flatten(str);
706 if(ptr)
707 hres = json_quote(ctx, ptr, jsstr_length(str));
708 else
709 hres = E_OUTOFMEMORY;
710 break;
711 }
712 case JSV_NUMBER: {
713 double n = get_number(value);
714 if(is_finite(n)) {
715 const WCHAR *ptr;
716 jsstr_t *str;
717
718 /* FIXME: Optimize. There is no need for jsstr_t here. */
719 hres = double_to_string(n, &str);
720 if(FAILED(hres))
721 break;
722
723 ptr = jsstr_flatten(str);
724 assert(ptr != NULL);
725 hres = ptr && !append_string_len(ctx, ptr, jsstr_length(str)) ? E_OUTOFMEMORY : S_OK;
726 jsstr_release(str);
727 }else {
728 if(!append_string(ctx, nullW))
729 hres = E_OUTOFMEMORY;
730 }
731 break;
732 }
733 case JSV_OBJECT: {
734 jsdisp_t *obj;
735
736 obj = iface_to_jsdisp(get_object(value));
737 if(!obj) {
738 hres = S_FALSE;
739 break;
740 }
741
742 if(!is_callable(obj))
743 hres = is_class(obj, JSCLASS_ARRAY) ? stringify_array(ctx, obj) : stringify_object(ctx, obj);
744 else
745 hres = S_FALSE;
746
747 jsdisp_release(obj);
748 break;
749 }
750 case JSV_UNDEFINED:
751 hres = S_FALSE;
752 break;
753 case JSV_VARIANT:
754 FIXME("VARIANT\n");
755 hres = E_NOTIMPL;
756 break;
757 }
758
759 jsval_release(value);
760 return hres;
761 }
762
763 /* ECMA-262 5.1 Edition 15.12.3 */
764 static HRESULT JSON_stringify(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
765 {
766 stringify_ctx_t stringify_ctx = {ctx, NULL,0,0, NULL,0,0, {0}};
767 HRESULT hres;
768
769 TRACE("\n");
770
771 if(!argc) {
772 if(r)
773 *r = jsval_undefined();
774 return S_OK;
775 }
776
777 if(argc >= 2 && is_object_instance(argv[1])) {
778 FIXME("Replacer %s not yet supported\n", debugstr_jsval(argv[1]));
779 return E_NOTIMPL;
780 }
781
782 if(argc >= 3) {
783 jsval_t space_val;
784
785 hres = maybe_to_primitive(ctx, argv[2], &space_val);
786 if(FAILED(hres))
787 return hres;
788
789 if(is_number(space_val)) {
790 double n = get_number(space_val);
791 if(n >= 1) {
792 int i, len;
793 if(n > 10)
794 n = 10;
795 len = floor(n);
796 for(i=0; i < len; i++)
797 stringify_ctx.gap[i] = ' ';
798 stringify_ctx.gap[len] = 0;
799 }
800 }else if(is_string(space_val)) {
801 jsstr_t *space_str = get_string(space_val);
802 size_t len = jsstr_length(space_str);
803 if(len > 10)
804 len = 10;
805 jsstr_extract(space_str, 0, len, stringify_ctx.gap);
806 }
807
808 jsval_release(space_val);
809 }
810
811 hres = stringify(&stringify_ctx, argv[0]);
812 if(SUCCEEDED(hres) && r) {
813 assert(!stringify_ctx.stack_top);
814
815 if(hres == S_OK) {
816 jsstr_t *ret = jsstr_alloc_len(stringify_ctx.buf, stringify_ctx.buf_len);
817 if(ret)
818 *r = jsval_string(ret);
819 else
820 hres = E_OUTOFMEMORY;
821 }else {
822 *r = jsval_undefined();
823 }
824 }
825
826 heap_free(stringify_ctx.buf);
827 heap_free(stringify_ctx.stack);
828 return hres;
829 }
830
831 static const builtin_prop_t JSON_props[] = {
832 {parseW, JSON_parse, PROPF_METHOD|2},
833 {stringifyW, JSON_stringify, PROPF_METHOD|3}
834 };
835
836 static const builtin_info_t JSON_info = {
837 JSCLASS_JSON,
838 {NULL, NULL, 0},
839 sizeof(JSON_props)/sizeof(*JSON_props),
840 JSON_props,
841 NULL,
842 NULL
843 };
844
845 HRESULT create_json(script_ctx_t *ctx, jsdisp_t **ret)
846 {
847 jsdisp_t *json;
848 HRESULT hres;
849
850 json = heap_alloc_zero(sizeof(*json));
851 if(!json)
852 return E_OUTOFMEMORY;
853
854 hres = init_dispex_from_constr(json, ctx, &JSON_info, ctx->object_constr);
855 if(FAILED(hres)) {
856 heap_free(json);
857 return hres;
858 }
859
860 *ret = json;
861 return S_OK;
862 }