1 /* 2 * Copyright 2008 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 "config.h" 20 #include "wine/port.h" 21 22 #include "jscript.h" 23 #include "regexp.h" 24 25 #include "wine/debug.h" 26 27 WINE_DEFAULT_DEBUG_CHANNEL(jscript); 28 29 typedef struct { 30 jsdisp_t dispex; 31 jsstr_t *str; 32 } StringInstance; 33 34 static const WCHAR lengthW[] = {'l','e','n','g','t','h',0}; 35 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0}; 36 static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0}; 37 static const WCHAR anchorW[] = {'a','n','c','h','o','r',0}; 38 static const WCHAR bigW[] = {'b','i','g',0}; 39 static const WCHAR blinkW[] = {'b','l','i','n','k',0}; 40 static const WCHAR boldW[] = {'b','o','l','d',0}; 41 static const WCHAR charAtW[] = {'c','h','a','r','A','t',0}; 42 static const WCHAR charCodeAtW[] = {'c','h','a','r','C','o','d','e','A','t',0}; 43 static const WCHAR concatW[] = {'c','o','n','c','a','t',0}; 44 static const WCHAR fixedW[] = {'f','i','x','e','d',0}; 45 static const WCHAR fontcolorW[] = {'f','o','n','t','c','o','l','o','r',0}; 46 static const WCHAR fontsizeW[] = {'f','o','n','t','s','i','z','e',0}; 47 static const WCHAR indexOfW[] = {'i','n','d','e','x','O','f',0}; 48 static const WCHAR italicsW[] = {'i','t','a','l','i','c','s',0}; 49 static const WCHAR lastIndexOfW[] = {'l','a','s','t','I','n','d','e','x','O','f',0}; 50 static const WCHAR linkW[] = {'l','i','n','k',0}; 51 static const WCHAR matchW[] = {'m','a','t','c','h',0}; 52 static const WCHAR replaceW[] = {'r','e','p','l','a','c','e',0}; 53 static const WCHAR searchW[] = {'s','e','a','r','c','h',0}; 54 static const WCHAR sliceW[] = {'s','l','i','c','e',0}; 55 static const WCHAR smallW[] = {'s','m','a','l','l',0}; 56 static const WCHAR splitW[] = {'s','p','l','i','t',0}; 57 static const WCHAR strikeW[] = {'s','t','r','i','k','e',0}; 58 static const WCHAR subW[] = {'s','u','b',0}; 59 static const WCHAR substringW[] = {'s','u','b','s','t','r','i','n','g',0}; 60 static const WCHAR substrW[] = {'s','u','b','s','t','r',0}; 61 static const WCHAR supW[] = {'s','u','p',0}; 62 static const WCHAR toLowerCaseW[] = {'t','o','L','o','w','e','r','C','a','s','e',0}; 63 static const WCHAR toUpperCaseW[] = {'t','o','U','p','p','e','r','C','a','s','e',0}; 64 static const WCHAR toLocaleLowerCaseW[] = {'t','o','L','o','c','a','l','e','L','o','w','e','r','C','a','s','e',0}; 65 static const WCHAR toLocaleUpperCaseW[] = {'t','o','L','o','c','a','l','e','U','p','p','e','r','C','a','s','e',0}; 66 static const WCHAR trimW[] = {'t','r','i','m',0}; 67 static const WCHAR localeCompareW[] = {'l','o','c','a','l','e','C','o','m','p','a','r','e',0}; 68 static const WCHAR fromCharCodeW[] = {'f','r','o','m','C','h','a','r','C','o','d','e',0}; 69 70 static inline StringInstance *string_from_jsdisp(jsdisp_t *jsdisp) 71 { 72 return CONTAINING_RECORD(jsdisp, StringInstance, dispex); 73 } 74 75 static inline StringInstance *string_from_vdisp(vdisp_t *vdisp) 76 { 77 return string_from_jsdisp(vdisp->u.jsdisp); 78 } 79 80 static inline StringInstance *string_this(vdisp_t *jsthis) 81 { 82 return is_vclass(jsthis, JSCLASS_STRING) ? string_from_vdisp(jsthis) : NULL; 83 } 84 85 static HRESULT get_string_val(script_ctx_t *ctx, vdisp_t *jsthis, jsstr_t **val) 86 { 87 StringInstance *string; 88 89 if((string = string_this(jsthis))) { 90 *val = jsstr_addref(string->str); 91 return S_OK; 92 } 93 94 return to_string(ctx, jsval_disp(jsthis->u.disp), val); 95 } 96 97 static HRESULT get_string_flat_val(script_ctx_t *ctx, vdisp_t *jsthis, jsstr_t **jsval, const WCHAR **val) 98 { 99 HRESULT hres; 100 101 hres = get_string_val(ctx, jsthis, jsval); 102 if(FAILED(hres)) 103 return hres; 104 105 *val = jsstr_flatten(*jsval); 106 if(*val) 107 return S_OK; 108 109 jsstr_release(*jsval); 110 return E_OUTOFMEMORY; 111 } 112 113 static HRESULT String_get_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 114 { 115 StringInstance *string = string_from_jsdisp(jsthis); 116 117 TRACE("%p\n", jsthis); 118 119 *r = jsval_number(jsstr_length(string->str)); 120 return S_OK; 121 } 122 123 static HRESULT stringobj_to_string(vdisp_t *jsthis, jsval_t *r) 124 { 125 StringInstance *string; 126 127 if(!(string = string_this(jsthis))) { 128 WARN("this is not a string object\n"); 129 return E_FAIL; 130 } 131 132 if(r) 133 *r = jsval_string(jsstr_addref(string->str)); 134 return S_OK; 135 } 136 137 /* ECMA-262 3rd Edition 15.5.4.2 */ 138 static HRESULT String_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 139 jsval_t *r) 140 { 141 TRACE("\n"); 142 143 return stringobj_to_string(jsthis, r); 144 } 145 146 /* ECMA-262 3rd Edition 15.5.4.2 */ 147 static HRESULT String_valueOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 148 jsval_t *r) 149 { 150 TRACE("\n"); 151 152 return stringobj_to_string(jsthis, r); 153 } 154 155 static HRESULT do_attributeless_tag_format(script_ctx_t *ctx, vdisp_t *jsthis, jsval_t *r, const WCHAR *tagname) 156 { 157 unsigned tagname_len; 158 jsstr_t *str, *ret; 159 WCHAR *ptr; 160 HRESULT hres; 161 162 hres = get_string_val(ctx, jsthis, &str); 163 if(FAILED(hres)) 164 return hres; 165 166 if(!r) { 167 jsstr_release(str); 168 return S_OK; 169 } 170 171 tagname_len = strlenW(tagname); 172 173 ret = jsstr_alloc_buf(jsstr_length(str) + 2*tagname_len + 5, &ptr); 174 if(!ret) { 175 jsstr_release(str); 176 return E_OUTOFMEMORY; 177 } 178 179 *ptr++ = '<'; 180 memcpy(ptr, tagname, tagname_len*sizeof(WCHAR)); 181 ptr += tagname_len; 182 *ptr++ = '>'; 183 184 ptr += jsstr_flush(str, ptr); 185 jsstr_release(str); 186 187 *ptr++ = '<'; 188 *ptr++ = '/'; 189 memcpy(ptr, tagname, tagname_len*sizeof(WCHAR)); 190 ptr += tagname_len; 191 *ptr = '>'; 192 193 *r = jsval_string(ret); 194 return S_OK; 195 } 196 197 static HRESULT do_attribute_tag_format(script_ctx_t *ctx, vdisp_t *jsthis, unsigned argc, jsval_t *argv, jsval_t *r, 198 const WCHAR *tagname, const WCHAR *attrname) 199 { 200 jsstr_t *str, *attr_value = NULL; 201 HRESULT hres; 202 203 hres = get_string_val(ctx, jsthis, &str); 204 if(FAILED(hres)) 205 return hres; 206 207 if(argc) { 208 hres = to_string(ctx, argv[0], &attr_value); 209 if(FAILED(hres)) { 210 jsstr_release(str); 211 return hres; 212 } 213 }else { 214 attr_value = jsstr_undefined(); 215 } 216 217 if(r) { 218 unsigned attrname_len = strlenW(attrname); 219 unsigned tagname_len = strlenW(tagname); 220 jsstr_t *ret; 221 WCHAR *ptr; 222 223 ret = jsstr_alloc_buf(2*tagname_len + attrname_len + jsstr_length(attr_value) + jsstr_length(str) + 9, &ptr); 224 if(ret) { 225 *ptr++ = '<'; 226 memcpy(ptr, tagname, tagname_len*sizeof(WCHAR)); 227 ptr += tagname_len; 228 *ptr++ = ' '; 229 memcpy(ptr, attrname, attrname_len*sizeof(WCHAR)); 230 ptr += attrname_len; 231 *ptr++ = '='; 232 *ptr++ = '"'; 233 ptr += jsstr_flush(attr_value, ptr); 234 *ptr++ = '"'; 235 *ptr++ = '>'; 236 ptr += jsstr_flush(str, ptr); 237 238 *ptr++ = '<'; 239 *ptr++ = '/'; 240 memcpy(ptr, tagname, tagname_len*sizeof(WCHAR)); 241 ptr += tagname_len; 242 *ptr = '>'; 243 244 *r = jsval_string(ret); 245 }else { 246 hres = E_OUTOFMEMORY; 247 } 248 } 249 250 jsstr_release(attr_value); 251 jsstr_release(str); 252 return hres; 253 } 254 255 static HRESULT String_anchor(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 256 jsval_t *r) 257 { 258 static const WCHAR fontW[] = {'A',0}; 259 static const WCHAR colorW[] = {'N','A','M','E',0}; 260 261 return do_attribute_tag_format(ctx, jsthis, argc, argv, r, fontW, colorW); 262 } 263 264 static HRESULT String_big(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 265 jsval_t *r) 266 { 267 static const WCHAR bigtagW[] = {'B','I','G',0}; 268 return do_attributeless_tag_format(ctx, jsthis, r, bigtagW); 269 } 270 271 static HRESULT String_blink(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 272 jsval_t *r) 273 { 274 static const WCHAR blinktagW[] = {'B','L','I','N','K',0}; 275 return do_attributeless_tag_format(ctx, jsthis, r, blinktagW); 276 } 277 278 static HRESULT String_bold(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 279 jsval_t *r) 280 { 281 static const WCHAR boldtagW[] = {'B',0}; 282 return do_attributeless_tag_format(ctx, jsthis, r, boldtagW); 283 } 284 285 /* ECMA-262 3rd Edition 15.5.4.5 */ 286 static HRESULT String_charAt(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 287 jsval_t *r) 288 { 289 jsstr_t *str, *ret; 290 INT pos = 0; 291 HRESULT hres; 292 293 TRACE("\n"); 294 295 hres = get_string_val(ctx, jsthis, &str); 296 if(FAILED(hres)) 297 return hres; 298 299 if(argc) { 300 double d; 301 302 hres = to_integer(ctx, argv[0], &d); 303 if(FAILED(hres)) { 304 jsstr_release(str); 305 return hres; 306 } 307 pos = is_int32(d) ? d : -1; 308 } 309 310 if(!r) { 311 jsstr_release(str); 312 return S_OK; 313 } 314 315 if(0 <= pos && pos < jsstr_length(str)) { 316 ret = jsstr_substr(str, pos, 1); 317 if(!ret) 318 return E_OUTOFMEMORY; 319 }else { 320 ret = jsstr_empty(); 321 } 322 323 *r = jsval_string(ret); 324 return S_OK; 325 } 326 327 /* ECMA-262 3rd Edition 15.5.4.5 */ 328 static HRESULT String_charCodeAt(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 329 jsval_t *r) 330 { 331 jsstr_t *str; 332 DWORD idx = 0; 333 HRESULT hres; 334 335 TRACE("\n"); 336 337 hres = get_string_val(ctx, jsthis, &str); 338 if(FAILED(hres)) 339 return hres; 340 341 if(argc > 0) { 342 double d; 343 344 hres = to_integer(ctx, argv[0], &d); 345 if(FAILED(hres)) { 346 jsstr_release(str); 347 return hres; 348 } 349 350 if(!is_int32(d) || d < 0 || d >= jsstr_length(str)) { 351 jsstr_release(str); 352 if(r) 353 *r = jsval_number(NAN); 354 return S_OK; 355 } 356 357 idx = d; 358 } 359 360 if(r) { 361 WCHAR c; 362 jsstr_extract(str, idx, 1, &c); 363 *r = jsval_number(c); 364 } 365 366 jsstr_release(str); 367 return S_OK; 368 } 369 370 /* ECMA-262 3rd Edition 15.5.4.6 */ 371 static HRESULT String_concat(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 372 jsval_t *r) 373 { 374 jsstr_t *ret = NULL, *str; 375 HRESULT hres; 376 377 TRACE("\n"); 378 379 hres = get_string_val(ctx, jsthis, &str); 380 if(FAILED(hres)) 381 return hres; 382 383 switch(argc) { 384 case 0: 385 ret = str; 386 break; 387 case 1: { 388 jsstr_t *arg_str; 389 390 hres = to_string(ctx, argv[0], &arg_str); 391 if(FAILED(hres)) { 392 jsstr_release(str); 393 return hres; 394 } 395 396 ret = jsstr_concat(str, arg_str); 397 jsstr_release(str); 398 if(!ret) 399 return E_OUTOFMEMORY; 400 break; 401 } 402 default: { 403 const unsigned str_cnt = argc+1; 404 unsigned len = 0, i; 405 jsstr_t **strs; 406 WCHAR *ptr; 407 408 strs = heap_alloc_zero(str_cnt * sizeof(*strs)); 409 if(!strs) { 410 jsstr_release(str); 411 return E_OUTOFMEMORY; 412 } 413 414 strs[0] = str; 415 for(i=0; i < argc; i++) { 416 hres = to_string(ctx, argv[i], strs+i+1); 417 if(FAILED(hres)) 418 break; 419 } 420 421 if(SUCCEEDED(hres)) { 422 for(i=0; i < str_cnt; i++) { 423 len += jsstr_length(strs[i]); 424 if(len > JSSTR_MAX_LENGTH) { 425 hres = E_OUTOFMEMORY; 426 break; 427 } 428 } 429 430 if(SUCCEEDED(hres)) { 431 ret = jsstr_alloc_buf(len, &ptr); 432 if(ret) { 433 for(i=0; i < str_cnt; i++) 434 ptr += jsstr_flush(strs[i], ptr); 435 }else { 436 hres = E_OUTOFMEMORY; 437 } 438 } 439 } 440 441 while(i--) 442 jsstr_release(strs[i]); 443 heap_free(strs); 444 if(FAILED(hres)) 445 return hres; 446 } 447 } 448 449 if(r) 450 *r = jsval_string(ret); 451 else 452 jsstr_release(ret); 453 return S_OK; 454 } 455 456 static HRESULT String_fixed(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 457 jsval_t *r) 458 { 459 static const WCHAR fixedtagW[] = {'T','T',0}; 460 return do_attributeless_tag_format(ctx, jsthis, r, fixedtagW); 461 } 462 463 static HRESULT String_fontcolor(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 464 jsval_t *r) 465 { 466 static const WCHAR fontW[] = {'F','O','N','T',0}; 467 static const WCHAR colorW[] = {'C','O','L','O','R',0}; 468 469 return do_attribute_tag_format(ctx, jsthis, argc, argv, r, fontW, colorW); 470 } 471 472 static HRESULT String_fontsize(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 473 jsval_t *r) 474 { 475 static const WCHAR fontW[] = {'F','O','N','T',0}; 476 static const WCHAR colorW[] = {'S','I','Z','E',0}; 477 478 return do_attribute_tag_format(ctx, jsthis, argc, argv, r, fontW, colorW); 479 } 480 481 static HRESULT String_indexOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 482 jsval_t *r) 483 { 484 unsigned pos = 0, search_len, length; 485 jsstr_t *search_jsstr, *jsstr; 486 const WCHAR *search_str, *str; 487 INT ret = -1; 488 HRESULT hres; 489 490 TRACE("\n"); 491 492 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str); 493 if(FAILED(hres)) 494 return hres; 495 496 if(!argc) { 497 if(r) 498 *r = jsval_number(-1); 499 jsstr_release(jsstr); 500 return S_OK; 501 } 502 503 hres = to_flat_string(ctx, argv[0], &search_jsstr, &search_str); 504 if(FAILED(hres)) { 505 jsstr_release(jsstr); 506 return hres; 507 } 508 509 search_len = jsstr_length(search_jsstr); 510 length = jsstr_length(jsstr); 511 512 if(argc >= 2) { 513 double d; 514 515 hres = to_integer(ctx, argv[1], &d); 516 if(SUCCEEDED(hres) && d > 0.0) 517 pos = is_int32(d) ? min(length, d) : length; 518 } 519 520 if(SUCCEEDED(hres) && length >= search_len) { 521 const WCHAR *end = str+length-search_len; 522 const WCHAR *ptr; 523 524 for(ptr = str+pos; ptr <= end; ptr++) { 525 if(!memcmp(ptr, search_str, search_len*sizeof(WCHAR))) { 526 ret = ptr-str; 527 break; 528 } 529 } 530 } 531 532 jsstr_release(search_jsstr); 533 jsstr_release(jsstr); 534 if(FAILED(hres)) 535 return hres; 536 537 if(r) 538 *r = jsval_number(ret); 539 return S_OK; 540 } 541 542 static HRESULT String_italics(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 543 jsval_t *r) 544 { 545 static const WCHAR italicstagW[] = {'I',0}; 546 return do_attributeless_tag_format(ctx, jsthis, r, italicstagW); 547 } 548 549 /* ECMA-262 3rd Edition 15.5.4.8 */ 550 static HRESULT String_lastIndexOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 551 jsval_t *r) 552 { 553 unsigned pos = 0, search_len, length; 554 jsstr_t *search_jsstr, *jsstr; 555 const WCHAR *search_str, *str; 556 INT ret = -1; 557 HRESULT hres; 558 559 TRACE("\n"); 560 561 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str); 562 if(FAILED(hres)) 563 return hres; 564 565 if(!argc) { 566 if(r) 567 *r = jsval_number(-1); 568 jsstr_release(jsstr); 569 return S_OK; 570 } 571 572 hres = to_flat_string(ctx, argv[0], &search_jsstr, &search_str); 573 if(FAILED(hres)) { 574 jsstr_release(jsstr); 575 return hres; 576 } 577 578 search_len = jsstr_length(search_jsstr); 579 length = jsstr_length(jsstr); 580 581 if(argc >= 2) { 582 double d; 583 584 hres = to_integer(ctx, argv[1], &d); 585 if(SUCCEEDED(hres) && d > 0) 586 pos = is_int32(d) ? min(length, d) : length; 587 }else { 588 pos = length; 589 } 590 591 if(SUCCEEDED(hres) && length >= search_len) { 592 const WCHAR *ptr; 593 594 for(ptr = str+min(pos, length-search_len); ptr >= str; ptr--) { 595 if(!memcmp(ptr, search_str, search_len*sizeof(WCHAR))) { 596 ret = ptr-str; 597 break; 598 } 599 } 600 } 601 602 jsstr_release(search_jsstr); 603 jsstr_release(jsstr); 604 if(FAILED(hres)) 605 return hres; 606 607 if(r) 608 *r = jsval_number(ret); 609 return S_OK; 610 } 611 612 static HRESULT String_link(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 613 jsval_t *r) 614 { 615 static const WCHAR fontW[] = {'A',0}; 616 static const WCHAR colorW[] = {'H','R','E','F',0}; 617 618 return do_attribute_tag_format(ctx, jsthis, argc, argv, r, fontW, colorW); 619 } 620 621 /* ECMA-262 3rd Edition 15.5.4.10 */ 622 static HRESULT String_match(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 623 jsval_t *r) 624 { 625 jsdisp_t *regexp = NULL; 626 jsstr_t *str; 627 HRESULT hres; 628 629 TRACE("\n"); 630 631 if(!argc) { 632 if(r) 633 *r = jsval_null(); 634 return S_OK; 635 } 636 637 if(is_object_instance(argv[0])) { 638 regexp = iface_to_jsdisp(get_object(argv[0])); 639 if(regexp && !is_class(regexp, JSCLASS_REGEXP)) { 640 jsdisp_release(regexp); 641 regexp = NULL; 642 } 643 } 644 645 if(!regexp) { 646 jsstr_t *match_str; 647 648 hres = to_string(ctx, argv[0], &match_str); 649 if(FAILED(hres)) 650 return hres; 651 652 hres = create_regexp(ctx, match_str, 0, ®exp); 653 jsstr_release(match_str); 654 if(FAILED(hres)) 655 return hres; 656 } 657 658 hres = get_string_val(ctx, jsthis, &str); 659 if(SUCCEEDED(hres)) 660 hres = regexp_string_match(ctx, regexp, str, r); 661 662 jsdisp_release(regexp); 663 jsstr_release(str); 664 return hres; 665 } 666 667 typedef struct { 668 WCHAR *buf; 669 DWORD size; 670 DWORD len; 671 } strbuf_t; 672 673 static BOOL strbuf_ensure_size(strbuf_t *buf, unsigned len) 674 { 675 WCHAR *new_buf; 676 DWORD new_size; 677 678 if(len <= buf->size) 679 return TRUE; 680 681 new_size = buf->size ? buf->size<<1 : 16; 682 if(new_size < len) 683 new_size = len; 684 if(buf->buf) 685 new_buf = heap_realloc(buf->buf, new_size*sizeof(WCHAR)); 686 else 687 new_buf = heap_alloc(new_size*sizeof(WCHAR)); 688 if(!new_buf) 689 return FALSE; 690 691 buf->buf = new_buf; 692 buf->size = new_size; 693 return TRUE; 694 } 695 696 static HRESULT strbuf_append(strbuf_t *buf, const WCHAR *str, DWORD len) 697 { 698 if(!len) 699 return S_OK; 700 701 if(!strbuf_ensure_size(buf, buf->len+len)) 702 return E_OUTOFMEMORY; 703 704 memcpy(buf->buf+buf->len, str, len*sizeof(WCHAR)); 705 buf->len += len; 706 return S_OK; 707 } 708 709 static HRESULT strbuf_append_jsstr(strbuf_t *buf, jsstr_t *str) 710 { 711 if(!strbuf_ensure_size(buf, buf->len+jsstr_length(str))) 712 return E_OUTOFMEMORY; 713 714 jsstr_flush(str, buf->buf+buf->len); 715 buf->len += jsstr_length(str); 716 return S_OK; 717 } 718 719 static HRESULT rep_call(script_ctx_t *ctx, jsdisp_t *func, 720 jsstr_t *jsstr, const WCHAR *str, match_state_t *match, jsstr_t **ret) 721 { 722 jsval_t *argv; 723 unsigned argc; 724 jsval_t val; 725 jsstr_t *tmp_str; 726 DWORD i; 727 HRESULT hres = S_OK; 728 729 argc = match->paren_count+3; 730 argv = heap_alloc_zero(sizeof(*argv)*argc); 731 if(!argv) 732 return E_OUTOFMEMORY; 733 734 tmp_str = jsstr_alloc_len(match->cp-match->match_len, match->match_len); 735 if(!tmp_str) 736 hres = E_OUTOFMEMORY; 737 argv[0] = jsval_string(tmp_str); 738 739 if(SUCCEEDED(hres)) { 740 for(i=0; i < match->paren_count; i++) { 741 if(match->parens[i].index != -1) 742 tmp_str = jsstr_substr(jsstr, match->parens[i].index, match->parens[i].length); 743 else 744 tmp_str = jsstr_empty(); 745 if(!tmp_str) { 746 hres = E_OUTOFMEMORY; 747 break; 748 } 749 argv[i+1] = jsval_string(tmp_str); 750 } 751 } 752 753 if(SUCCEEDED(hres)) { 754 argv[match->paren_count+1] = jsval_number(match->cp-str - match->match_len); 755 argv[match->paren_count+2] = jsval_string(jsstr); 756 } 757 758 if(SUCCEEDED(hres)) 759 hres = jsdisp_call_value(func, NULL, DISPATCH_METHOD, argc, argv, &val); 760 761 for(i=0; i <= match->paren_count; i++) 762 jsstr_release(get_string(argv[i])); 763 heap_free(argv); 764 765 if(FAILED(hres)) 766 return hres; 767 768 hres = to_string(ctx, val, ret); 769 jsval_release(val); 770 return hres; 771 } 772 773 /* ECMA-262 3rd Edition 15.5.4.11 */ 774 static HRESULT String_replace(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 775 jsval_t *r) 776 { 777 const WCHAR *str, *match_str = NULL, *rep_str = NULL; 778 jsstr_t *rep_jsstr, *match_jsstr, *jsstr; 779 jsdisp_t *rep_func = NULL, *regexp = NULL; 780 match_state_t *match = NULL, last_match = {0}; 781 strbuf_t ret = {NULL,0,0}; 782 DWORD re_flags = REM_NO_CTX_UPDATE|REM_ALLOC_RESULT; 783 DWORD rep_len=0; 784 HRESULT hres = S_OK; 785 786 TRACE("\n"); 787 788 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str); 789 if(FAILED(hres)) 790 return hres; 791 792 if(!argc) { 793 if(r) 794 *r = jsval_string(jsstr); 795 else 796 jsstr_release(jsstr); 797 return S_OK; 798 } 799 800 if(is_object_instance(argv[0])) { 801 regexp = iface_to_jsdisp(get_object(argv[0])); 802 if(regexp && !is_class(regexp, JSCLASS_REGEXP)) { 803 jsdisp_release(regexp); 804 regexp = NULL; 805 } 806 } 807 808 if(!regexp) { 809 hres = to_flat_string(ctx, argv[0], &match_jsstr, &match_str); 810 if(FAILED(hres)) { 811 jsstr_release(jsstr); 812 return hres; 813 } 814 } 815 816 if(argc >= 2) { 817 if(is_object_instance(argv[1])) { 818 rep_func = iface_to_jsdisp(get_object(argv[1])); 819 if(rep_func && !is_class(rep_func, JSCLASS_FUNCTION)) { 820 jsdisp_release(rep_func); 821 rep_func = NULL; 822 } 823 } 824 825 if(!rep_func) { 826 hres = to_flat_string(ctx, argv[1], &rep_jsstr, &rep_str); 827 if(SUCCEEDED(hres)) 828 rep_len = jsstr_length(rep_jsstr); 829 } 830 } 831 832 if(SUCCEEDED(hres)) { 833 const WCHAR *ecp = str; 834 835 while(1) { 836 if(regexp) { 837 hres = regexp_match_next(ctx, regexp, re_flags, jsstr, &match); 838 re_flags = (re_flags | REM_CHECK_GLOBAL) & (~REM_ALLOC_RESULT); 839 840 if(hres == S_FALSE) { 841 hres = S_OK; 842 break; 843 } 844 if(FAILED(hres)) 845 break; 846 847 last_match.cp = match->cp; 848 last_match.match_len = match->match_len; 849 }else { 850 if(re_flags & REM_ALLOC_RESULT) { 851 re_flags &= ~REM_ALLOC_RESULT; 852 match = &last_match; 853 match->cp = str; 854 } 855 856 match->cp = strstrW(match->cp, match_str); 857 if(!match->cp) 858 break; 859 match->match_len = jsstr_length(match_jsstr); 860 match->cp += match->match_len; 861 } 862 863 hres = strbuf_append(&ret, ecp, match->cp-ecp-match->match_len); 864 ecp = match->cp; 865 if(FAILED(hres)) 866 break; 867 868 if(rep_func) { 869 jsstr_t *cstr; 870 871 hres = rep_call(ctx, rep_func, jsstr, str, match, &cstr); 872 if(FAILED(hres)) 873 break; 874 875 hres = strbuf_append_jsstr(&ret, cstr); 876 jsstr_release(cstr); 877 if(FAILED(hres)) 878 break; 879 }else if(rep_str && regexp) { 880 const WCHAR *ptr = rep_str, *ptr2; 881 882 while((ptr2 = strchrW(ptr, '$'))) { 883 hres = strbuf_append(&ret, ptr, ptr2-ptr); 884 if(FAILED(hres)) 885 break; 886 887 switch(ptr2[1]) { 888 case '$': 889 hres = strbuf_append(&ret, ptr2, 1); 890 ptr = ptr2+2; 891 break; 892 case '&': 893 hres = strbuf_append(&ret, match->cp-match->match_len, match->match_len); 894 ptr = ptr2+2; 895 break; 896 case '`': 897 hres = strbuf_append(&ret, str, match->cp-str-match->match_len); 898 ptr = ptr2+2; 899 break; 900 case '\'': 901 hres = strbuf_append(&ret, ecp, (str+jsstr_length(jsstr))-ecp); 902 ptr = ptr2+2; 903 break; 904 default: { 905 DWORD idx; 906 907 if(!isdigitW(ptr2[1])) { 908 hres = strbuf_append(&ret, ptr2, 1); 909 ptr = ptr2+1; 910 break; 911 } 912 913 idx = ptr2[1] - '0'; 914 if(isdigitW(ptr2[2]) && idx*10 + (ptr2[2]-'0') <= match->paren_count) { 915 idx = idx*10 + (ptr[2]-'0'); 916 ptr = ptr2+3; 917 }else if(idx && idx <= match->paren_count) { 918 ptr = ptr2+2; 919 }else { 920 hres = strbuf_append(&ret, ptr2, 1); 921 ptr = ptr2+1; 922 break; 923 } 924 925 if(match->parens[idx-1].index != -1) 926 hres = strbuf_append(&ret, str+match->parens[idx-1].index, 927 match->parens[idx-1].length); 928 } 929 } 930 931 if(FAILED(hres)) 932 break; 933 } 934 935 if(SUCCEEDED(hres)) 936 hres = strbuf_append(&ret, ptr, (rep_str+rep_len)-ptr); 937 if(FAILED(hres)) 938 break; 939 }else if(rep_str) { 940 hres = strbuf_append(&ret, rep_str, rep_len); 941 if(FAILED(hres)) 942 break; 943 }else { 944 static const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d'}; 945 946 hres = strbuf_append(&ret, undefinedW, ARRAY_SIZE(undefinedW)); 947 if(FAILED(hres)) 948 break; 949 } 950 951 if(!regexp) 952 break; 953 else if(!match->match_len) 954 match->cp++; 955 } 956 957 if(SUCCEEDED(hres)) 958 hres = strbuf_append(&ret, ecp, str+jsstr_length(jsstr)-ecp); 959 } 960 961 if(rep_func) 962 jsdisp_release(rep_func); 963 if(rep_str) 964 jsstr_release(rep_jsstr); 965 if(match_str) 966 jsstr_release(match_jsstr); 967 if(regexp) 968 heap_free(match); 969 970 if(SUCCEEDED(hres) && last_match.cp && regexp) { 971 jsstr_release(ctx->last_match); 972 ctx->last_match = jsstr_addref(jsstr); 973 ctx->last_match_index = last_match.cp-str-last_match.match_len; 974 ctx->last_match_length = last_match.match_len; 975 } 976 977 if(regexp) 978 jsdisp_release(regexp); 979 jsstr_release(jsstr); 980 981 if(SUCCEEDED(hres) && r) { 982 jsstr_t *ret_str; 983 984 ret_str = jsstr_alloc_len(ret.buf, ret.len); 985 if(!ret_str) 986 return E_OUTOFMEMORY; 987 988 TRACE("= %s\n", debugstr_jsstr(ret_str)); 989 *r = jsval_string(ret_str); 990 } 991 992 heap_free(ret.buf); 993 return hres; 994 } 995 996 static HRESULT String_search(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 997 jsval_t *r) 998 { 999 jsdisp_t *regexp = NULL; 1000 const WCHAR *str; 1001 jsstr_t *jsstr; 1002 match_state_t match, *match_ptr = &match; 1003 HRESULT hres; 1004 1005 TRACE("\n"); 1006 1007 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str); 1008 if(FAILED(hres)) 1009 return hres; 1010 1011 if(!argc) { 1012 if(r) 1013 *r = jsval_null(); 1014 jsstr_release(jsstr); 1015 return S_OK; 1016 } 1017 1018 if(is_object_instance(argv[0])) { 1019 regexp = iface_to_jsdisp(get_object(argv[0])); 1020 if(regexp && !is_class(regexp, JSCLASS_REGEXP)) { 1021 jsdisp_release(regexp); 1022 regexp = NULL; 1023 } 1024 } 1025 1026 if(!regexp) { 1027 hres = create_regexp_var(ctx, argv[0], NULL, ®exp); 1028 if(FAILED(hres)) { 1029 jsstr_release(jsstr); 1030 return hres; 1031 } 1032 } 1033 1034 match.cp = str; 1035 hres = regexp_match_next(ctx, regexp, REM_RESET_INDEX|REM_NO_PARENS, jsstr, &match_ptr); 1036 jsstr_release(jsstr); 1037 jsdisp_release(regexp); 1038 if(FAILED(hres)) 1039 return hres; 1040 1041 if(r) 1042 *r = jsval_number(hres == S_OK ? match.cp-match.match_len-str : -1); 1043 return S_OK; 1044 } 1045 1046 /* ECMA-262 3rd Edition 15.5.4.13 */ 1047 static HRESULT String_slice(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1048 jsval_t *r) 1049 { 1050 int start=0, end, length; 1051 jsstr_t *str; 1052 double d; 1053 HRESULT hres; 1054 1055 TRACE("\n"); 1056 1057 hres = get_string_val(ctx, jsthis, &str); 1058 if(FAILED(hres)) 1059 return hres; 1060 1061 length = jsstr_length(str); 1062 if(argc) { 1063 hres = to_integer(ctx, argv[0], &d); 1064 if(FAILED(hres)) { 1065 jsstr_release(str); 1066 return hres; 1067 } 1068 1069 if(is_int32(d)) { 1070 start = d; 1071 if(start < 0) { 1072 start = length + start; 1073 if(start < 0) 1074 start = 0; 1075 }else if(start > length) { 1076 start = length; 1077 } 1078 }else if(d > 0) { 1079 start = length; 1080 } 1081 } 1082 1083 if(argc >= 2) { 1084 hres = to_integer(ctx, argv[1], &d); 1085 if(FAILED(hres)) { 1086 jsstr_release(str); 1087 return hres; 1088 } 1089 1090 if(is_int32(d)) { 1091 end = d; 1092 if(end < 0) { 1093 end = length + end; 1094 if(end < 0) 1095 end = 0; 1096 }else if(end > length) { 1097 end = length; 1098 } 1099 }else { 1100 end = d < 0.0 ? 0 : length; 1101 } 1102 }else { 1103 end = length; 1104 } 1105 1106 if(end < start) 1107 end = start; 1108 1109 if(r) { 1110 jsstr_t *retstr = jsstr_substr(str, start, end-start); 1111 if(!retstr) { 1112 jsstr_release(str); 1113 return E_OUTOFMEMORY; 1114 } 1115 1116 *r = jsval_string(retstr); 1117 } 1118 1119 jsstr_release(str); 1120 return S_OK; 1121 } 1122 1123 static HRESULT String_small(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1124 jsval_t *r) 1125 { 1126 static const WCHAR smalltagW[] = {'S','M','A','L','L',0}; 1127 return do_attributeless_tag_format(ctx, jsthis, r, smalltagW); 1128 } 1129 1130 static HRESULT String_split(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1131 jsval_t *r) 1132 { 1133 match_state_t match_result, *match_ptr = &match_result; 1134 DWORD length, i, match_len = 0; 1135 const WCHAR *ptr, *ptr2, *str, *match_str = NULL; 1136 unsigned limit = ~0u; 1137 jsdisp_t *array, *regexp = NULL; 1138 jsstr_t *jsstr, *match_jsstr, *tmp_str; 1139 HRESULT hres; 1140 1141 TRACE("\n"); 1142 1143 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str); 1144 if(FAILED(hres)) 1145 return hres; 1146 1147 length = jsstr_length(jsstr); 1148 1149 if(!argc || (is_undefined(argv[0]) && ctx->version >= SCRIPTLANGUAGEVERSION_ES5)) { 1150 if(!r) 1151 return S_OK; 1152 1153 hres = create_array(ctx, 0, &array); 1154 if(FAILED(hres)) 1155 return hres; 1156 1157 /* NOTE: according to spec, we should respect limit argument here (if provided). 1158 * We have a test showing that it's broken in native IE. */ 1159 hres = jsdisp_propput_idx(array, 0, jsval_string(jsstr)); 1160 if(FAILED(hres)) { 1161 jsdisp_release(array); 1162 return hres; 1163 } 1164 1165 *r = jsval_obj(array); 1166 return S_OK; 1167 } 1168 1169 if(argc > 1 && !is_undefined(argv[1])) { 1170 hres = to_uint32(ctx, argv[1], &limit); 1171 if(FAILED(hres)) { 1172 jsstr_release(jsstr); 1173 return hres; 1174 } 1175 } 1176 1177 if(is_object_instance(argv[0])) { 1178 regexp = iface_to_jsdisp(get_object(argv[0])); 1179 if(regexp) { 1180 if(!is_class(regexp, JSCLASS_REGEXP)) { 1181 jsdisp_release(regexp); 1182 regexp = NULL; 1183 } 1184 } 1185 } 1186 1187 if(!regexp) { 1188 hres = to_flat_string(ctx, argv[0], &match_jsstr, &match_str); 1189 if(FAILED(hres)) { 1190 jsstr_release(jsstr); 1191 return hres; 1192 } 1193 1194 match_len = jsstr_length(match_jsstr); 1195 if(!match_len) { 1196 jsstr_release(match_jsstr); 1197 match_str = NULL; 1198 } 1199 } 1200 1201 hres = create_array(ctx, 0, &array); 1202 1203 if(SUCCEEDED(hres)) { 1204 ptr = str; 1205 match_result.cp = str; 1206 for(i=0; i<limit; i++) { 1207 if(regexp) { 1208 hres = regexp_match_next(ctx, regexp, REM_NO_PARENS, jsstr, &match_ptr); 1209 if(hres != S_OK) 1210 break; 1211 ptr2 = match_result.cp - match_result.match_len; 1212 }else if(match_str) { 1213 ptr2 = strstrW(ptr, match_str); 1214 if(!ptr2) 1215 break; 1216 }else { 1217 if(!*ptr) 1218 break; 1219 ptr2 = ptr+1; 1220 } 1221 1222 tmp_str = jsstr_alloc_len(ptr, ptr2-ptr); 1223 if(!tmp_str) { 1224 hres = E_OUTOFMEMORY; 1225 break; 1226 } 1227 1228 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str)); 1229 jsstr_release(tmp_str); 1230 if(FAILED(hres)) 1231 break; 1232 1233 if(regexp) 1234 ptr = match_result.cp; 1235 else if(match_str) 1236 ptr = ptr2 + match_len; 1237 else 1238 ptr++; 1239 } 1240 } 1241 1242 if(SUCCEEDED(hres) && (match_str || regexp) && i<limit) { 1243 DWORD len = (str+length) - ptr; 1244 1245 if(len || match_str) { 1246 tmp_str = jsstr_alloc_len(ptr, len); 1247 1248 if(tmp_str) { 1249 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str)); 1250 jsstr_release(tmp_str); 1251 }else { 1252 hres = E_OUTOFMEMORY; 1253 } 1254 } 1255 } 1256 1257 if(regexp) 1258 jsdisp_release(regexp); 1259 if(match_str) 1260 jsstr_release(match_jsstr); 1261 jsstr_release(jsstr); 1262 1263 if(SUCCEEDED(hres) && r) 1264 *r = jsval_obj(array); 1265 else 1266 jsdisp_release(array); 1267 1268 return hres; 1269 } 1270 1271 static HRESULT String_strike(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1272 jsval_t *r) 1273 { 1274 static const WCHAR striketagW[] = {'S','T','R','I','K','E',0}; 1275 return do_attributeless_tag_format(ctx, jsthis, r, striketagW); 1276 } 1277 1278 static HRESULT String_sub(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1279 jsval_t *r) 1280 { 1281 static const WCHAR subtagW[] = {'S','U','B',0}; 1282 return do_attributeless_tag_format(ctx, jsthis, r, subtagW); 1283 } 1284 1285 /* ECMA-262 3rd Edition 15.5.4.15 */ 1286 static HRESULT String_substring(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1287 jsval_t *r) 1288 { 1289 INT start=0, end, length; 1290 jsstr_t *str; 1291 double d; 1292 HRESULT hres; 1293 1294 TRACE("\n"); 1295 1296 hres = get_string_val(ctx, jsthis, &str); 1297 if(FAILED(hres)) 1298 return hres; 1299 1300 length = jsstr_length(str); 1301 if(argc >= 1) { 1302 hres = to_integer(ctx, argv[0], &d); 1303 if(FAILED(hres)) { 1304 jsstr_release(str); 1305 return hres; 1306 } 1307 1308 if(d >= 0) 1309 start = is_int32(d) ? min(length, d) : length; 1310 } 1311 1312 if(argc >= 2) { 1313 hres = to_integer(ctx, argv[1], &d); 1314 if(FAILED(hres)) { 1315 jsstr_release(str); 1316 return hres; 1317 } 1318 1319 if(d >= 0) 1320 end = is_int32(d) ? min(length, d) : length; 1321 else 1322 end = 0; 1323 }else { 1324 end = length; 1325 } 1326 1327 if(start > end) { 1328 INT tmp = start; 1329 start = end; 1330 end = tmp; 1331 } 1332 1333 if(r) { 1334 jsstr_t *ret = jsstr_substr(str, start, end-start); 1335 if(ret) 1336 *r = jsval_string(ret); 1337 else 1338 hres = E_OUTOFMEMORY; 1339 } 1340 jsstr_release(str); 1341 return hres; 1342 } 1343 1344 /* ECMA-262 3rd Edition B.2.3 */ 1345 static HRESULT String_substr(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1346 jsval_t *r) 1347 { 1348 int start=0, len, length; 1349 jsstr_t *str; 1350 double d; 1351 HRESULT hres; 1352 1353 TRACE("\n"); 1354 1355 hres = get_string_val(ctx, jsthis, &str); 1356 if(FAILED(hres)) 1357 return hres; 1358 1359 length = jsstr_length(str); 1360 if(argc >= 1) { 1361 hres = to_integer(ctx, argv[0], &d); 1362 if(FAILED(hres)) { 1363 jsstr_release(str); 1364 return hres; 1365 } 1366 1367 if(d >= 0) 1368 start = is_int32(d) ? min(length, d) : length; 1369 } 1370 1371 if(argc >= 2) { 1372 hres = to_integer(ctx, argv[1], &d); 1373 if(FAILED(hres)) { 1374 jsstr_release(str); 1375 return hres; 1376 } 1377 1378 if(d >= 0.0) 1379 len = is_int32(d) ? min(length-start, d) : length-start; 1380 else 1381 len = 0; 1382 }else { 1383 len = length-start; 1384 } 1385 1386 hres = S_OK; 1387 if(r) { 1388 jsstr_t *ret = jsstr_substr(str, start, len); 1389 if(ret) 1390 *r = jsval_string(ret); 1391 else 1392 hres = E_OUTOFMEMORY; 1393 } 1394 1395 jsstr_release(str); 1396 return hres; 1397 } 1398 1399 static HRESULT String_sup(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1400 jsval_t *r) 1401 { 1402 static const WCHAR suptagW[] = {'S','U','P',0}; 1403 return do_attributeless_tag_format(ctx, jsthis, r, suptagW); 1404 } 1405 1406 static HRESULT String_toLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1407 jsval_t *r) 1408 { 1409 jsstr_t *str; 1410 HRESULT hres; 1411 1412 TRACE("\n"); 1413 1414 hres = get_string_val(ctx, jsthis, &str); 1415 if(FAILED(hres)) 1416 return hres; 1417 1418 if(r) { 1419 unsigned len = jsstr_length(str); 1420 jsstr_t *ret; 1421 WCHAR *buf; 1422 1423 ret = jsstr_alloc_buf(len, &buf); 1424 if(!ret) { 1425 jsstr_release(str); 1426 return E_OUTOFMEMORY; 1427 } 1428 1429 jsstr_flush(str, buf); 1430 for (; len--; buf++) *buf = tolowerW(*buf); 1431 1432 *r = jsval_string(ret); 1433 } 1434 jsstr_release(str); 1435 return S_OK; 1436 } 1437 1438 static HRESULT String_toUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1439 jsval_t *r) 1440 { 1441 jsstr_t *str; 1442 HRESULT hres; 1443 1444 TRACE("\n"); 1445 1446 hres = get_string_val(ctx, jsthis, &str); 1447 if(FAILED(hres)) 1448 return hres; 1449 1450 if(r) { 1451 unsigned len = jsstr_length(str); 1452 jsstr_t *ret; 1453 WCHAR *buf; 1454 1455 ret = jsstr_alloc_buf(len, &buf); 1456 if(!ret) { 1457 jsstr_release(str); 1458 return E_OUTOFMEMORY; 1459 } 1460 1461 jsstr_flush(str, buf); 1462 for (; len--; buf++) *buf = toupperW(*buf); 1463 1464 *r = jsval_string(ret); 1465 } 1466 jsstr_release(str); 1467 return S_OK; 1468 } 1469 1470 static HRESULT String_toLocaleLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1471 jsval_t *r) 1472 { 1473 FIXME("\n"); 1474 return E_NOTIMPL; 1475 } 1476 1477 static HRESULT String_toLocaleUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1478 jsval_t *r) 1479 { 1480 FIXME("\n"); 1481 return E_NOTIMPL; 1482 } 1483 1484 static HRESULT String_trim(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, 1485 jsval_t *argv, jsval_t *r) 1486 { 1487 const WCHAR *str, *begin, *end; 1488 jsstr_t *jsstr; 1489 unsigned len; 1490 HRESULT hres; 1491 1492 hres = to_flat_string(ctx, jsval_disp(jsthis->u.disp), &jsstr, &str); 1493 if(FAILED(hres)) { 1494 WARN("to_flat_string failed: %08x\n", hres); 1495 return hres; 1496 } 1497 len = jsstr_length(jsstr); 1498 TRACE("%s\n", debugstr_wn(str, len)); 1499 1500 for(begin = str, end = str + len; begin < end && isspaceW(*begin); begin++); 1501 while(end > begin + 1 && isspaceW(*(end-1))) end--; 1502 1503 if(r) { 1504 jsstr_t *ret; 1505 1506 if(begin == str && end == str + len) 1507 ret = jsstr_addref(jsstr); 1508 else 1509 ret = jsstr_alloc_len(begin, end - begin); 1510 if(ret) 1511 *r = jsval_string(ret); 1512 else 1513 hres = E_OUTOFMEMORY; 1514 } 1515 jsstr_release(jsstr); 1516 return hres; 1517 } 1518 1519 static HRESULT String_localeCompare(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1520 jsval_t *r) 1521 { 1522 FIXME("\n"); 1523 return E_NOTIMPL; 1524 } 1525 1526 static HRESULT String_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 1527 { 1528 StringInstance *This = string_from_jsdisp(jsthis); 1529 1530 TRACE("\n"); 1531 1532 *r = jsval_string(jsstr_addref(This->str)); 1533 return S_OK; 1534 } 1535 1536 static void String_destructor(jsdisp_t *dispex) 1537 { 1538 StringInstance *This = string_from_jsdisp(dispex); 1539 1540 jsstr_release(This->str); 1541 heap_free(This); 1542 } 1543 1544 static unsigned String_idx_length(jsdisp_t *jsdisp) 1545 { 1546 StringInstance *string = string_from_jsdisp(jsdisp); 1547 1548 /* 1549 * NOTE: For invoke version < 2, indexed array is not implemented at all. 1550 * Newer jscript.dll versions implement it on string type, not class, 1551 * which is not how it should work according to spec. IE9 implements it 1552 * properly, but it uses its own JavaScript engine inside MSHTML. We 1553 * implement it here, but in the way IE9 and spec work. 1554 */ 1555 return string->dispex.ctx->version < 2 ? 0 : jsstr_length(string->str); 1556 } 1557 1558 static HRESULT String_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *r) 1559 { 1560 StringInstance *string = string_from_jsdisp(jsdisp); 1561 jsstr_t *ret; 1562 1563 ret = jsstr_substr(string->str, idx, 1); 1564 if(!ret) 1565 return E_OUTOFMEMORY; 1566 1567 TRACE("%p[%u] = %s\n", string, idx, debugstr_jsstr(ret)); 1568 1569 *r = jsval_string(ret); 1570 return S_OK; 1571 } 1572 1573 static const builtin_prop_t String_props[] = { 1574 {anchorW, String_anchor, PROPF_METHOD|1}, 1575 {bigW, String_big, PROPF_METHOD}, 1576 {blinkW, String_blink, PROPF_METHOD}, 1577 {boldW, String_bold, PROPF_METHOD}, 1578 {charAtW, String_charAt, PROPF_METHOD|1}, 1579 {charCodeAtW, String_charCodeAt, PROPF_METHOD|1}, 1580 {concatW, String_concat, PROPF_METHOD|1}, 1581 {fixedW, String_fixed, PROPF_METHOD}, 1582 {fontcolorW, String_fontcolor, PROPF_METHOD|1}, 1583 {fontsizeW, String_fontsize, PROPF_METHOD|1}, 1584 {indexOfW, String_indexOf, PROPF_METHOD|2}, 1585 {italicsW, String_italics, PROPF_METHOD}, 1586 {lastIndexOfW, String_lastIndexOf, PROPF_METHOD|2}, 1587 {lengthW, NULL,0, String_get_length}, 1588 {linkW, String_link, PROPF_METHOD|1}, 1589 {localeCompareW, String_localeCompare, PROPF_METHOD|1}, 1590 {matchW, String_match, PROPF_METHOD|1}, 1591 {replaceW, String_replace, PROPF_METHOD|1}, 1592 {searchW, String_search, PROPF_METHOD}, 1593 {sliceW, String_slice, PROPF_METHOD}, 1594 {smallW, String_small, PROPF_METHOD}, 1595 {splitW, String_split, PROPF_METHOD|2}, 1596 {strikeW, String_strike, PROPF_METHOD}, 1597 {subW, String_sub, PROPF_METHOD}, 1598 {substrW, String_substr, PROPF_METHOD|2}, 1599 {substringW, String_substring, PROPF_METHOD|2}, 1600 {supW, String_sup, PROPF_METHOD}, 1601 {toLocaleLowerCaseW, String_toLocaleLowerCase, PROPF_METHOD}, 1602 {toLocaleUpperCaseW, String_toLocaleUpperCase, PROPF_METHOD}, 1603 {toLowerCaseW, String_toLowerCase, PROPF_METHOD}, 1604 {toStringW, String_toString, PROPF_METHOD}, 1605 {toUpperCaseW, String_toUpperCase, PROPF_METHOD}, 1606 {trimW, String_trim, PROPF_ES5|PROPF_METHOD}, 1607 {valueOfW, String_valueOf, PROPF_METHOD} 1608 }; 1609 1610 static const builtin_info_t String_info = { 1611 JSCLASS_STRING, 1612 {NULL, NULL,0, String_get_value}, 1613 ARRAY_SIZE(String_props), 1614 String_props, 1615 String_destructor, 1616 NULL 1617 }; 1618 1619 static const builtin_prop_t StringInst_props[] = { 1620 {lengthW, NULL,0, String_get_length} 1621 }; 1622 1623 static const builtin_info_t StringInst_info = { 1624 JSCLASS_STRING, 1625 {NULL, NULL,0, String_get_value}, 1626 ARRAY_SIZE(StringInst_props), 1627 StringInst_props, 1628 String_destructor, 1629 NULL, 1630 String_idx_length, 1631 String_idx_get 1632 }; 1633 1634 /* ECMA-262 3rd Edition 15.5.3.2 */ 1635 static HRESULT StringConstr_fromCharCode(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, 1636 unsigned argc, jsval_t *argv, jsval_t *r) 1637 { 1638 WCHAR *ret_str; 1639 DWORD i, code; 1640 jsstr_t *ret; 1641 HRESULT hres; 1642 1643 TRACE("\n"); 1644 1645 ret = jsstr_alloc_buf(argc, &ret_str); 1646 if(!ret) 1647 return E_OUTOFMEMORY; 1648 1649 for(i=0; i<argc; i++) { 1650 hres = to_uint32(ctx, argv[i], &code); 1651 if(FAILED(hres)) { 1652 jsstr_release(ret); 1653 return hres; 1654 } 1655 1656 ret_str[i] = code; 1657 } 1658 1659 if(r) 1660 *r = jsval_string(ret); 1661 else 1662 jsstr_release(ret); 1663 return S_OK; 1664 } 1665 1666 static HRESULT StringConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1667 jsval_t *r) 1668 { 1669 HRESULT hres; 1670 1671 TRACE("\n"); 1672 1673 switch(flags) { 1674 case INVOKE_FUNC: { 1675 jsstr_t *str; 1676 1677 if(argc) { 1678 hres = to_string(ctx, argv[0], &str); 1679 if(FAILED(hres)) 1680 return hres; 1681 }else { 1682 str = jsstr_empty(); 1683 } 1684 1685 *r = jsval_string(str); 1686 break; 1687 } 1688 case DISPATCH_CONSTRUCT: { 1689 jsstr_t *str; 1690 jsdisp_t *ret; 1691 1692 if(argc) { 1693 hres = to_string(ctx, argv[0], &str); 1694 if(FAILED(hres)) 1695 return hres; 1696 }else { 1697 str = jsstr_empty(); 1698 } 1699 1700 hres = create_string(ctx, str, &ret); 1701 if (SUCCEEDED(hres)) *r = jsval_obj(ret); 1702 jsstr_release(str); 1703 return hres; 1704 } 1705 1706 default: 1707 FIXME("unimplemented flags: %x\n", flags); 1708 return E_NOTIMPL; 1709 } 1710 1711 return S_OK; 1712 } 1713 1714 static HRESULT string_alloc(script_ctx_t *ctx, jsdisp_t *object_prototype, jsstr_t *str, StringInstance **ret) 1715 { 1716 StringInstance *string; 1717 HRESULT hres; 1718 1719 string = heap_alloc_zero(sizeof(StringInstance)); 1720 if(!string) 1721 return E_OUTOFMEMORY; 1722 1723 if(object_prototype) 1724 hres = init_dispex(&string->dispex, ctx, &String_info, object_prototype); 1725 else 1726 hres = init_dispex_from_constr(&string->dispex, ctx, &StringInst_info, ctx->string_constr); 1727 if(FAILED(hres)) { 1728 heap_free(string); 1729 return hres; 1730 } 1731 1732 string->str = jsstr_addref(str); 1733 *ret = string; 1734 return S_OK; 1735 } 1736 1737 static const builtin_prop_t StringConstr_props[] = { 1738 {fromCharCodeW, StringConstr_fromCharCode, PROPF_METHOD}, 1739 }; 1740 1741 static const builtin_info_t StringConstr_info = { 1742 JSCLASS_FUNCTION, 1743 DEFAULT_FUNCTION_VALUE, 1744 ARRAY_SIZE(StringConstr_props), 1745 StringConstr_props, 1746 NULL, 1747 NULL 1748 }; 1749 1750 HRESULT create_string_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret) 1751 { 1752 StringInstance *string; 1753 HRESULT hres; 1754 1755 static const WCHAR StringW[] = {'S','t','r','i','n','g',0}; 1756 1757 hres = string_alloc(ctx, object_prototype, jsstr_empty(), &string); 1758 if(FAILED(hres)) 1759 return hres; 1760 1761 hres = create_builtin_constructor(ctx, StringConstr_value, StringW, &StringConstr_info, 1762 PROPF_CONSTR|1, &string->dispex, ret); 1763 1764 jsdisp_release(&string->dispex); 1765 return hres; 1766 } 1767 1768 HRESULT create_string(script_ctx_t *ctx, jsstr_t *str, jsdisp_t **ret) 1769 { 1770 StringInstance *string; 1771 HRESULT hres; 1772 1773 hres = string_alloc(ctx, NULL, str, &string); 1774 if(FAILED(hres)) 1775 return hres; 1776 1777 *ret = &string->dispex; 1778 return S_OK; 1779 1780 } 1781