ntdll: Make wine_build a hidden symbol.
[wine.git] / dlls / jscript / json.c
blobf2fbb80dc099b5a7320532a02bbabe7de9867626
1 /*
2 * Copyright 2016 Jacek Caban for CodeWeavers
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.
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.
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
19 #include <math.h>
20 #include <assert.h>
22 #include "jscript.h"
23 #include "parser.h"
25 #include "wine/debug.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
29 typedef struct {
30 const WCHAR *ptr;
31 const WCHAR *end;
32 script_ctx_t *ctx;
33 } json_parse_ctx_t;
35 static BOOL is_json_space(WCHAR c)
37 return c == ' ' || c == '\t' || c == '\n' || c == '\r';
40 static WCHAR skip_spaces(json_parse_ctx_t *ctx)
42 while(is_json_space(*ctx->ptr))
43 ctx->ptr++;
44 return *ctx->ptr;
47 static BOOL is_keyword(json_parse_ctx_t *ctx, const WCHAR *keyword)
49 unsigned i;
50 for(i=0; keyword[i]; i++) {
51 if(!ctx->ptr[i] || keyword[i] != ctx->ptr[i])
52 return FALSE;
54 if(is_identifier_char(ctx->ptr[i]))
55 return FALSE;
56 ctx->ptr += i;
57 return TRUE;
60 /* ECMA-262 5.1 Edition 15.12.1.1 */
61 static HRESULT parse_json_string(json_parse_ctx_t *ctx, WCHAR **r)
63 const WCHAR *ptr = ++ctx->ptr;
64 size_t len;
65 WCHAR *buf;
67 while(*ctx->ptr && *ctx->ptr != '"') {
68 if(*ctx->ptr++ == '\\')
69 ctx->ptr++;
71 if(!*ctx->ptr) {
72 FIXME("unterminated string\n");
73 return E_FAIL;
76 len = ctx->ptr-ptr;
77 buf = heap_alloc((len+1)*sizeof(WCHAR));
78 if(!buf)
79 return E_OUTOFMEMORY;
80 if(len)
81 memcpy(buf, ptr, len*sizeof(WCHAR));
83 if(!unescape(buf, &len)) {
84 FIXME("unescape failed\n");
85 heap_free(buf);
86 return E_FAIL;
89 buf[len] = 0;
90 ctx->ptr++;
91 *r = buf;
92 return S_OK;
95 /* ECMA-262 5.1 Edition 15.12.1.2 */
96 static HRESULT parse_json_value(json_parse_ctx_t *ctx, jsval_t *r)
98 HRESULT hres;
100 switch(skip_spaces(ctx)) {
102 /* JSONNullLiteral */
103 case 'n':
104 if(!is_keyword(ctx, L"null"))
105 break;
106 *r = jsval_null();
107 return S_OK;
109 /* JSONBooleanLiteral */
110 case 't':
111 if(!is_keyword(ctx, L"true"))
112 break;
113 *r = jsval_bool(TRUE);
114 return S_OK;
115 case 'f':
116 if(!is_keyword(ctx, L"false"))
117 break;
118 *r = jsval_bool(FALSE);
119 return S_OK;
121 /* JSONObject */
122 case '{': {
123 WCHAR *prop_name;
124 jsdisp_t *obj;
125 jsval_t val;
127 hres = create_object(ctx->ctx, NULL, &obj);
128 if(FAILED(hres))
129 return hres;
131 ctx->ptr++;
132 if(skip_spaces(ctx) == '}') {
133 ctx->ptr++;
134 *r = jsval_obj(obj);
135 return S_OK;
138 while(1) {
139 if(*ctx->ptr != '"')
140 break;
141 hres = parse_json_string(ctx, &prop_name);
142 if(FAILED(hres))
143 break;
145 if(skip_spaces(ctx) != ':') {
146 FIXME("missing ':'\n");
147 heap_free(prop_name);
148 break;
151 ctx->ptr++;
152 hres = parse_json_value(ctx, &val);
153 if(SUCCEEDED(hres)) {
154 hres = jsdisp_propput_name(obj, prop_name, val);
155 jsval_release(val);
157 heap_free(prop_name);
158 if(FAILED(hres))
159 break;
161 if(skip_spaces(ctx) == '}') {
162 ctx->ptr++;
163 *r = jsval_obj(obj);
164 return S_OK;
167 if(*ctx->ptr++ != ',') {
168 FIXME("expected ','\n");
169 break;
171 skip_spaces(ctx);
174 jsdisp_release(obj);
175 break;
178 /* JSONString */
179 case '"': {
180 WCHAR *string;
181 jsstr_t *str;
183 hres = parse_json_string(ctx, &string);
184 if(FAILED(hres))
185 return hres;
187 /* FIXME: avoid reallocation */
188 str = jsstr_alloc(string);
189 heap_free(string);
190 if(!str)
191 return E_OUTOFMEMORY;
193 *r = jsval_string(str);
194 return S_OK;
197 /* JSONArray */
198 case '[': {
199 jsdisp_t *array;
200 unsigned i = 0;
201 jsval_t val;
203 hres = create_array(ctx->ctx, 0, &array);
204 if(FAILED(hres))
205 return hres;
207 ctx->ptr++;
208 if(skip_spaces(ctx) == ']') {
209 ctx->ptr++;
210 *r = jsval_obj(array);
211 return S_OK;
214 while(1) {
215 hres = parse_json_value(ctx, &val);
216 if(FAILED(hres))
217 break;
219 hres = jsdisp_propput_idx(array, i, val);
220 jsval_release(val);
221 if(FAILED(hres))
222 break;
224 if(skip_spaces(ctx) == ']') {
225 ctx->ptr++;
226 *r = jsval_obj(array);
227 return S_OK;
230 if(*ctx->ptr != ',') {
231 FIXME("expected ','\n");
232 break;
235 ctx->ptr++;
236 i++;
239 jsdisp_release(array);
240 break;
243 /* JSONNumber */
244 default: {
245 int sign = 1;
246 double n;
248 if(*ctx->ptr == '-') {
249 sign = -1;
250 ctx->ptr++;
251 skip_spaces(ctx);
254 if(*ctx->ptr == '0' && ctx->ptr + 1 < ctx->end && is_digit(ctx->ptr[1]))
255 break;
257 hres = parse_decimal(&ctx->ptr, ctx->end, &n);
258 if(FAILED(hres))
259 break;
261 *r = jsval_number(sign*n);
262 return S_OK;
266 FIXME("Syntax error at %s\n", debugstr_w(ctx->ptr));
267 return E_FAIL;
270 /* ECMA-262 5.1 Edition 15.12.2 */
271 static HRESULT JSON_parse(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
273 json_parse_ctx_t parse_ctx;
274 const WCHAR *buf;
275 jsstr_t *str;
276 jsval_t ret;
277 HRESULT hres;
279 if(argc != 1) {
280 FIXME("Unsupported args\n");
281 return E_INVALIDARG;
284 hres = to_flat_string(ctx, argv[0], &str, &buf);
285 if(FAILED(hres))
286 return hres;
288 TRACE("%s\n", debugstr_w(buf));
290 parse_ctx.ptr = buf;
291 parse_ctx.end = buf + jsstr_length(str);
292 parse_ctx.ctx = ctx;
293 hres = parse_json_value(&parse_ctx, &ret);
294 if(SUCCEEDED(hres) && skip_spaces(&parse_ctx)) {
295 FIXME("syntax error\n");
296 hres = E_FAIL;
298 jsstr_release(str);
299 if(FAILED(hres))
300 return hres;
302 if(r)
303 *r = ret;
304 else
305 jsval_release(ret);
306 return S_OK;
309 typedef struct {
310 script_ctx_t *ctx;
312 WCHAR *buf;
313 size_t buf_size;
314 size_t buf_len;
316 jsdisp_t **stack;
317 size_t stack_top;
318 size_t stack_size;
320 WCHAR gap[11]; /* according to the spec, it's no longer than 10 chars */
322 jsdisp_t *replacer;
323 } stringify_ctx_t;
325 static BOOL stringify_push_obj(stringify_ctx_t *ctx, jsdisp_t *obj)
327 if(!ctx->stack_size) {
328 ctx->stack = heap_alloc(4*sizeof(*ctx->stack));
329 if(!ctx->stack)
330 return FALSE;
331 ctx->stack_size = 4;
332 }else if(ctx->stack_top == ctx->stack_size) {
333 jsdisp_t **new_stack;
335 new_stack = heap_realloc(ctx->stack, ctx->stack_size*2*sizeof(*ctx->stack));
336 if(!new_stack)
337 return FALSE;
338 ctx->stack = new_stack;
339 ctx->stack_size *= 2;
342 ctx->stack[ctx->stack_top++] = obj;
343 return TRUE;
346 static void stringify_pop_obj(stringify_ctx_t *ctx)
348 ctx->stack_top--;
351 static BOOL is_on_stack(stringify_ctx_t *ctx, jsdisp_t *obj)
353 size_t i = ctx->stack_top;
354 while(i--) {
355 if(ctx->stack[i] == obj)
356 return TRUE;
358 return FALSE;
361 static BOOL append_string_len(stringify_ctx_t *ctx, const WCHAR *str, size_t len)
363 if(!ctx->buf_size) {
364 ctx->buf = heap_alloc(len*2*sizeof(WCHAR));
365 if(!ctx->buf)
366 return FALSE;
367 ctx->buf_size = len*2;
368 }else if(ctx->buf_len + len > ctx->buf_size) {
369 WCHAR *new_buf;
370 size_t new_size;
372 new_size = ctx->buf_size * 2 + len;
373 new_buf = heap_realloc(ctx->buf, new_size*sizeof(WCHAR));
374 if(!new_buf)
375 return FALSE;
376 ctx->buf = new_buf;
377 ctx->buf_size = new_size;
380 if(len)
381 memcpy(ctx->buf + ctx->buf_len, str, len*sizeof(WCHAR));
382 ctx->buf_len += len;
383 return TRUE;
386 static inline BOOL append_string(stringify_ctx_t *ctx, const WCHAR *str)
388 return append_string_len(ctx, str, lstrlenW(str));
391 static inline BOOL append_char(stringify_ctx_t *ctx, WCHAR c)
393 return append_string_len(ctx, &c, 1);
396 static inline BOOL append_simple_quote(stringify_ctx_t *ctx, WCHAR c)
398 WCHAR str[] = {'\\',c};
399 return append_string_len(ctx, str, 2);
402 static HRESULT maybe_to_primitive(script_ctx_t *ctx, jsval_t val, jsval_t *r)
404 jsdisp_t *obj;
405 HRESULT hres;
407 if(!is_object_instance(val) || !get_object(val) || !(obj = iface_to_jsdisp(get_object(val))))
408 return jsval_copy(val, r);
410 if(is_class(obj, JSCLASS_NUMBER)) {
411 double n;
412 hres = to_number(ctx, val, &n);
413 jsdisp_release(obj);
414 if(SUCCEEDED(hres))
415 *r = jsval_number(n);
416 return hres;
419 if(is_class(obj, JSCLASS_STRING)) {
420 jsstr_t *str;
421 hres = to_string(ctx, val, &str);
422 jsdisp_release(obj);
423 if(SUCCEEDED(hres))
424 *r = jsval_string(str);
425 return hres;
428 if(is_class(obj, JSCLASS_BOOLEAN)) {
429 *r = jsval_bool(bool_obj_value(obj));
430 jsdisp_release(obj);
431 return S_OK;
434 *r = jsval_obj(obj);
435 return S_OK;
438 /* ECMA-262 5.1 Edition 15.12.3 (abstract operation Quote) */
439 static HRESULT json_quote(stringify_ctx_t *ctx, const WCHAR *ptr, size_t len)
441 if(!ptr || !append_char(ctx, '"'))
442 return E_OUTOFMEMORY;
444 while(len--) {
445 switch(*ptr) {
446 case '"':
447 case '\\':
448 if(!append_simple_quote(ctx, *ptr))
449 return E_OUTOFMEMORY;
450 break;
451 case '\b':
452 if(!append_simple_quote(ctx, 'b'))
453 return E_OUTOFMEMORY;
454 break;
455 case '\f':
456 if(!append_simple_quote(ctx, 'f'))
457 return E_OUTOFMEMORY;
458 break;
459 case '\n':
460 if(!append_simple_quote(ctx, 'n'))
461 return E_OUTOFMEMORY;
462 break;
463 case '\r':
464 if(!append_simple_quote(ctx, 'r'))
465 return E_OUTOFMEMORY;
466 break;
467 case '\t':
468 if(!append_simple_quote(ctx, 't'))
469 return E_OUTOFMEMORY;
470 break;
471 default:
472 if(*ptr < ' ') {
473 WCHAR buf[7];
474 swprintf(buf, ARRAY_SIZE(buf), L"\\u%04x", *ptr);
475 if(!append_string(ctx, buf))
476 return E_OUTOFMEMORY;
477 }else {
478 if(!append_char(ctx, *ptr))
479 return E_OUTOFMEMORY;
482 ptr++;
485 return append_char(ctx, '"') ? S_OK : E_OUTOFMEMORY;
488 static inline BOOL is_callable(jsdisp_t *obj)
490 return is_class(obj, JSCLASS_FUNCTION);
493 static HRESULT stringify(stringify_ctx_t *ctx, jsdisp_t *object, const WCHAR *name);
495 /* ECMA-262 5.1 Edition 15.12.3 (abstract operation JA) */
496 static HRESULT stringify_array(stringify_ctx_t *ctx, jsdisp_t *obj)
498 unsigned length, i, j;
499 WCHAR name[16];
500 HRESULT hres;
502 if(is_on_stack(ctx, obj)) {
503 FIXME("Found a cycle\n");
504 return E_FAIL;
507 if(!stringify_push_obj(ctx, obj))
508 return E_OUTOFMEMORY;
510 if(!append_char(ctx, '['))
511 return E_OUTOFMEMORY;
513 length = array_get_length(obj);
515 for(i=0; i < length; i++) {
516 if(i && !append_char(ctx, ','))
517 return E_OUTOFMEMORY;
519 if(*ctx->gap) {
520 if(!append_char(ctx, '\n'))
521 return E_OUTOFMEMORY;
523 for(j=0; j < ctx->stack_top; j++) {
524 if(!append_string(ctx, ctx->gap))
525 return E_OUTOFMEMORY;
529 _itow(i, name, ARRAY_SIZE(name));
530 hres = stringify(ctx, obj, name);
531 if(FAILED(hres))
532 return hres;
533 if(hres == S_FALSE && !append_string(ctx, L"null"))
534 return E_OUTOFMEMORY;
537 if((length && *ctx->gap && !append_char(ctx, '\n')) || !append_char(ctx, ']'))
538 return E_OUTOFMEMORY;
540 stringify_pop_obj(ctx);
541 return S_OK;
544 /* ECMA-262 5.1 Edition 15.12.3 (abstract operation JO) */
545 static HRESULT stringify_object(stringify_ctx_t *ctx, jsdisp_t *obj)
547 DISPID dispid = DISPID_STARTENUM;
548 unsigned prop_cnt = 0, i;
549 size_t stepback;
550 BSTR prop_name;
551 HRESULT hres;
553 if(is_on_stack(ctx, obj)) {
554 FIXME("Found a cycle\n");
555 return E_FAIL;
558 if(!stringify_push_obj(ctx, obj))
559 return E_OUTOFMEMORY;
561 if(!append_char(ctx, '{'))
562 return E_OUTOFMEMORY;
564 while((hres = IDispatchEx_GetNextDispID(&obj->IDispatchEx_iface, fdexEnumDefault, dispid, &dispid)) == S_OK) {
565 stepback = ctx->buf_len;
567 if(prop_cnt && !append_char(ctx, ',')) {
568 hres = E_OUTOFMEMORY;
569 break;
572 if(*ctx->gap) {
573 if(!append_char(ctx, '\n')) {
574 hres = E_OUTOFMEMORY;
575 break;
578 for(i=0; i < ctx->stack_top; i++) {
579 if(!append_string(ctx, ctx->gap)) {
580 hres = E_OUTOFMEMORY;
581 break;
586 hres = IDispatchEx_GetMemberName(&obj->IDispatchEx_iface, dispid, &prop_name);
587 if(FAILED(hres))
588 return hres;
590 hres = json_quote(ctx, prop_name, SysStringLen(prop_name));
591 if(FAILED(hres)) {
592 SysFreeString(prop_name);
593 return hres;
596 if(!append_char(ctx, ':') || (*ctx->gap && !append_char(ctx, ' '))) {
597 SysFreeString(prop_name);
598 return E_OUTOFMEMORY;
601 hres = stringify(ctx, obj, prop_name);
602 SysFreeString(prop_name);
603 if(FAILED(hres))
604 return hres;
606 if(hres == S_FALSE) {
607 ctx->buf_len = stepback;
608 continue;
611 prop_cnt++;
614 if(prop_cnt && *ctx->gap) {
615 if(!append_char(ctx, '\n'))
616 return E_OUTOFMEMORY;
618 for(i=1; i < ctx->stack_top; i++) {
619 if(!append_string(ctx, ctx->gap)) {
620 hres = E_OUTOFMEMORY;
621 break;
626 if(!append_char(ctx, '}'))
627 return E_OUTOFMEMORY;
629 stringify_pop_obj(ctx);
630 return S_OK;
633 /* ECMA-262 5.1 Edition 15.12.3 (abstract operation Str) */
634 static HRESULT stringify(stringify_ctx_t *ctx, jsdisp_t *object, const WCHAR *name)
636 jsval_t value, v;
637 HRESULT hres;
639 hres = jsdisp_propget_name(object, name, &value);
640 if(FAILED(hres))
641 return hres == DISP_E_UNKNOWNNAME ? S_FALSE : hres;
643 if(is_object_instance(value) && get_object(value)) {
644 jsdisp_t *obj;
645 DISPID id;
647 obj = iface_to_jsdisp(get_object(value));
648 if(!obj) {
649 jsval_release(value);
650 return S_FALSE;
653 hres = jsdisp_get_id(obj, L"toJSON", 0, &id);
654 jsdisp_release(obj);
655 if(hres == S_OK)
656 FIXME("Use toJSON.\n");
659 if(ctx->replacer) {
660 jsstr_t *name_str;
661 jsval_t args[2];
662 if(!(name_str = jsstr_alloc(name))) {
663 jsval_release(value);
664 return E_OUTOFMEMORY;
666 args[0] = jsval_string(name_str);
667 args[1] = value;
668 hres = jsdisp_call_value(ctx->replacer, to_disp(object), DISPATCH_METHOD, ARRAY_SIZE(args), args, &v);
669 jsstr_release(name_str);
670 jsval_release(value);
671 if(FAILED(hres))
672 return hres;
673 value = v;
676 v = value;
677 hres = maybe_to_primitive(ctx->ctx, v, &value);
678 jsval_release(v);
679 if(FAILED(hres))
680 return hres;
682 switch(jsval_type(value)) {
683 case JSV_NULL:
684 if(!append_string(ctx, L"null"))
685 hres = E_OUTOFMEMORY;
686 break;
687 case JSV_BOOL:
688 if(!append_string(ctx, get_bool(value) ? L"true" : L"false"))
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;
700 case JSV_NUMBER: {
701 double n = get_number(value);
702 if(isfinite(n)) {
703 const WCHAR *ptr;
704 jsstr_t *str;
706 /* FIXME: Optimize. There is no need for jsstr_t here. */
707 hres = double_to_string(n, &str);
708 if(FAILED(hres))
709 break;
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, L"null"))
717 hres = E_OUTOFMEMORY;
719 break;
721 case JSV_OBJECT: {
722 jsdisp_t *obj;
724 obj = iface_to_jsdisp(get_object(value));
725 if(!obj) {
726 hres = S_FALSE;
727 break;
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;
735 jsdisp_release(obj);
736 break;
738 case JSV_UNDEFINED:
739 hres = S_FALSE;
740 break;
741 case JSV_VARIANT:
742 FIXME("VARIANT\n");
743 hres = E_NOTIMPL;
744 break;
747 jsval_release(value);
748 return hres;
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)
754 stringify_ctx_t stringify_ctx = { ctx };
755 jsdisp_t *obj = NULL, *replacer;
756 HRESULT hres;
758 TRACE("\n");
760 if(!argc) {
761 if(r)
762 *r = jsval_undefined();
763 return S_OK;
766 if(argc >= 2 && is_object_instance(argv[1]) && get_object(argv[1]) &&
767 (replacer = to_jsdisp(get_object(argv[1])))) {
768 if(is_callable(replacer)) {
769 stringify_ctx.replacer = jsdisp_addref(replacer);
770 }else if(is_class(replacer, JSCLASS_ARRAY)) {
771 FIXME("Array replacer not yet supported\n");
772 return E_NOTIMPL;
776 if(argc >= 3) {
777 jsval_t space_val;
779 hres = maybe_to_primitive(ctx, argv[2], &space_val);
780 if(FAILED(hres))
781 goto fail;
783 if(is_number(space_val)) {
784 double n = get_number(space_val);
785 if(n >= 1) {
786 int i, len;
787 if(n > 10)
788 n = 10;
789 len = floor(n);
790 for(i=0; i < len; i++)
791 stringify_ctx.gap[i] = ' ';
792 stringify_ctx.gap[len] = 0;
794 }else if(is_string(space_val)) {
795 jsstr_t *space_str = get_string(space_val);
796 size_t len = jsstr_length(space_str);
797 if(len > 10)
798 len = 10;
799 jsstr_extract(space_str, 0, len, stringify_ctx.gap);
802 jsval_release(space_val);
805 if(FAILED(hres = create_object(ctx, NULL, &obj)))
806 goto fail;
807 if(FAILED(hres = jsdisp_propput_name(obj, L"", argv[0])))
808 goto fail;
810 hres = stringify(&stringify_ctx, obj, L"");
811 if(SUCCEEDED(hres) && r) {
812 assert(!stringify_ctx.stack_top);
814 if(hres == S_OK) {
815 jsstr_t *ret = jsstr_alloc_len(stringify_ctx.buf, stringify_ctx.buf_len);
816 if(ret)
817 *r = jsval_string(ret);
818 else
819 hres = E_OUTOFMEMORY;
820 }else {
821 *r = jsval_undefined();
825 fail:
826 if(obj)
827 jsdisp_release(obj);
828 if(stringify_ctx.replacer)
829 jsdisp_release(stringify_ctx.replacer);
830 heap_free(stringify_ctx.buf);
831 heap_free(stringify_ctx.stack);
832 return hres;
835 static const builtin_prop_t JSON_props[] = {
836 {L"parse", JSON_parse, PROPF_METHOD|2},
837 {L"stringify", JSON_stringify, PROPF_METHOD|3}
840 static const builtin_info_t JSON_info = {
841 JSCLASS_JSON,
842 {NULL, NULL, 0},
843 ARRAY_SIZE(JSON_props),
844 JSON_props,
845 NULL,
846 NULL
849 HRESULT create_json(script_ctx_t *ctx, jsdisp_t **ret)
851 jsdisp_t *json;
852 HRESULT hres;
854 json = heap_alloc_zero(sizeof(*json));
855 if(!json)
856 return E_OUTOFMEMORY;
858 hres = init_dispex_from_constr(json, ctx, &JSON_info, ctx->object_constr);
859 if(FAILED(hres)) {
860 heap_free(json);
861 return hres;
864 *ret = json;
865 return S_OK;