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 20 #include <math.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 = lstrlenW(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 = lstrlenW(attrname); 219 unsigned tagname_len = lstrlenW(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 = wcsstr(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 = wcschr(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(!iswdigit(ptr2[1])) { 908 hres = strbuf_append(&ret, ptr2, 1); 909 ptr = ptr2+1; 910 break; 911 } 912 913 idx = ptr2[1] - '0'; 914 if(iswdigit(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 size_t length, i = 0, 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 hres = get_string_flat_val(ctx, jsthis, &jsstr, &str); 1142 if(FAILED(hres)) 1143 return hres; 1144 length = jsstr_length(jsstr); 1145 1146 TRACE("%s\n", debugstr_wn(str, length)); 1147 1148 if(!argc || (is_undefined(argv[0]) && ctx->version >= SCRIPTLANGUAGEVERSION_ES5)) { 1149 if(!r) 1150 return S_OK; 1151 1152 hres = create_array(ctx, 0, &array); 1153 if(FAILED(hres)) 1154 return hres; 1155 1156 /* NOTE: according to spec, we should respect limit argument here (if provided). 1157 * We have a test showing that it's broken in native IE. */ 1158 hres = jsdisp_propput_idx(array, 0, jsval_string(jsstr)); 1159 if(FAILED(hres)) { 1160 jsdisp_release(array); 1161 return hres; 1162 } 1163 1164 *r = jsval_obj(array); 1165 return S_OK; 1166 } 1167 1168 if(argc > 1 && !is_undefined(argv[1])) { 1169 hres = to_uint32(ctx, argv[1], &limit); 1170 if(FAILED(hres)) { 1171 jsstr_release(jsstr); 1172 return hres; 1173 } 1174 } 1175 1176 if(is_object_instance(argv[0])) { 1177 regexp = iface_to_jsdisp(get_object(argv[0])); 1178 if(regexp) { 1179 if(!is_class(regexp, JSCLASS_REGEXP)) { 1180 jsdisp_release(regexp); 1181 regexp = NULL; 1182 } 1183 } 1184 } 1185 1186 if(!regexp) { 1187 hres = to_flat_string(ctx, argv[0], &match_jsstr, &match_str); 1188 if(FAILED(hres)) { 1189 jsstr_release(jsstr); 1190 return hres; 1191 } 1192 1193 match_len = jsstr_length(match_jsstr); 1194 if(!match_len) { 1195 jsstr_release(match_jsstr); 1196 match_str = NULL; 1197 } 1198 } 1199 1200 hres = create_array(ctx, 0, &array); 1201 1202 if(SUCCEEDED(hres)) { 1203 ptr = str; 1204 match_result.cp = str; 1205 while(i < limit) { 1206 if(regexp) { 1207 hres = regexp_match_next(ctx, regexp, REM_NO_PARENS, jsstr, &match_ptr); 1208 if(hres != S_OK) 1209 break; 1210 TRACE("got match %d %d\n", (int)(match_result.cp - match_result.match_len - str), match_result.match_len); 1211 if(!match_result.match_len) { 1212 /* If an empty string is matched, prevent including any match in the result */ 1213 if(!length) { 1214 limit = 0; 1215 break; 1216 } 1217 if(match_result.cp == ptr) { 1218 match_result.cp++; 1219 hres = regexp_match_next(ctx, regexp, REM_NO_PARENS, jsstr, &match_ptr); 1220 if(hres != S_OK) 1221 break; 1222 TRACE("retried, got match %d %d\n", (int)(match_result.cp - match_result.match_len - str), 1223 match_result.match_len); 1224 } 1225 if(!match_result.match_len && match_result.cp == str + length) 1226 break; 1227 } 1228 ptr2 = match_result.cp - match_result.match_len; 1229 }else if(match_str) { 1230 ptr2 = wcsstr(ptr, match_str); 1231 if(!ptr2) 1232 break; 1233 }else { 1234 if(!*ptr) 1235 break; 1236 ptr2 = ptr+1; 1237 } 1238 1239 if(!regexp || ptr2 > ptr || ctx->version >= SCRIPTLANGUAGEVERSION_ES5) { 1240 tmp_str = jsstr_alloc_len(ptr, ptr2-ptr); 1241 if(!tmp_str) { 1242 hres = E_OUTOFMEMORY; 1243 break; 1244 } 1245 1246 hres = jsdisp_propput_idx(array, i++, jsval_string(tmp_str)); 1247 jsstr_release(tmp_str); 1248 if(FAILED(hres)) 1249 break; 1250 } 1251 1252 if(regexp) 1253 ptr = match_result.cp; 1254 else if(match_str) 1255 ptr = ptr2 + match_len; 1256 else 1257 ptr++; 1258 } 1259 } 1260 1261 if(SUCCEEDED(hres) && (match_str || regexp) && i<limit) { 1262 DWORD len = (str+length) - ptr; 1263 1264 if(len || match_str || !length || ctx->version >= SCRIPTLANGUAGEVERSION_ES5) { 1265 tmp_str = jsstr_alloc_len(ptr, len); 1266 1267 if(tmp_str) { 1268 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str)); 1269 jsstr_release(tmp_str); 1270 }else { 1271 hres = E_OUTOFMEMORY; 1272 } 1273 } 1274 } 1275 1276 if(regexp) 1277 jsdisp_release(regexp); 1278 if(match_str) 1279 jsstr_release(match_jsstr); 1280 jsstr_release(jsstr); 1281 1282 if(SUCCEEDED(hres) && r) 1283 *r = jsval_obj(array); 1284 else 1285 jsdisp_release(array); 1286 1287 return hres; 1288 } 1289 1290 static HRESULT String_strike(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1291 jsval_t *r) 1292 { 1293 static const WCHAR striketagW[] = {'S','T','R','I','K','E',0}; 1294 return do_attributeless_tag_format(ctx, jsthis, r, striketagW); 1295 } 1296 1297 static HRESULT String_sub(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1298 jsval_t *r) 1299 { 1300 static const WCHAR subtagW[] = {'S','U','B',0}; 1301 return do_attributeless_tag_format(ctx, jsthis, r, subtagW); 1302 } 1303 1304 /* ECMA-262 3rd Edition 15.5.4.15 */ 1305 static HRESULT String_substring(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1306 jsval_t *r) 1307 { 1308 INT start=0, end, length; 1309 jsstr_t *str; 1310 double d; 1311 HRESULT hres; 1312 1313 TRACE("\n"); 1314 1315 hres = get_string_val(ctx, jsthis, &str); 1316 if(FAILED(hres)) 1317 return hres; 1318 1319 length = jsstr_length(str); 1320 if(argc >= 1) { 1321 hres = to_integer(ctx, argv[0], &d); 1322 if(FAILED(hres)) { 1323 jsstr_release(str); 1324 return hres; 1325 } 1326 1327 if(d >= 0) 1328 start = is_int32(d) ? min(length, d) : length; 1329 } 1330 1331 if(argc >= 2) { 1332 hres = to_integer(ctx, argv[1], &d); 1333 if(FAILED(hres)) { 1334 jsstr_release(str); 1335 return hres; 1336 } 1337 1338 if(d >= 0) 1339 end = is_int32(d) ? min(length, d) : length; 1340 else 1341 end = 0; 1342 }else { 1343 end = length; 1344 } 1345 1346 if(start > end) { 1347 INT tmp = start; 1348 start = end; 1349 end = tmp; 1350 } 1351 1352 if(r) { 1353 jsstr_t *ret = jsstr_substr(str, start, end-start); 1354 if(ret) 1355 *r = jsval_string(ret); 1356 else 1357 hres = E_OUTOFMEMORY; 1358 } 1359 jsstr_release(str); 1360 return hres; 1361 } 1362 1363 /* ECMA-262 3rd Edition B.2.3 */ 1364 static HRESULT String_substr(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1365 jsval_t *r) 1366 { 1367 int start=0, len, length; 1368 jsstr_t *str; 1369 double d; 1370 HRESULT hres; 1371 1372 TRACE("\n"); 1373 1374 hres = get_string_val(ctx, jsthis, &str); 1375 if(FAILED(hres)) 1376 return hres; 1377 1378 length = jsstr_length(str); 1379 if(argc >= 1) { 1380 hres = to_integer(ctx, argv[0], &d); 1381 if(FAILED(hres)) { 1382 jsstr_release(str); 1383 return hres; 1384 } 1385 1386 if(d >= 0) 1387 start = is_int32(d) ? min(length, d) : length; 1388 } 1389 1390 if(argc >= 2) { 1391 hres = to_integer(ctx, argv[1], &d); 1392 if(FAILED(hres)) { 1393 jsstr_release(str); 1394 return hres; 1395 } 1396 1397 if(d >= 0.0) 1398 len = is_int32(d) ? min(length-start, d) : length-start; 1399 else 1400 len = 0; 1401 }else { 1402 len = length-start; 1403 } 1404 1405 hres = S_OK; 1406 if(r) { 1407 jsstr_t *ret = jsstr_substr(str, start, len); 1408 if(ret) 1409 *r = jsval_string(ret); 1410 else 1411 hres = E_OUTOFMEMORY; 1412 } 1413 1414 jsstr_release(str); 1415 return hres; 1416 } 1417 1418 static HRESULT String_sup(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1419 jsval_t *r) 1420 { 1421 static const WCHAR suptagW[] = {'S','U','P',0}; 1422 return do_attributeless_tag_format(ctx, jsthis, r, suptagW); 1423 } 1424 1425 static HRESULT String_toLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1426 jsval_t *r) 1427 { 1428 jsstr_t *str; 1429 HRESULT hres; 1430 1431 TRACE("\n"); 1432 1433 hres = get_string_val(ctx, jsthis, &str); 1434 if(FAILED(hres)) 1435 return hres; 1436 1437 if(r) { 1438 unsigned len = jsstr_length(str); 1439 jsstr_t *ret; 1440 WCHAR *buf; 1441 1442 ret = jsstr_alloc_buf(len, &buf); 1443 if(!ret) { 1444 jsstr_release(str); 1445 return E_OUTOFMEMORY; 1446 } 1447 1448 jsstr_flush(str, buf); 1449 for (; len--; buf++) *buf = towlower(*buf); 1450 1451 *r = jsval_string(ret); 1452 } 1453 jsstr_release(str); 1454 return S_OK; 1455 } 1456 1457 static HRESULT String_toUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1458 jsval_t *r) 1459 { 1460 jsstr_t *str; 1461 HRESULT hres; 1462 1463 TRACE("\n"); 1464 1465 hres = get_string_val(ctx, jsthis, &str); 1466 if(FAILED(hres)) 1467 return hres; 1468 1469 if(r) { 1470 unsigned len = jsstr_length(str); 1471 jsstr_t *ret; 1472 WCHAR *buf; 1473 1474 ret = jsstr_alloc_buf(len, &buf); 1475 if(!ret) { 1476 jsstr_release(str); 1477 return E_OUTOFMEMORY; 1478 } 1479 1480 jsstr_flush(str, buf); 1481 for (; len--; buf++) *buf = towupper(*buf); 1482 1483 *r = jsval_string(ret); 1484 } 1485 jsstr_release(str); 1486 return S_OK; 1487 } 1488 1489 static HRESULT String_toLocaleLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1490 jsval_t *r) 1491 { 1492 FIXME("\n"); 1493 return E_NOTIMPL; 1494 } 1495 1496 static HRESULT String_toLocaleUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1497 jsval_t *r) 1498 { 1499 FIXME("\n"); 1500 return E_NOTIMPL; 1501 } 1502 1503 static HRESULT String_trim(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, 1504 jsval_t *argv, jsval_t *r) 1505 { 1506 const WCHAR *str, *begin, *end; 1507 jsstr_t *jsstr; 1508 unsigned len; 1509 HRESULT hres; 1510 1511 hres = to_flat_string(ctx, jsval_disp(jsthis->u.disp), &jsstr, &str); 1512 if(FAILED(hres)) { 1513 WARN("to_flat_string failed: %08x\n", hres); 1514 return hres; 1515 } 1516 len = jsstr_length(jsstr); 1517 TRACE("%s\n", debugstr_wn(str, len)); 1518 1519 for(begin = str, end = str + len; begin < end && iswspace(*begin); begin++); 1520 while(end > begin + 1 && iswspace(*(end-1))) end--; 1521 1522 if(r) { 1523 jsstr_t *ret; 1524 1525 if(begin == str && end == str + len) 1526 ret = jsstr_addref(jsstr); 1527 else 1528 ret = jsstr_alloc_len(begin, end - begin); 1529 if(ret) 1530 *r = jsval_string(ret); 1531 else 1532 hres = E_OUTOFMEMORY; 1533 } 1534 jsstr_release(jsstr); 1535 return hres; 1536 } 1537 1538 static HRESULT String_localeCompare(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1539 jsval_t *r) 1540 { 1541 FIXME("\n"); 1542 return E_NOTIMPL; 1543 } 1544 1545 static HRESULT String_get_value(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 1546 { 1547 StringInstance *This = string_from_jsdisp(jsthis); 1548 1549 TRACE("\n"); 1550 1551 *r = jsval_string(jsstr_addref(This->str)); 1552 return S_OK; 1553 } 1554 1555 static void String_destructor(jsdisp_t *dispex) 1556 { 1557 StringInstance *This = string_from_jsdisp(dispex); 1558 1559 jsstr_release(This->str); 1560 heap_free(This); 1561 } 1562 1563 static unsigned String_idx_length(jsdisp_t *jsdisp) 1564 { 1565 StringInstance *string = string_from_jsdisp(jsdisp); 1566 1567 /* 1568 * NOTE: For invoke version < 2, indexed array is not implemented at all. 1569 * Newer jscript.dll versions implement it on string type, not class, 1570 * which is not how it should work according to spec. IE9 implements it 1571 * properly, but it uses its own JavaScript engine inside MSHTML. We 1572 * implement it here, but in the way IE9 and spec work. 1573 */ 1574 return string->dispex.ctx->version < 2 ? 0 : jsstr_length(string->str); 1575 } 1576 1577 static HRESULT String_idx_get(jsdisp_t *jsdisp, unsigned idx, jsval_t *r) 1578 { 1579 StringInstance *string = string_from_jsdisp(jsdisp); 1580 jsstr_t *ret; 1581 1582 ret = jsstr_substr(string->str, idx, 1); 1583 if(!ret) 1584 return E_OUTOFMEMORY; 1585 1586 TRACE("%p[%u] = %s\n", string, idx, debugstr_jsstr(ret)); 1587 1588 *r = jsval_string(ret); 1589 return S_OK; 1590 } 1591 1592 static const builtin_prop_t String_props[] = { 1593 {anchorW, String_anchor, PROPF_METHOD|1}, 1594 {bigW, String_big, PROPF_METHOD}, 1595 {blinkW, String_blink, PROPF_METHOD}, 1596 {boldW, String_bold, PROPF_METHOD}, 1597 {charAtW, String_charAt, PROPF_METHOD|1}, 1598 {charCodeAtW, String_charCodeAt, PROPF_METHOD|1}, 1599 {concatW, String_concat, PROPF_METHOD|1}, 1600 {fixedW, String_fixed, PROPF_METHOD}, 1601 {fontcolorW, String_fontcolor, PROPF_METHOD|1}, 1602 {fontsizeW, String_fontsize, PROPF_METHOD|1}, 1603 {indexOfW, String_indexOf, PROPF_METHOD|2}, 1604 {italicsW, String_italics, PROPF_METHOD}, 1605 {lastIndexOfW, String_lastIndexOf, PROPF_METHOD|2}, 1606 {lengthW, NULL,0, String_get_length}, 1607 {linkW, String_link, PROPF_METHOD|1}, 1608 {localeCompareW, String_localeCompare, PROPF_METHOD|1}, 1609 {matchW, String_match, PROPF_METHOD|1}, 1610 {replaceW, String_replace, PROPF_METHOD|1}, 1611 {searchW, String_search, PROPF_METHOD}, 1612 {sliceW, String_slice, PROPF_METHOD}, 1613 {smallW, String_small, PROPF_METHOD}, 1614 {splitW, String_split, PROPF_METHOD|2}, 1615 {strikeW, String_strike, PROPF_METHOD}, 1616 {subW, String_sub, PROPF_METHOD}, 1617 {substrW, String_substr, PROPF_METHOD|2}, 1618 {substringW, String_substring, PROPF_METHOD|2}, 1619 {supW, String_sup, PROPF_METHOD}, 1620 {toLocaleLowerCaseW, String_toLocaleLowerCase, PROPF_METHOD}, 1621 {toLocaleUpperCaseW, String_toLocaleUpperCase, PROPF_METHOD}, 1622 {toLowerCaseW, String_toLowerCase, PROPF_METHOD}, 1623 {toStringW, String_toString, PROPF_METHOD}, 1624 {toUpperCaseW, String_toUpperCase, PROPF_METHOD}, 1625 {trimW, String_trim, PROPF_ES5|PROPF_METHOD}, 1626 {valueOfW, String_valueOf, PROPF_METHOD} 1627 }; 1628 1629 static const builtin_info_t String_info = { 1630 JSCLASS_STRING, 1631 {NULL, NULL,0, String_get_value}, 1632 ARRAY_SIZE(String_props), 1633 String_props, 1634 String_destructor, 1635 NULL 1636 }; 1637 1638 static const builtin_prop_t StringInst_props[] = { 1639 {lengthW, NULL,0, String_get_length} 1640 }; 1641 1642 static const builtin_info_t StringInst_info = { 1643 JSCLASS_STRING, 1644 {NULL, NULL,0, String_get_value}, 1645 ARRAY_SIZE(StringInst_props), 1646 StringInst_props, 1647 String_destructor, 1648 NULL, 1649 String_idx_length, 1650 String_idx_get 1651 }; 1652 1653 /* ECMA-262 3rd Edition 15.5.3.2 */ 1654 static HRESULT StringConstr_fromCharCode(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, 1655 unsigned argc, jsval_t *argv, jsval_t *r) 1656 { 1657 WCHAR *ret_str; 1658 DWORD i, code; 1659 jsstr_t *ret; 1660 HRESULT hres; 1661 1662 TRACE("\n"); 1663 1664 ret = jsstr_alloc_buf(argc, &ret_str); 1665 if(!ret) 1666 return E_OUTOFMEMORY; 1667 1668 for(i=0; i<argc; i++) { 1669 hres = to_uint32(ctx, argv[i], &code); 1670 if(FAILED(hres)) { 1671 jsstr_release(ret); 1672 return hres; 1673 } 1674 1675 ret_str[i] = code; 1676 } 1677 1678 if(r) 1679 *r = jsval_string(ret); 1680 else 1681 jsstr_release(ret); 1682 return S_OK; 1683 } 1684 1685 static HRESULT StringConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 1686 jsval_t *r) 1687 { 1688 HRESULT hres; 1689 1690 TRACE("\n"); 1691 1692 switch(flags) { 1693 case INVOKE_FUNC: { 1694 jsstr_t *str; 1695 1696 if(argc) { 1697 hres = to_string(ctx, argv[0], &str); 1698 if(FAILED(hres)) 1699 return hres; 1700 }else { 1701 str = jsstr_empty(); 1702 } 1703 1704 *r = jsval_string(str); 1705 break; 1706 } 1707 case DISPATCH_CONSTRUCT: { 1708 jsstr_t *str; 1709 jsdisp_t *ret; 1710 1711 if(argc) { 1712 hres = to_string(ctx, argv[0], &str); 1713 if(FAILED(hres)) 1714 return hres; 1715 }else { 1716 str = jsstr_empty(); 1717 } 1718 1719 hres = create_string(ctx, str, &ret); 1720 if (SUCCEEDED(hres)) *r = jsval_obj(ret); 1721 jsstr_release(str); 1722 return hres; 1723 } 1724 1725 default: 1726 FIXME("unimplemented flags: %x\n", flags); 1727 return E_NOTIMPL; 1728 } 1729 1730 return S_OK; 1731 } 1732 1733 static HRESULT string_alloc(script_ctx_t *ctx, jsdisp_t *object_prototype, jsstr_t *str, StringInstance **ret) 1734 { 1735 StringInstance *string; 1736 HRESULT hres; 1737 1738 string = heap_alloc_zero(sizeof(StringInstance)); 1739 if(!string) 1740 return E_OUTOFMEMORY; 1741 1742 if(object_prototype) 1743 hres = init_dispex(&string->dispex, ctx, &String_info, object_prototype); 1744 else 1745 hres = init_dispex_from_constr(&string->dispex, ctx, &StringInst_info, ctx->string_constr); 1746 if(FAILED(hres)) { 1747 heap_free(string); 1748 return hres; 1749 } 1750 1751 string->str = jsstr_addref(str); 1752 *ret = string; 1753 return S_OK; 1754 } 1755 1756 static const builtin_prop_t StringConstr_props[] = { 1757 {fromCharCodeW, StringConstr_fromCharCode, PROPF_METHOD}, 1758 }; 1759 1760 static const builtin_info_t StringConstr_info = { 1761 JSCLASS_FUNCTION, 1762 DEFAULT_FUNCTION_VALUE, 1763 ARRAY_SIZE(StringConstr_props), 1764 StringConstr_props, 1765 NULL, 1766 NULL 1767 }; 1768 1769 HRESULT create_string_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret) 1770 { 1771 StringInstance *string; 1772 HRESULT hres; 1773 1774 static const WCHAR StringW[] = {'S','t','r','i','n','g',0}; 1775 1776 hres = string_alloc(ctx, object_prototype, jsstr_empty(), &string); 1777 if(FAILED(hres)) 1778 return hres; 1779 1780 hres = create_builtin_constructor(ctx, StringConstr_value, StringW, &StringConstr_info, 1781 PROPF_CONSTR|1, &string->dispex, ret); 1782 1783 jsdisp_release(&string->dispex); 1784 return hres; 1785 } 1786 1787 HRESULT create_string(script_ctx_t *ctx, jsstr_t *str, jsdisp_t **ret) 1788 { 1789 StringInstance *string; 1790 HRESULT hres; 1791 1792 hres = string_alloc(ctx, NULL, str, &string); 1793 if(FAILED(hres)) 1794 return hres; 1795 1796 *ret = &string->dispex; 1797 return S_OK; 1798 1799 } 1800