opengl32: Preserve the remainder of the version string when limiting the version...
[wine.git] / dlls / jscript / json.c
blobe07ad86334887c0a82778626409ef6eb13516589
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 = malloc((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 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 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 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 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 struct transform_json_object_ctx
272 script_ctx_t *ctx;
273 IDispatch *reviver;
274 HRESULT hres;
277 static jsval_t transform_json_object(struct transform_json_object_ctx *proc_ctx, jsdisp_t *holder, jsstr_t *name)
279 jsval_t res, args[2];
280 const WCHAR *str;
282 if(!(str = jsstr_flatten(name)))
283 proc_ctx->hres = E_OUTOFMEMORY;
284 else
285 proc_ctx->hres = jsdisp_propget_name(holder, str, &args[1]);
286 if(FAILED(proc_ctx->hres))
287 return jsval_undefined();
289 if(is_object_instance(args[1])) {
290 jsdisp_t *obj = to_jsdisp(get_object(args[1]));
291 jsstr_t *jsstr;
292 DISPID id;
293 BOOL b;
295 if(!obj) {
296 FIXME("non-JS obj in JSON object: %p\n", get_object(args[1]));
297 proc_ctx->hres = E_NOTIMPL;
298 return jsval_undefined();
299 }else if(is_class(obj, JSCLASS_ARRAY)) {
300 unsigned i, length = array_get_length(obj);
301 WCHAR buf[14], *buf_end;
303 buf_end = buf + ARRAY_SIZE(buf) - 1;
304 *buf_end-- = 0;
305 for(i = 0; i < length; i++) {
306 str = idx_to_str(i, buf_end);
307 if(!(jsstr = jsstr_alloc(str))) {
308 proc_ctx->hres = E_OUTOFMEMORY;
309 return jsval_undefined();
311 res = transform_json_object(proc_ctx, obj, jsstr);
312 jsstr_release(jsstr);
313 if(is_undefined(res)) {
314 if(FAILED(proc_ctx->hres))
315 return jsval_undefined();
316 if(FAILED(jsdisp_get_id(obj, str, 0, &id)))
317 continue;
318 proc_ctx->hres = disp_delete((IDispatch*)&obj->IDispatchEx_iface, id, &b);
319 }else {
320 proc_ctx->hres = jsdisp_define_data_property(obj, str, PROPF_WRITABLE | PROPF_ENUMERABLE | PROPF_CONFIGURABLE, res);
321 jsval_release(res);
323 if(FAILED(proc_ctx->hres))
324 return jsval_undefined();
326 }else {
327 id = DISPID_STARTENUM;
328 for(;;) {
329 proc_ctx->hres = jsdisp_next_prop(obj, id, JSDISP_ENUM_OWN_ENUMERABLE, &id);
330 if(proc_ctx->hres == S_FALSE)
331 break;
332 if(FAILED(proc_ctx->hres) || FAILED(proc_ctx->hres = jsdisp_get_prop_name(obj, id, &jsstr)))
333 return jsval_undefined();
334 res = transform_json_object(proc_ctx, obj, jsstr);
335 if(is_undefined(res)) {
336 if(SUCCEEDED(proc_ctx->hres))
337 proc_ctx->hres = disp_delete((IDispatch*)&obj->IDispatchEx_iface, id, &b);
338 }else {
339 if(!(str = jsstr_flatten(jsstr)))
340 proc_ctx->hres = E_OUTOFMEMORY;
341 else
342 proc_ctx->hres = jsdisp_define_data_property(obj, str, PROPF_WRITABLE | PROPF_ENUMERABLE | PROPF_CONFIGURABLE, res);
343 jsval_release(res);
345 jsstr_release(jsstr);
346 if(FAILED(proc_ctx->hres))
347 return jsval_undefined();
352 args[0] = jsval_string(name);
353 proc_ctx->hres = disp_call_value(proc_ctx->ctx, proc_ctx->reviver, jsval_obj(holder),
354 DISPATCH_METHOD, ARRAY_SIZE(args), args, &res);
355 return FAILED(proc_ctx->hres) ? jsval_undefined() : res;
358 /* ECMA-262 5.1 Edition 15.12.2 */
359 static HRESULT JSON_parse(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
361 json_parse_ctx_t parse_ctx;
362 const WCHAR *buf;
363 jsdisp_t *root;
364 jsstr_t *str;
365 jsval_t ret;
366 HRESULT hres;
368 hres = to_flat_string(ctx, argv[0], &str, &buf);
369 if(FAILED(hres))
370 return hres;
372 TRACE("%s\n", debugstr_w(buf));
374 parse_ctx.ptr = buf;
375 parse_ctx.end = buf + jsstr_length(str);
376 parse_ctx.ctx = ctx;
377 hres = parse_json_value(&parse_ctx, &ret);
378 if(SUCCEEDED(hres) && skip_spaces(&parse_ctx)) {
379 FIXME("syntax error\n");
380 jsval_release(ret);
381 hres = E_FAIL;
383 jsstr_release(str);
384 if(FAILED(hres))
385 return hres;
387 /* FIXME: check IsCallable */
388 if(argc > 1 && is_object_instance(argv[1])) {
389 hres = create_object(ctx, NULL, &root);
390 if(FAILED(hres)) {
391 jsval_release(ret);
392 return hres;
394 hres = jsdisp_define_data_property(root, L"", PROPF_WRITABLE | PROPF_ENUMERABLE | PROPF_CONFIGURABLE, ret);
395 jsval_release(ret);
397 if(SUCCEEDED(hres)) {
398 struct transform_json_object_ctx proc_ctx = { ctx, get_object(argv[1]), S_OK };
400 str = jsstr_empty();
401 ret = transform_json_object(&proc_ctx, root, str);
402 jsstr_release(str);
403 hres = proc_ctx.hres;
405 jsdisp_release(root);
406 if(FAILED(hres))
407 return hres;
410 if(r)
411 *r = ret;
412 else
413 jsval_release(ret);
414 return S_OK;
417 typedef struct {
418 script_ctx_t *ctx;
420 WCHAR *buf;
421 size_t buf_size;
422 size_t buf_len;
424 jsdisp_t **stack;
425 size_t stack_top;
426 size_t stack_size;
428 WCHAR gap[11]; /* according to the spec, it's no longer than 10 chars */
430 jsdisp_t *replacer;
431 } stringify_ctx_t;
433 static BOOL stringify_push_obj(stringify_ctx_t *ctx, jsdisp_t *obj)
435 if(!ctx->stack_size) {
436 ctx->stack = malloc(4*sizeof(*ctx->stack));
437 if(!ctx->stack)
438 return FALSE;
439 ctx->stack_size = 4;
440 }else if(ctx->stack_top == ctx->stack_size) {
441 jsdisp_t **new_stack;
443 new_stack = realloc(ctx->stack, ctx->stack_size*2*sizeof(*ctx->stack));
444 if(!new_stack)
445 return FALSE;
446 ctx->stack = new_stack;
447 ctx->stack_size *= 2;
450 ctx->stack[ctx->stack_top++] = obj;
451 return TRUE;
454 static void stringify_pop_obj(stringify_ctx_t *ctx)
456 ctx->stack_top--;
459 static BOOL is_on_stack(stringify_ctx_t *ctx, jsdisp_t *obj)
461 size_t i = ctx->stack_top;
462 while(i--) {
463 if(ctx->stack[i] == obj)
464 return TRUE;
466 return FALSE;
469 static BOOL append_string_len(stringify_ctx_t *ctx, const WCHAR *str, size_t len)
471 if(!ctx->buf_size) {
472 ctx->buf = malloc(len*2*sizeof(WCHAR));
473 if(!ctx->buf)
474 return FALSE;
475 ctx->buf_size = len*2;
476 }else if(ctx->buf_len + len > ctx->buf_size) {
477 WCHAR *new_buf;
478 size_t new_size;
480 new_size = ctx->buf_size * 2 + len;
481 new_buf = realloc(ctx->buf, new_size*sizeof(WCHAR));
482 if(!new_buf)
483 return FALSE;
484 ctx->buf = new_buf;
485 ctx->buf_size = new_size;
488 if(len)
489 memcpy(ctx->buf + ctx->buf_len, str, len*sizeof(WCHAR));
490 ctx->buf_len += len;
491 return TRUE;
494 static inline BOOL append_string(stringify_ctx_t *ctx, const WCHAR *str)
496 return append_string_len(ctx, str, lstrlenW(str));
499 static inline BOOL append_char(stringify_ctx_t *ctx, WCHAR c)
501 return append_string_len(ctx, &c, 1);
504 static inline BOOL append_simple_quote(stringify_ctx_t *ctx, WCHAR c)
506 WCHAR str[] = {'\\',c};
507 return append_string_len(ctx, str, 2);
510 static HRESULT maybe_to_primitive(script_ctx_t *ctx, jsval_t val, jsval_t *r)
512 jsdisp_t *obj;
513 HRESULT hres;
515 if(!is_object_instance(val) || !(obj = iface_to_jsdisp(get_object(val))))
516 return jsval_copy(val, r);
518 if(is_class(obj, JSCLASS_NUMBER)) {
519 double n;
520 hres = to_number(ctx, val, &n);
521 jsdisp_release(obj);
522 if(SUCCEEDED(hres))
523 *r = jsval_number(n);
524 return hres;
527 if(is_class(obj, JSCLASS_STRING)) {
528 jsstr_t *str;
529 hres = to_string(ctx, val, &str);
530 jsdisp_release(obj);
531 if(SUCCEEDED(hres))
532 *r = jsval_string(str);
533 return hres;
536 if(is_class(obj, JSCLASS_BOOLEAN)) {
537 *r = jsval_bool(bool_obj_value(obj));
538 jsdisp_release(obj);
539 return S_OK;
542 *r = jsval_obj(obj);
543 return S_OK;
546 /* ECMA-262 5.1 Edition 15.12.3 (abstract operation Quote) */
547 static HRESULT json_quote(stringify_ctx_t *ctx, const WCHAR *ptr, size_t len)
549 if(!ptr || !append_char(ctx, '"'))
550 return E_OUTOFMEMORY;
552 while(len--) {
553 switch(*ptr) {
554 case '"':
555 case '\\':
556 if(!append_simple_quote(ctx, *ptr))
557 return E_OUTOFMEMORY;
558 break;
559 case '\b':
560 if(!append_simple_quote(ctx, 'b'))
561 return E_OUTOFMEMORY;
562 break;
563 case '\f':
564 if(!append_simple_quote(ctx, 'f'))
565 return E_OUTOFMEMORY;
566 break;
567 case '\n':
568 if(!append_simple_quote(ctx, 'n'))
569 return E_OUTOFMEMORY;
570 break;
571 case '\r':
572 if(!append_simple_quote(ctx, 'r'))
573 return E_OUTOFMEMORY;
574 break;
575 case '\t':
576 if(!append_simple_quote(ctx, 't'))
577 return E_OUTOFMEMORY;
578 break;
579 default:
580 if(*ptr < ' ') {
581 WCHAR buf[7];
582 swprintf(buf, ARRAY_SIZE(buf), L"\\u%04x", *ptr);
583 if(!append_string(ctx, buf))
584 return E_OUTOFMEMORY;
585 }else {
586 if(!append_char(ctx, *ptr))
587 return E_OUTOFMEMORY;
590 ptr++;
593 return append_char(ctx, '"') ? S_OK : E_OUTOFMEMORY;
596 static inline BOOL is_callable(jsdisp_t *obj)
598 return is_class(obj, JSCLASS_FUNCTION);
601 static HRESULT stringify(stringify_ctx_t *ctx, jsdisp_t *object, const WCHAR *name);
603 /* ECMA-262 5.1 Edition 15.12.3 (abstract operation JA) */
604 static HRESULT stringify_array(stringify_ctx_t *ctx, jsdisp_t *obj)
606 unsigned length, i, j;
607 WCHAR name[16];
608 HRESULT hres;
610 if(is_on_stack(ctx, obj)) {
611 FIXME("Found a cycle\n");
612 return E_FAIL;
615 if(!stringify_push_obj(ctx, obj))
616 return E_OUTOFMEMORY;
618 if(!append_char(ctx, '['))
619 return E_OUTOFMEMORY;
621 length = array_get_length(obj);
623 for(i=0; i < length; i++) {
624 if(i && !append_char(ctx, ','))
625 return E_OUTOFMEMORY;
627 if(*ctx->gap) {
628 if(!append_char(ctx, '\n'))
629 return E_OUTOFMEMORY;
631 for(j=0; j < ctx->stack_top; j++) {
632 if(!append_string(ctx, ctx->gap))
633 return E_OUTOFMEMORY;
637 _itow(i, name, ARRAY_SIZE(name));
638 hres = stringify(ctx, obj, name);
639 if(FAILED(hres))
640 return hres;
641 if(hres == S_FALSE && !append_string(ctx, L"null"))
642 return E_OUTOFMEMORY;
645 if((length && *ctx->gap && !append_char(ctx, '\n')) || !append_char(ctx, ']'))
646 return E_OUTOFMEMORY;
648 stringify_pop_obj(ctx);
649 return S_OK;
652 /* ECMA-262 5.1 Edition 15.12.3 (abstract operation JO) */
653 static HRESULT stringify_object(stringify_ctx_t *ctx, jsdisp_t *obj)
655 DISPID dispid = DISPID_STARTENUM;
656 unsigned prop_cnt = 0, i;
657 size_t stepback;
658 BSTR prop_name;
659 HRESULT hres;
661 if(is_on_stack(ctx, obj)) {
662 FIXME("Found a cycle\n");
663 return E_FAIL;
666 if(!stringify_push_obj(ctx, obj))
667 return E_OUTOFMEMORY;
669 if(!append_char(ctx, '{'))
670 return E_OUTOFMEMORY;
672 while((hres = IDispatchEx_GetNextDispID(&obj->IDispatchEx_iface, fdexEnumDefault, dispid, &dispid)) == S_OK) {
673 stepback = ctx->buf_len;
675 if(prop_cnt && !append_char(ctx, ',')) {
676 hres = E_OUTOFMEMORY;
677 break;
680 if(*ctx->gap) {
681 if(!append_char(ctx, '\n')) {
682 hres = E_OUTOFMEMORY;
683 break;
686 for(i=0; i < ctx->stack_top; i++) {
687 if(!append_string(ctx, ctx->gap)) {
688 hres = E_OUTOFMEMORY;
689 break;
694 hres = IDispatchEx_GetMemberName(&obj->IDispatchEx_iface, dispid, &prop_name);
695 if(FAILED(hres))
696 return hres;
698 hres = json_quote(ctx, prop_name, SysStringLen(prop_name));
699 if(FAILED(hres)) {
700 SysFreeString(prop_name);
701 return hres;
704 if(!append_char(ctx, ':') || (*ctx->gap && !append_char(ctx, ' '))) {
705 SysFreeString(prop_name);
706 return E_OUTOFMEMORY;
709 hres = stringify(ctx, obj, prop_name);
710 SysFreeString(prop_name);
711 if(FAILED(hres))
712 return hres;
714 if(hres == S_FALSE) {
715 ctx->buf_len = stepback;
716 continue;
719 prop_cnt++;
722 if(prop_cnt && *ctx->gap) {
723 if(!append_char(ctx, '\n'))
724 return E_OUTOFMEMORY;
726 for(i=1; i < ctx->stack_top; i++) {
727 if(!append_string(ctx, ctx->gap)) {
728 hres = E_OUTOFMEMORY;
729 break;
734 if(!append_char(ctx, '}'))
735 return E_OUTOFMEMORY;
737 stringify_pop_obj(ctx);
738 return S_OK;
741 /* ECMA-262 5.1 Edition 15.12.3 (abstract operation Str) */
742 static HRESULT stringify(stringify_ctx_t *ctx, jsdisp_t *object, const WCHAR *name)
744 jsval_t value, v;
745 HRESULT hres;
747 hres = jsdisp_propget_name(object, name, &value);
748 if(FAILED(hres))
749 return hres == DISP_E_UNKNOWNNAME ? S_FALSE : hres;
751 if(is_object_instance(value)) {
752 jsdisp_t *obj;
753 DISPID id;
755 obj = to_jsdisp(get_object(value));
756 if(!obj) {
757 jsval_release(value);
758 return S_FALSE;
761 hres = jsdisp_get_id(obj, L"toJSON", 0, &id);
762 if(hres == S_OK)
763 FIXME("Use toJSON.\n");
766 if(ctx->replacer) {
767 jsstr_t *name_str;
768 jsval_t args[2];
769 if(!(name_str = jsstr_alloc(name))) {
770 jsval_release(value);
771 return E_OUTOFMEMORY;
773 args[0] = jsval_string(name_str);
774 args[1] = value;
775 hres = jsdisp_call_value(ctx->replacer, jsval_obj(object), DISPATCH_METHOD, ARRAY_SIZE(args), args, &v);
776 jsstr_release(name_str);
777 jsval_release(value);
778 if(FAILED(hres))
779 return hres;
780 value = v;
783 v = value;
784 hres = maybe_to_primitive(ctx->ctx, v, &value);
785 jsval_release(v);
786 if(FAILED(hres))
787 return hres;
789 switch(jsval_type(value)) {
790 case JSV_NULL:
791 if(is_null_disp(value))
792 hres = S_FALSE;
793 else if(!append_string(ctx, L"null"))
794 hres = E_OUTOFMEMORY;
795 break;
796 case JSV_BOOL:
797 if(!append_string(ctx, get_bool(value) ? L"true" : L"false"))
798 hres = E_OUTOFMEMORY;
799 break;
800 case JSV_STRING: {
801 jsstr_t *str = get_string(value);
802 const WCHAR *ptr = jsstr_flatten(str);
803 if(ptr)
804 hres = json_quote(ctx, ptr, jsstr_length(str));
805 else
806 hres = E_OUTOFMEMORY;
807 break;
809 case JSV_NUMBER: {
810 double n = get_number(value);
811 if(isfinite(n)) {
812 const WCHAR *ptr;
813 jsstr_t *str;
815 /* FIXME: Optimize. There is no need for jsstr_t here. */
816 hres = double_to_string(n, &str);
817 if(FAILED(hres))
818 break;
820 ptr = jsstr_flatten(str);
821 assert(ptr != NULL);
822 hres = ptr && !append_string_len(ctx, ptr, jsstr_length(str)) ? E_OUTOFMEMORY : S_OK;
823 jsstr_release(str);
824 }else {
825 if(!append_string(ctx, L"null"))
826 hres = E_OUTOFMEMORY;
828 break;
830 case JSV_OBJECT: {
831 jsdisp_t *obj;
833 obj = iface_to_jsdisp(get_object(value));
834 if(!obj) {
835 hres = S_FALSE;
836 break;
839 if(!is_callable(obj))
840 hres = is_class(obj, JSCLASS_ARRAY) ? stringify_array(ctx, obj) : stringify_object(ctx, obj);
841 else
842 hres = S_FALSE;
844 jsdisp_release(obj);
845 break;
847 case JSV_UNDEFINED:
848 hres = S_FALSE;
849 break;
850 case JSV_VARIANT:
851 FIXME("VARIANT\n");
852 hres = E_NOTIMPL;
853 break;
856 jsval_release(value);
857 return hres;
860 /* ECMA-262 5.1 Edition 15.12.3 */
861 static HRESULT JSON_stringify(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
863 stringify_ctx_t stringify_ctx = { ctx };
864 jsdisp_t *obj = NULL, *replacer;
865 HRESULT hres;
867 TRACE("\n");
869 if(!argc) {
870 if(r)
871 *r = jsval_undefined();
872 return S_OK;
875 if(argc >= 2 && is_object_instance(argv[1]) && (replacer = to_jsdisp(get_object(argv[1])))) {
876 if(is_callable(replacer)) {
877 stringify_ctx.replacer = jsdisp_addref(replacer);
878 }else if(is_class(replacer, JSCLASS_ARRAY)) {
879 FIXME("Array replacer not yet supported\n");
880 return E_NOTIMPL;
884 if(argc >= 3) {
885 jsval_t space_val;
887 hres = maybe_to_primitive(ctx, argv[2], &space_val);
888 if(FAILED(hres))
889 goto fail;
891 if(is_number(space_val)) {
892 double n = get_number(space_val);
893 if(n >= 1) {
894 int i, len;
895 if(n > 10)
896 n = 10;
897 len = floor(n);
898 for(i=0; i < len; i++)
899 stringify_ctx.gap[i] = ' ';
900 stringify_ctx.gap[len] = 0;
902 }else if(is_string(space_val)) {
903 jsstr_t *space_str = get_string(space_val);
904 size_t len = jsstr_length(space_str);
905 if(len > 10)
906 len = 10;
907 jsstr_extract(space_str, 0, len, stringify_ctx.gap);
910 jsval_release(space_val);
913 if(FAILED(hres = create_object(ctx, NULL, &obj)))
914 goto fail;
915 if(FAILED(hres = jsdisp_propput_name(obj, L"", argv[0])))
916 goto fail;
918 hres = stringify(&stringify_ctx, obj, L"");
919 if(SUCCEEDED(hres) && r) {
920 assert(!stringify_ctx.stack_top);
922 if(hres == S_OK) {
923 jsstr_t *ret = jsstr_alloc_len(stringify_ctx.buf, stringify_ctx.buf_len);
924 if(ret)
925 *r = jsval_string(ret);
926 else
927 hres = E_OUTOFMEMORY;
928 }else {
929 *r = jsval_undefined();
933 fail:
934 if(obj)
935 jsdisp_release(obj);
936 if(stringify_ctx.replacer)
937 jsdisp_release(stringify_ctx.replacer);
938 free(stringify_ctx.buf);
939 free(stringify_ctx.stack);
940 return hres;
943 static const builtin_prop_t JSON_props[] = {
944 {L"parse", JSON_parse, PROPF_METHOD|2},
945 {L"stringify", JSON_stringify, PROPF_METHOD|3}
948 static const builtin_info_t JSON_info = {
949 JSCLASS_JSON,
950 NULL,
951 ARRAY_SIZE(JSON_props),
952 JSON_props,
953 NULL,
954 NULL
957 HRESULT create_json(script_ctx_t *ctx, jsdisp_t **ret)
959 jsdisp_t *json;
960 HRESULT hres;
962 json = calloc(1, sizeof(*json));
963 if(!json)
964 return E_OUTOFMEMORY;
966 hres = init_dispex_from_constr(json, ctx, &JSON_info, ctx->object_constr);
967 if(FAILED(hres)) {
968 free(json);
969 return hres;
972 *ret = json;
973 return S_OK;