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