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