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 <math.h> 20 21 #include "jscript.h" 22 #include "regexp.h" 23 24 #include "wine/debug.h" 25 26 WINE_DEFAULT_DEBUG_CHANNEL(jscript); 27 28 typedef struct { 29 jsdisp_t dispex; 30 31 regexp_t *jsregexp; 32 jsstr_t *str; 33 INT last_index; 34 jsval_t last_index_val; 35 } RegExpInstance; 36 37 static const WCHAR sourceW[] = {'s','o','u','r','c','e',0}; 38 static const WCHAR globalW[] = {'g','l','o','b','a','l',0}; 39 static const WCHAR ignoreCaseW[] = {'i','g','n','o','r','e','C','a','s','e',0}; 40 static const WCHAR multilineW[] = {'m','u','l','t','i','l','i','n','e',0}; 41 static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0}; 42 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0}; 43 static const WCHAR execW[] = {'e','x','e','c',0}; 44 static const WCHAR testW[] = {'t','e','s','t',0}; 45 46 static const WCHAR leftContextW[] = 47 {'l','e','f','t','C','o','n','t','e','x','t',0}; 48 static const WCHAR rightContextW[] = 49 {'r','i','g','h','t','C','o','n','t','e','x','t',0}; 50 51 static const WCHAR idx1W[] = {'$','1',0}; 52 static const WCHAR idx2W[] = {'$','2',0}; 53 static const WCHAR idx3W[] = {'$','3',0}; 54 static const WCHAR idx4W[] = {'$','4',0}; 55 static const WCHAR idx5W[] = {'$','5',0}; 56 static const WCHAR idx6W[] = {'$','6',0}; 57 static const WCHAR idx7W[] = {'$','7',0}; 58 static const WCHAR idx8W[] = {'$','8',0}; 59 static const WCHAR idx9W[] = {'$','9',0}; 60 61 static inline RegExpInstance *regexp_from_jsdisp(jsdisp_t *jsdisp) 62 { 63 return CONTAINING_RECORD(jsdisp, RegExpInstance, dispex); 64 } 65 66 static inline RegExpInstance *regexp_from_vdisp(vdisp_t *vdisp) 67 { 68 return regexp_from_jsdisp(vdisp->u.jsdisp); 69 } 70 71 static void set_last_index(RegExpInstance *This, DWORD last_index) 72 { 73 This->last_index = last_index; 74 jsval_release(This->last_index_val); 75 This->last_index_val = jsval_number(last_index); 76 } 77 78 static HRESULT do_regexp_match_next(script_ctx_t *ctx, RegExpInstance *regexp, 79 DWORD rem_flags, jsstr_t *jsstr, const WCHAR *str, match_state_t *ret) 80 { 81 HRESULT hres; 82 83 hres = regexp_execute(regexp->jsregexp, ctx, &ctx->tmp_heap, 84 str, jsstr_length(jsstr), ret); 85 if(FAILED(hres)) 86 return hres; 87 if(hres == S_FALSE) { 88 if(rem_flags & REM_RESET_INDEX) 89 set_last_index(regexp, 0); 90 return S_FALSE; 91 } 92 93 if(!(rem_flags & REM_NO_CTX_UPDATE) && ctx->last_match != jsstr) { 94 jsstr_release(ctx->last_match); 95 ctx->last_match = jsstr_addref(jsstr); 96 } 97 98 if(!(rem_flags & REM_NO_CTX_UPDATE)) { 99 DWORD i, n = min(ARRAY_SIZE(ctx->match_parens), ret->paren_count); 100 101 for(i=0; i < n; i++) { 102 if(ret->parens[i].index == -1) { 103 ctx->match_parens[i].index = 0; 104 ctx->match_parens[i].length = 0; 105 }else { 106 ctx->match_parens[i].index = ret->parens[i].index; 107 ctx->match_parens[i].length = ret->parens[i].length; 108 } 109 } 110 111 if(n < ARRAY_SIZE(ctx->match_parens)) 112 memset(ctx->match_parens+n, 0, sizeof(ctx->match_parens) - n*sizeof(ctx->match_parens[0])); 113 } 114 115 set_last_index(regexp, ret->cp-str); 116 117 if(!(rem_flags & REM_NO_CTX_UPDATE)) { 118 ctx->last_match_index = ret->cp-str-ret->match_len; 119 ctx->last_match_length = ret->match_len; 120 } 121 122 return S_OK; 123 } 124 125 HRESULT regexp_match_next(script_ctx_t *ctx, jsdisp_t *dispex, 126 DWORD rem_flags, jsstr_t *jsstr, match_state_t **ret) 127 { 128 RegExpInstance *regexp = regexp_from_jsdisp(dispex); 129 match_state_t *match; 130 heap_pool_t *mark; 131 const WCHAR *str; 132 HRESULT hres; 133 134 if((rem_flags & REM_CHECK_GLOBAL) && !(regexp->jsregexp->flags & REG_GLOB)) { 135 if(rem_flags & REM_ALLOC_RESULT) 136 *ret = NULL; 137 return S_FALSE; 138 } 139 140 str = jsstr_flatten(jsstr); 141 if(!str) 142 return E_OUTOFMEMORY; 143 144 if(rem_flags & REM_ALLOC_RESULT) { 145 match = alloc_match_state(regexp->jsregexp, NULL, str); 146 if(!match) 147 return E_OUTOFMEMORY; 148 *ret = match; 149 } 150 151 mark = heap_pool_mark(&ctx->tmp_heap); 152 153 if(rem_flags & REM_NO_PARENS) { 154 match = alloc_match_state(regexp->jsregexp, &ctx->tmp_heap, NULL); 155 if(!match) { 156 heap_pool_clear(mark); 157 return E_OUTOFMEMORY; 158 } 159 match->cp = (*ret)->cp; 160 match->match_len = (*ret)->match_len; 161 }else { 162 match = *ret; 163 } 164 165 hres = do_regexp_match_next(ctx, regexp, rem_flags, jsstr, str, match); 166 167 if(rem_flags & REM_NO_PARENS) { 168 (*ret)->cp = match->cp; 169 (*ret)->match_len = match->match_len; 170 } 171 172 heap_pool_clear(mark); 173 174 if(hres != S_OK && (rem_flags & REM_ALLOC_RESULT)) { 175 heap_free(match); 176 *ret = NULL; 177 } 178 179 return hres; 180 } 181 182 static HRESULT regexp_match(script_ctx_t *ctx, jsdisp_t *dispex, jsstr_t *jsstr, BOOL gflag, 183 match_result_t **match_result, DWORD *result_cnt) 184 { 185 RegExpInstance *This = regexp_from_jsdisp(dispex); 186 match_result_t *ret = NULL; 187 match_state_t *result; 188 DWORD i=0, ret_size = 0; 189 heap_pool_t *mark; 190 const WCHAR *str; 191 HRESULT hres; 192 193 mark = heap_pool_mark(&ctx->tmp_heap); 194 195 str = jsstr_flatten(jsstr); 196 if(!str) 197 return E_OUTOFMEMORY; 198 199 result = alloc_match_state(This->jsregexp, &ctx->tmp_heap, str); 200 if(!result) { 201 heap_pool_clear(mark); 202 return E_OUTOFMEMORY; 203 } 204 205 while(1) { 206 hres = do_regexp_match_next(ctx, This, 0, jsstr, str, result); 207 if(hres == S_FALSE) { 208 hres = S_OK; 209 break; 210 } 211 212 if(FAILED(hres)) 213 break; 214 215 if(ret_size == i) { 216 if(ret) { 217 match_result_t *old_ret = ret; 218 219 ret = heap_realloc(old_ret, (ret_size <<= 1) * sizeof(match_result_t)); 220 if(!ret) 221 heap_free(old_ret); 222 }else { 223 ret = heap_alloc((ret_size=4) * sizeof(match_result_t)); 224 } 225 if(!ret) { 226 hres = E_OUTOFMEMORY; 227 break; 228 } 229 } 230 231 ret[i].index = result->cp - str - result->match_len; 232 ret[i++].length = result->match_len; 233 234 if(!gflag && !(This->jsregexp->flags & REG_GLOB)) { 235 hres = S_OK; 236 break; 237 } 238 } 239 240 heap_pool_clear(mark); 241 if(FAILED(hres)) { 242 heap_free(ret); 243 return hres; 244 } 245 246 *match_result = ret; 247 *result_cnt = i; 248 return S_OK; 249 } 250 251 static HRESULT RegExp_get_source(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 252 { 253 TRACE("\n"); 254 255 *r = jsval_string(jsstr_addref(regexp_from_jsdisp(jsthis)->str)); 256 return S_OK; 257 } 258 259 static HRESULT RegExp_get_global(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 260 { 261 TRACE("\n"); 262 263 *r = jsval_bool(!!(regexp_from_jsdisp(jsthis)->jsregexp->flags & REG_GLOB)); 264 return S_OK; 265 } 266 267 static HRESULT RegExp_get_ignoreCase(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 268 { 269 TRACE("\n"); 270 271 *r = jsval_bool(!!(regexp_from_jsdisp(jsthis)->jsregexp->flags & REG_FOLD)); 272 return S_OK; 273 } 274 275 static HRESULT RegExp_get_multiline(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 276 { 277 TRACE("\n"); 278 279 *r = jsval_bool(!!(regexp_from_jsdisp(jsthis)->jsregexp->flags & REG_MULTILINE)); 280 return S_OK; 281 } 282 283 static INT index_from_val(script_ctx_t *ctx, jsval_t v) 284 { 285 double n; 286 HRESULT hres; 287 288 hres = to_number(ctx, v, &n); 289 if(FAILED(hres)) { 290 clear_ei(ctx); /* FIXME: Move ignoring exceptions to to_primitive */ 291 return 0; 292 } 293 294 n = floor(n); 295 return is_int32(n) ? n : 0; 296 } 297 298 static HRESULT RegExp_get_lastIndex(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 299 { 300 RegExpInstance *regexp = regexp_from_jsdisp(jsthis); 301 302 TRACE("\n"); 303 304 return jsval_copy(regexp->last_index_val, r); 305 } 306 307 static HRESULT RegExp_set_lastIndex(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value) 308 { 309 RegExpInstance *regexp = regexp_from_jsdisp(jsthis); 310 HRESULT hres; 311 312 TRACE("\n"); 313 314 jsval_release(regexp->last_index_val); 315 hres = jsval_copy(value, ®exp->last_index_val); 316 if(FAILED(hres)) 317 return hres; 318 319 regexp->last_index = index_from_val(ctx, value); 320 return S_OK; 321 } 322 323 static HRESULT RegExp_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 324 jsval_t *r) 325 { 326 RegExpInstance *regexp; 327 unsigned len, f; 328 jsstr_t *ret; 329 WCHAR *ptr; 330 331 TRACE("\n"); 332 333 if(!is_vclass(jsthis, JSCLASS_REGEXP)) { 334 FIXME("Not a RegExp\n"); 335 return E_NOTIMPL; 336 } 337 338 regexp = regexp_from_vdisp(jsthis); 339 340 if(!r) 341 return S_OK; 342 343 len = jsstr_length(regexp->str) + 2; 344 345 f = regexp->jsregexp->flags; 346 if(f & REG_FOLD) 347 len++; 348 if(f & REG_GLOB) 349 len++; 350 if(f & REG_MULTILINE) 351 len++; 352 353 ret = jsstr_alloc_buf(len, &ptr); 354 if(!ret) 355 return E_OUTOFMEMORY; 356 357 *ptr++ = '/'; 358 ptr += jsstr_flush(regexp->str, ptr); 359 *ptr++ = '/'; 360 361 if(f & REG_FOLD) 362 *ptr++ = 'i'; 363 if(f & REG_GLOB) 364 *ptr++ = 'g'; 365 if(f & REG_MULTILINE) 366 *ptr++ = 'm'; 367 368 *r = jsval_string(ret); 369 return S_OK; 370 } 371 372 static HRESULT create_match_array(script_ctx_t *ctx, jsstr_t *input_str, 373 const match_state_t *result, IDispatch **ret) 374 { 375 const WCHAR *input; 376 jsdisp_t *array; 377 jsstr_t *str; 378 DWORD i; 379 HRESULT hres = S_OK; 380 381 static const WCHAR indexW[] = {'i','n','d','e','x',0}; 382 static const WCHAR inputW[] = {'i','n','p','u','t',0}; 383 static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0}; 384 static const WCHAR zeroW[] = {'0',0}; 385 386 input = jsstr_flatten(input_str); 387 if(!input) 388 return E_OUTOFMEMORY; 389 390 hres = create_array(ctx, result->paren_count+1, &array); 391 if(FAILED(hres)) 392 return hres; 393 394 for(i=0; i < result->paren_count; i++) { 395 if(result->parens[i].index != -1) 396 str = jsstr_substr(input_str, result->parens[i].index, result->parens[i].length); 397 else 398 str = jsstr_empty(); 399 if(!str) { 400 hres = E_OUTOFMEMORY; 401 break; 402 } 403 404 hres = jsdisp_propput_idx(array, i+1, jsval_string(str)); 405 jsstr_release(str); 406 if(FAILED(hres)) 407 break; 408 } 409 410 while(SUCCEEDED(hres)) { 411 hres = jsdisp_propput_name(array, indexW, jsval_number(result->cp-input-result->match_len)); 412 if(FAILED(hres)) 413 break; 414 415 hres = jsdisp_propput_name(array, lastIndexW, jsval_number(result->cp-input)); 416 if(FAILED(hres)) 417 break; 418 419 hres = jsdisp_propput_name(array, inputW, jsval_string(jsstr_addref(input_str))); 420 if(FAILED(hres)) 421 break; 422 423 str = jsstr_alloc_len(result->cp-result->match_len, result->match_len); 424 if(!str) { 425 hres = E_OUTOFMEMORY; 426 break; 427 } 428 hres = jsdisp_propput_name(array, zeroW, jsval_string(str)); 429 jsstr_release(str); 430 break; 431 } 432 433 if(FAILED(hres)) { 434 jsdisp_release(array); 435 return hres; 436 } 437 438 *ret = to_disp(array); 439 return S_OK; 440 } 441 442 static HRESULT run_exec(script_ctx_t *ctx, vdisp_t *jsthis, jsval_t arg, 443 jsstr_t **input, match_state_t **result, BOOL *ret) 444 { 445 RegExpInstance *regexp; 446 match_state_t *match; 447 DWORD last_index = 0; 448 const WCHAR *string; 449 jsstr_t *jsstr; 450 HRESULT hres; 451 452 if(!is_vclass(jsthis, JSCLASS_REGEXP)) { 453 FIXME("Not a RegExp\n"); 454 return E_NOTIMPL; 455 } 456 457 regexp = regexp_from_vdisp(jsthis); 458 459 hres = to_flat_string(ctx, arg, &jsstr, &string); 460 if(FAILED(hres)) 461 return hres; 462 463 if(regexp->jsregexp->flags & REG_GLOB) { 464 if(regexp->last_index < 0) { 465 jsstr_release(jsstr); 466 set_last_index(regexp, 0); 467 *ret = FALSE; 468 if(input) 469 *input = jsstr_empty(); 470 return S_OK; 471 } 472 473 last_index = regexp->last_index; 474 } 475 476 match = alloc_match_state(regexp->jsregexp, &ctx->tmp_heap, string+last_index); 477 if(!match) { 478 jsstr_release(jsstr); 479 return E_OUTOFMEMORY; 480 } 481 482 hres = regexp_match_next(ctx, ®exp->dispex, REM_RESET_INDEX, jsstr, &match); 483 if(FAILED(hres)) { 484 jsstr_release(jsstr); 485 return hres; 486 } 487 488 *result = match; 489 *ret = hres == S_OK; 490 if(input) 491 *input = jsstr; 492 else 493 jsstr_release(jsstr); 494 return S_OK; 495 } 496 497 static HRESULT RegExp_exec(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 498 jsval_t *r) 499 { 500 match_state_t *match; 501 heap_pool_t *mark; 502 BOOL b; 503 jsstr_t *string; 504 HRESULT hres; 505 506 TRACE("\n"); 507 508 mark = heap_pool_mark(&ctx->tmp_heap); 509 510 hres = run_exec(ctx, jsthis, argc ? argv[0] : jsval_string(jsstr_empty()), &string, &match, &b); 511 if(FAILED(hres)) { 512 heap_pool_clear(mark); 513 return hres; 514 } 515 516 if(r) { 517 if(b) { 518 IDispatch *ret; 519 520 hres = create_match_array(ctx, string, match, &ret); 521 if(SUCCEEDED(hres)) 522 *r = jsval_disp(ret); 523 }else { 524 *r = jsval_null(); 525 } 526 } 527 528 heap_pool_clear(mark); 529 jsstr_release(string); 530 return hres; 531 } 532 533 static HRESULT RegExp_test(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 534 jsval_t *r) 535 { 536 match_state_t *match; 537 jsstr_t *undef_str; 538 heap_pool_t *mark; 539 BOOL b; 540 HRESULT hres; 541 542 TRACE("\n"); 543 544 mark = heap_pool_mark(&ctx->tmp_heap); 545 hres = run_exec(ctx, jsthis, argc ? argv[0] : jsval_string(undef_str = jsstr_undefined()), NULL, &match, &b); 546 heap_pool_clear(mark); 547 if(!argc) 548 jsstr_release(undef_str); 549 if(FAILED(hres)) 550 return hres; 551 552 if(r) 553 *r = jsval_bool(b); 554 return S_OK; 555 } 556 557 static HRESULT RegExp_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 558 jsval_t *r) 559 { 560 TRACE("\n"); 561 562 switch(flags) { 563 case INVOKE_FUNC: 564 return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL); 565 default: 566 FIXME("unimplemented flags %x\n", flags); 567 return E_NOTIMPL; 568 } 569 570 return S_OK; 571 } 572 573 static void RegExp_destructor(jsdisp_t *dispex) 574 { 575 RegExpInstance *This = regexp_from_jsdisp(dispex); 576 577 if(This->jsregexp) 578 regexp_destroy(This->jsregexp); 579 jsval_release(This->last_index_val); 580 jsstr_release(This->str); 581 heap_free(This); 582 } 583 584 static const builtin_prop_t RegExp_props[] = { 585 {execW, RegExp_exec, PROPF_METHOD|1}, 586 {globalW, NULL,0, RegExp_get_global}, 587 {ignoreCaseW, NULL,0, RegExp_get_ignoreCase}, 588 {lastIndexW, NULL,0, RegExp_get_lastIndex, RegExp_set_lastIndex}, 589 {multilineW, NULL,0, RegExp_get_multiline}, 590 {sourceW, NULL,0, RegExp_get_source}, 591 {testW, RegExp_test, PROPF_METHOD|1}, 592 {toStringW, RegExp_toString, PROPF_METHOD} 593 }; 594 595 static const builtin_info_t RegExp_info = { 596 JSCLASS_REGEXP, 597 {NULL, RegExp_value, 0}, 598 ARRAY_SIZE(RegExp_props), 599 RegExp_props, 600 RegExp_destructor, 601 NULL 602 }; 603 604 static const builtin_prop_t RegExpInst_props[] = { 605 {globalW, NULL,0, RegExp_get_global}, 606 {ignoreCaseW, NULL,0, RegExp_get_ignoreCase}, 607 {lastIndexW, NULL,0, RegExp_get_lastIndex, RegExp_set_lastIndex}, 608 {multilineW, NULL,0, RegExp_get_multiline}, 609 {sourceW, NULL,0, RegExp_get_source} 610 }; 611 612 static const builtin_info_t RegExpInst_info = { 613 JSCLASS_REGEXP, 614 {NULL, RegExp_value, 0}, 615 ARRAY_SIZE(RegExpInst_props), 616 RegExpInst_props, 617 RegExp_destructor, 618 NULL 619 }; 620 621 static HRESULT alloc_regexp(script_ctx_t *ctx, jsdisp_t *object_prototype, RegExpInstance **ret) 622 { 623 RegExpInstance *regexp; 624 HRESULT hres; 625 626 regexp = heap_alloc_zero(sizeof(RegExpInstance)); 627 if(!regexp) 628 return E_OUTOFMEMORY; 629 630 if(object_prototype) 631 hres = init_dispex(®exp->dispex, ctx, &RegExp_info, object_prototype); 632 else 633 hres = init_dispex_from_constr(®exp->dispex, ctx, &RegExpInst_info, ctx->regexp_constr); 634 635 if(FAILED(hres)) { 636 heap_free(regexp); 637 return hres; 638 } 639 640 *ret = regexp; 641 return S_OK; 642 } 643 644 HRESULT create_regexp(script_ctx_t *ctx, jsstr_t *src, DWORD flags, jsdisp_t **ret) 645 { 646 RegExpInstance *regexp; 647 const WCHAR *str; 648 HRESULT hres; 649 650 str = jsstr_flatten(src); 651 if(!str) 652 return E_OUTOFMEMORY; 653 654 TRACE("%s %x\n", debugstr_wn(str, jsstr_length(src)), flags); 655 656 hres = alloc_regexp(ctx, NULL, ®exp); 657 if(FAILED(hres)) 658 return hres; 659 660 regexp->str = jsstr_addref(src); 661 regexp->last_index_val = jsval_number(0); 662 663 regexp->jsregexp = regexp_new(ctx, &ctx->tmp_heap, str, jsstr_length(regexp->str), flags, FALSE); 664 if(!regexp->jsregexp) { 665 WARN("regexp_new failed\n"); 666 jsdisp_release(®exp->dispex); 667 return E_FAIL; 668 } 669 670 *ret = ®exp->dispex; 671 return S_OK; 672 } 673 674 HRESULT create_regexp_var(script_ctx_t *ctx, jsval_t src_arg, jsval_t *flags_arg, jsdisp_t **ret) 675 { 676 unsigned flags, opt_len = 0; 677 const WCHAR *opt = NULL; 678 jsstr_t *src; 679 HRESULT hres; 680 681 if(is_object_instance(src_arg)) { 682 jsdisp_t *obj; 683 684 obj = iface_to_jsdisp(get_object(src_arg)); 685 if(obj) { 686 if(is_class(obj, JSCLASS_REGEXP)) { 687 RegExpInstance *regexp = regexp_from_jsdisp(obj); 688 689 hres = create_regexp(ctx, regexp->str, regexp->jsregexp->flags, ret); 690 jsdisp_release(obj); 691 return hres; 692 } 693 694 jsdisp_release(obj); 695 } 696 } 697 698 if(!is_string(src_arg)) { 699 FIXME("src_arg = %s\n", debugstr_jsval(src_arg)); 700 return E_NOTIMPL; 701 } 702 703 src = get_string(src_arg); 704 705 if(flags_arg) { 706 jsstr_t *opt_str; 707 708 if(!is_string(*flags_arg)) { 709 FIXME("unimplemented for %s\n", debugstr_jsval(*flags_arg)); 710 return E_NOTIMPL; 711 } 712 713 opt_str = get_string(*flags_arg); 714 opt = jsstr_flatten(opt_str); 715 if(!opt) 716 return E_OUTOFMEMORY; 717 opt_len = jsstr_length(opt_str); 718 } 719 720 hres = parse_regexp_flags(opt, opt_len, &flags); 721 if(FAILED(hres)) 722 return hres; 723 724 return create_regexp(ctx, src, flags, ret); 725 } 726 727 HRESULT regexp_string_match(script_ctx_t *ctx, jsdisp_t *re, jsstr_t *jsstr, jsval_t *r) 728 { 729 static const WCHAR indexW[] = {'i','n','d','e','x',0}; 730 static const WCHAR inputW[] = {'i','n','p','u','t',0}; 731 static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0}; 732 733 RegExpInstance *regexp = regexp_from_jsdisp(re); 734 match_result_t *match_result; 735 unsigned match_cnt, i; 736 const WCHAR *str; 737 jsdisp_t *array; 738 HRESULT hres; 739 740 str = jsstr_flatten(jsstr); 741 if(!str) 742 return E_OUTOFMEMORY; 743 744 if(!(regexp->jsregexp->flags & REG_GLOB)) { 745 match_state_t *match; 746 heap_pool_t *mark; 747 748 mark = heap_pool_mark(&ctx->tmp_heap); 749 match = alloc_match_state(regexp->jsregexp, &ctx->tmp_heap, str); 750 if(!match) { 751 heap_pool_clear(mark); 752 return E_OUTOFMEMORY; 753 } 754 755 hres = regexp_match_next(ctx, ®exp->dispex, 0, jsstr, &match); 756 if(FAILED(hres)) { 757 heap_pool_clear(mark); 758 return hres; 759 } 760 761 if(r) { 762 if(hres == S_OK) { 763 IDispatch *ret; 764 765 hres = create_match_array(ctx, jsstr, match, &ret); 766 if(SUCCEEDED(hres)) 767 *r = jsval_disp(ret); 768 }else { 769 *r = jsval_null(); 770 } 771 } 772 773 heap_pool_clear(mark); 774 return S_OK; 775 } 776 777 hres = regexp_match(ctx, ®exp->dispex, jsstr, FALSE, &match_result, &match_cnt); 778 if(FAILED(hres)) 779 return hres; 780 781 if(!match_cnt) { 782 TRACE("no match\n"); 783 784 if(r) 785 *r = jsval_null(); 786 return S_OK; 787 } 788 789 hres = create_array(ctx, match_cnt, &array); 790 if(FAILED(hres)) 791 return hres; 792 793 for(i=0; i < match_cnt; i++) { 794 jsstr_t *tmp_str; 795 796 tmp_str = jsstr_substr(jsstr, match_result[i].index, match_result[i].length); 797 if(!tmp_str) { 798 hres = E_OUTOFMEMORY; 799 break; 800 } 801 802 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str)); 803 jsstr_release(tmp_str); 804 if(FAILED(hres)) 805 break; 806 } 807 808 while(SUCCEEDED(hres)) { 809 hres = jsdisp_propput_name(array, indexW, jsval_number(match_result[match_cnt-1].index)); 810 if(FAILED(hres)) 811 break; 812 813 hres = jsdisp_propput_name(array, lastIndexW, 814 jsval_number(match_result[match_cnt-1].index + match_result[match_cnt-1].length)); 815 if(FAILED(hres)) 816 break; 817 818 hres = jsdisp_propput_name(array, inputW, jsval_string(jsstr)); 819 break; 820 } 821 822 heap_free(match_result); 823 824 if(SUCCEEDED(hres) && r) 825 *r = jsval_obj(array); 826 else 827 jsdisp_release(array); 828 return hres; 829 } 830 831 static HRESULT global_idx(script_ctx_t *ctx, DWORD idx, jsval_t *r) 832 { 833 jsstr_t *ret; 834 835 ret = jsstr_substr(ctx->last_match, ctx->match_parens[idx].index, ctx->match_parens[idx].length); 836 if(!ret) 837 return E_OUTOFMEMORY; 838 839 *r = jsval_string(ret); 840 return S_OK; 841 } 842 843 static HRESULT RegExpConstr_get_idx1(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 844 { 845 TRACE("\n"); 846 return global_idx(ctx, 0, r); 847 } 848 849 static HRESULT RegExpConstr_get_idx2(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 850 { 851 TRACE("\n"); 852 return global_idx(ctx, 1, r); 853 } 854 855 static HRESULT RegExpConstr_get_idx3(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 856 { 857 TRACE("\n"); 858 return global_idx(ctx, 2, r); 859 } 860 861 static HRESULT RegExpConstr_get_idx4(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 862 { 863 TRACE("\n"); 864 return global_idx(ctx, 3, r); 865 } 866 867 static HRESULT RegExpConstr_get_idx5(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 868 { 869 TRACE("\n"); 870 return global_idx(ctx, 4, r); 871 } 872 873 static HRESULT RegExpConstr_get_idx6(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 874 { 875 TRACE("\n"); 876 return global_idx(ctx, 5, r); 877 } 878 879 static HRESULT RegExpConstr_get_idx7(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 880 { 881 TRACE("\n"); 882 return global_idx(ctx, 6, r); 883 } 884 885 static HRESULT RegExpConstr_get_idx8(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 886 { 887 TRACE("\n"); 888 return global_idx(ctx, 7, r); 889 } 890 891 static HRESULT RegExpConstr_get_idx9(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 892 { 893 TRACE("\n"); 894 return global_idx(ctx, 8, r); 895 } 896 897 static HRESULT RegExpConstr_get_leftContext(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 898 { 899 jsstr_t *ret; 900 901 TRACE("\n"); 902 903 ret = jsstr_substr(ctx->last_match, 0, ctx->last_match_index); 904 if(!ret) 905 return E_OUTOFMEMORY; 906 907 *r = jsval_string(ret); 908 return S_OK; 909 } 910 911 static HRESULT RegExpConstr_get_rightContext(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r) 912 { 913 jsstr_t *ret; 914 915 TRACE("\n"); 916 917 ret = jsstr_substr(ctx->last_match, ctx->last_match_index+ctx->last_match_length, 918 jsstr_length(ctx->last_match) - ctx->last_match_index - ctx->last_match_length); 919 if(!ret) 920 return E_OUTOFMEMORY; 921 922 *r = jsval_string(ret); 923 return S_OK; 924 } 925 926 static HRESULT RegExpConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, 927 jsval_t *r) 928 { 929 TRACE("\n"); 930 931 switch(flags) { 932 case DISPATCH_METHOD: 933 if(argc) { 934 if(is_object_instance(argv[0])) { 935 jsdisp_t *jsdisp = iface_to_jsdisp(get_object(argv[0])); 936 if(jsdisp) { 937 if(is_class(jsdisp, JSCLASS_REGEXP)) { 938 if(argc > 1 && !is_undefined(argv[1])) { 939 jsdisp_release(jsdisp); 940 return throw_regexp_error(ctx, JS_E_REGEXP_SYNTAX, NULL); 941 } 942 943 if(r) 944 *r = jsval_obj(jsdisp); 945 else 946 jsdisp_release(jsdisp); 947 return S_OK; 948 } 949 jsdisp_release(jsdisp); 950 } 951 } 952 } 953 /* fall through */ 954 case DISPATCH_CONSTRUCT: { 955 jsdisp_t *ret; 956 HRESULT hres; 957 958 if(!argc) { 959 FIXME("no args\n"); 960 return E_NOTIMPL; 961 } 962 963 hres = create_regexp_var(ctx, argv[0], argc > 1 ? argv+1 : NULL, &ret); 964 if(FAILED(hres)) 965 return hres; 966 967 if(r) 968 *r = jsval_obj(ret); 969 else 970 jsdisp_release(ret); 971 return S_OK; 972 } 973 default: 974 FIXME("unimplemented flags: %x\n", flags); 975 return E_NOTIMPL; 976 } 977 978 return S_OK; 979 } 980 981 static const builtin_prop_t RegExpConstr_props[] = { 982 {idx1W, NULL,0, RegExpConstr_get_idx1, builtin_set_const}, 983 {idx2W, NULL,0, RegExpConstr_get_idx2, builtin_set_const}, 984 {idx3W, NULL,0, RegExpConstr_get_idx3, builtin_set_const}, 985 {idx4W, NULL,0, RegExpConstr_get_idx4, builtin_set_const}, 986 {idx5W, NULL,0, RegExpConstr_get_idx5, builtin_set_const}, 987 {idx6W, NULL,0, RegExpConstr_get_idx6, builtin_set_const}, 988 {idx7W, NULL,0, RegExpConstr_get_idx7, builtin_set_const}, 989 {idx8W, NULL,0, RegExpConstr_get_idx8, builtin_set_const}, 990 {idx9W, NULL,0, RegExpConstr_get_idx9, builtin_set_const}, 991 {leftContextW, NULL,0, RegExpConstr_get_leftContext, builtin_set_const}, 992 {rightContextW, NULL,0, RegExpConstr_get_rightContext, builtin_set_const} 993 }; 994 995 static const builtin_info_t RegExpConstr_info = { 996 JSCLASS_FUNCTION, 997 DEFAULT_FUNCTION_VALUE, 998 ARRAY_SIZE(RegExpConstr_props), 999 RegExpConstr_props, 1000 NULL, 1001 NULL 1002 }; 1003 1004 HRESULT create_regexp_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret) 1005 { 1006 RegExpInstance *regexp; 1007 HRESULT hres; 1008 1009 static const WCHAR RegExpW[] = {'R','e','g','E','x','p',0}; 1010 1011 hres = alloc_regexp(ctx, object_prototype, ®exp); 1012 if(FAILED(hres)) 1013 return hres; 1014 1015 hres = create_builtin_constructor(ctx, RegExpConstr_value, RegExpW, &RegExpConstr_info, 1016 PROPF_CONSTR|2, ®exp->dispex, ret); 1017 1018 jsdisp_release(®exp->dispex); 1019 return hres; 1020 } 1021 1022 HRESULT parse_regexp_flags(const WCHAR *str, DWORD str_len, DWORD *ret) 1023 { 1024 const WCHAR *p; 1025 DWORD flags = 0; 1026 1027 for (p = str; p < str+str_len; p++) { 1028 switch (*p) { 1029 case 'g': 1030 flags |= REG_GLOB; 1031 break; 1032 case 'i': 1033 flags |= REG_FOLD; 1034 break; 1035 case 'm': 1036 flags |= REG_MULTILINE; 1037 break; 1038 case 'y': 1039 flags |= REG_STICKY; 1040 break; 1041 default: 1042 WARN("wrong flag %c\n", *p); 1043 return E_FAIL; 1044 } 1045 } 1046 1047 *ret = flags; 1048 return S_OK; 1049 } 1050