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 44 static BOOL is_json_space(WCHAR c) 45 { 46 return c == ' ' || c == '\t' || c == '\n' || c == '\r'; 47 } 48 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 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 */ 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 */ 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 */ 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 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 355 static void stringify_pop_obj(stringify_ctx_t *ctx) 356 { 357 ctx->stack_top--; 358 } 359 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 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 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 400 static inline BOOL append_char(stringify_ctx_t *ctx, WCHAR c) 401 { 402 return append_string_len(ctx, &c, 1); 403 } 404 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 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) */ 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 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) */ 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) */ 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) */ 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 */ 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 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