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