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