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